Merge "Prevents uninstalled packages from being added to the DB."
diff --git a/Android.bp b/Android.bp
index f9b60e6..d94bd84 100644
--- a/Android.bp
+++ b/Android.bp
@@ -560,6 +560,7 @@
         "telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
         "telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
         "telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
+        "telephony/java/com/android/internal/telephony/IRcs.aidl",
         "telephony/java/com/android/internal/telephony/ISms.aidl",
         "telephony/java/com/android/internal/telephony/ISub.aidl",
         "telephony/java/com/android/internal/telephony/IAns.aidl",
@@ -1220,11 +1221,6 @@
     srcs_lib_whitelist_dirs: frameworks_base_subdirs,
     srcs_lib_whitelist_pkgs: packages_to_document,
     libs: [
-        "core-oj",
-        "core-libart",
-        "conscrypt",
-        "bouncycastle",
-        "okhttp",
         "ext",
         "framework",
         "voip-common",
diff --git a/api/current.txt b/api/current.txt
index 5951433..1983fb4 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -1403,6 +1403,7 @@
     field public static final int textFilterEnabled = 16843007; // 0x10100ff
     field public static final int textFontWeight = 16844165; // 0x1010585
     field public static final int textIsSelectable = 16843542; // 0x1010316
+    field public static final int textLocale = 16844178; // 0x1010592
     field public static final int textOff = 16843045; // 0x1010125
     field public static final int textOn = 16843044; // 0x1010124
     field public static final int textScaleX = 16843089; // 0x1010151
@@ -9167,6 +9168,7 @@
     method public int bulkInsert(android.net.Uri, android.content.ContentValues[]);
     method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle);
     method public android.net.Uri canonicalize(android.net.Uri);
+    method public final android.content.ContentProvider.CallingIdentity clearCallingIdentity();
     method public abstract int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public final java.lang.String getCallingPackage();
@@ -9194,6 +9196,7 @@
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
     method public boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal);
+    method public final void restoreCallingIdentity(android.content.ContentProvider.CallingIdentity);
     method protected final void setPathPermissions(android.content.pm.PathPermission[]);
     method protected final void setReadPermission(java.lang.String);
     method protected final void setWritePermission(java.lang.String);
@@ -9202,6 +9205,9 @@
     method public abstract int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
   }
 
+  public final class ContentProvider.CallingIdentity {
+  }
+
   public static abstract interface ContentProvider.PipeDataWriter<T> {
     method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
   }
@@ -9316,6 +9322,7 @@
     method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
     method public static boolean isSyncActive(android.accounts.Account, java.lang.String);
     method public static boolean isSyncPending(android.accounts.Account, java.lang.String);
+    method public android.graphics.Bitmap loadThumbnail(android.net.Uri, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
     method public void notifyChange(android.net.Uri, android.database.ContentObserver);
     method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
     method public void notifyChange(android.net.Uri, android.database.ContentObserver, int);
@@ -15468,8 +15475,8 @@
 
   public static class MeasuredText.Builder {
     ctor public MeasuredText.Builder(char[]);
-    method public android.graphics.text.MeasuredText.Builder addReplacementRun(android.graphics.Paint, int, int, float);
-    method public android.graphics.text.MeasuredText.Builder addStyleRun(android.graphics.Paint, int, int, boolean);
+    method public android.graphics.text.MeasuredText.Builder appendReplacementRun(android.graphics.Paint, int, float);
+    method public android.graphics.text.MeasuredText.Builder appendStyleRun(android.graphics.Paint, int, boolean);
     method public android.graphics.text.MeasuredText build();
     method public android.graphics.text.MeasuredText.Builder setComputeHyphenation(boolean);
     method public android.graphics.text.MeasuredText.Builder setComputeLayout(boolean);
@@ -27446,6 +27453,7 @@
   public static class ConnectivityManager.NetworkCallback {
     ctor public ConnectivityManager.NetworkCallback();
     method public void onAvailable(android.net.Network);
+    method public void onBlockedStatusChanged(android.net.Network, boolean);
     method public void onCapabilitiesChanged(android.net.Network, android.net.NetworkCapabilities);
     method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
     method public void onLosing(android.net.Network, int);
@@ -27717,16 +27725,16 @@
 
   public class NetworkInfo implements android.os.Parcelable {
     method public int describeContents();
-    method public android.net.NetworkInfo.DetailedState getDetailedState();
+    method public deprecated android.net.NetworkInfo.DetailedState getDetailedState();
     method public java.lang.String getExtraInfo();
     method public deprecated java.lang.String getReason();
     method public deprecated android.net.NetworkInfo.State getState();
-    method public int getSubtype();
-    method public java.lang.String getSubtypeName();
+    method public deprecated int getSubtype();
+    method public deprecated java.lang.String getSubtypeName();
     method public deprecated int getType();
     method public deprecated java.lang.String getTypeName();
     method public deprecated boolean isAvailable();
-    method public boolean isConnected();
+    method public deprecated boolean isConnected();
     method public deprecated boolean isConnectedOrConnecting();
     method public deprecated boolean isFailover();
     method public deprecated boolean isRoaming();
@@ -29722,6 +29730,65 @@
     field public static final int EGL_WINDOW_BIT = 4; // 0x4
   }
 
+  public class EGL15 {
+    ctor public EGL15();
+    method public static int eglClientWaitSync(android.opengl.EGLDisplay, android.opengl.EGLSync, int, long);
+    method public static android.opengl.EGLSurface eglCreatePlatformPixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, java.nio.Buffer, long[], int);
+    method public static android.opengl.EGLSurface eglCreatePlatformWindowSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, java.nio.Buffer, long[], int);
+    method public static android.opengl.EGLSync eglCreateSync(android.opengl.EGLDisplay, int, long[], int);
+    method public static boolean eglDestroySync(android.opengl.EGLDisplay, android.opengl.EGLSync);
+    method public static android.opengl.EGLDisplay eglGetPlatformDisplay(int, long, long[], int);
+    method public static boolean eglGetSyncAttrib(android.opengl.EGLDisplay, android.opengl.EGLSync, int, long[], int);
+    method public static boolean eglWaitSync(android.opengl.EGLDisplay, android.opengl.EGLSync, int);
+    field public static final int EGL_CL_EVENT_HANDLE = 12444; // 0x309c
+    field public static final int EGL_CONDITION_SATISFIED = 12534; // 0x30f6
+    field public static final int EGL_CONTEXT_MAJOR_VERSION = 12440; // 0x3098
+    field public static final int EGL_CONTEXT_MINOR_VERSION = 12539; // 0x30fb
+    field public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT = 2; // 0x2
+    field public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT = 1; // 0x1
+    field public static final int EGL_CONTEXT_OPENGL_DEBUG = 12720; // 0x31b0
+    field public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE = 12721; // 0x31b1
+    field public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK = 12541; // 0x30fd
+    field public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 12733; // 0x31bd
+    field public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS = 12722; // 0x31b2
+    field public static final long EGL_FOREVER = -1L; // 0xffffffffffffffffL
+    field public static final int EGL_GL_COLORSPACE = 12445; // 0x309d
+    field public static final int EGL_GL_COLORSPACE_LINEAR = 12426; // 0x308a
+    field public static final int EGL_GL_COLORSPACE_SRGB = 12425; // 0x3089
+    field public static final int EGL_GL_RENDERBUFFER = 12473; // 0x30b9
+    field public static final int EGL_GL_TEXTURE_2D = 12465; // 0x30b1
+    field public static final int EGL_GL_TEXTURE_3D = 12466; // 0x30b2
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 12468; // 0x30b4
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 12470; // 0x30b6
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 12472; // 0x30b8
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X = 12467; // 0x30b3
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 12469; // 0x30b5
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 12471; // 0x30b7
+    field public static final int EGL_GL_TEXTURE_LEVEL = 12476; // 0x30bc
+    field public static final int EGL_GL_TEXTURE_ZOFFSET = 12477; // 0x30bd
+    field public static final int EGL_IMAGE_PRESERVED = 12498; // 0x30d2
+    field public static final int EGL_LOSE_CONTEXT_ON_RESET = 12735; // 0x31bf
+    field public static final android.opengl.EGLContext EGL_NO_CONTEXT;
+    field public static final android.opengl.EGLDisplay EGL_NO_DISPLAY;
+    field public static final android.opengl.EGLImage EGL_NO_IMAGE;
+    field public static final int EGL_NO_RESET_NOTIFICATION = 12734; // 0x31be
+    field public static final android.opengl.EGLSurface EGL_NO_SURFACE;
+    field public static final android.opengl.EGLSync EGL_NO_SYNC;
+    field public static final int EGL_OPENGL_ES3_BIT = 64; // 0x40
+    field public static final int EGL_PLATFORM_ANDROID_KHR = 12609; // 0x3141
+    field public static final int EGL_SIGNALED = 12530; // 0x30f2
+    field public static final int EGL_SYNC_CL_EVENT = 12542; // 0x30fe
+    field public static final int EGL_SYNC_CL_EVENT_COMPLETE = 12543; // 0x30ff
+    field public static final int EGL_SYNC_CONDITION = 12536; // 0x30f8
+    field public static final int EGL_SYNC_FENCE = 12537; // 0x30f9
+    field public static final int EGL_SYNC_FLUSH_COMMANDS_BIT = 1; // 0x1
+    field public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE = 12528; // 0x30f0
+    field public static final int EGL_SYNC_STATUS = 12529; // 0x30f1
+    field public static final int EGL_SYNC_TYPE = 12535; // 0x30f7
+    field public static final int EGL_TIMEOUT_EXPIRED = 12533; // 0x30f5
+    field public static final int EGL_UNSIGNALED = 12531; // 0x30f3
+  }
+
   public class EGLConfig extends android.opengl.EGLObjectHandle {
   }
 
@@ -29741,6 +29808,9 @@
     field public static final int EGL_RECORDABLE_ANDROID = 12610; // 0x3142
   }
 
+  public class EGLImage extends android.opengl.EGLObjectHandle {
+  }
+
   public abstract class EGLObjectHandle {
     ctor protected deprecated EGLObjectHandle(int);
     ctor protected EGLObjectHandle(long);
@@ -29751,6 +29821,9 @@
   public class EGLSurface extends android.opengl.EGLObjectHandle {
   }
 
+  public class EGLSync extends android.opengl.EGLObjectHandle {
+  }
+
   public class ETC1 {
     ctor public ETC1();
     method public static void decodeBlock(java.nio.Buffer, java.nio.Buffer);
@@ -36745,7 +36818,7 @@
 
   public static abstract interface MediaStore.Audio.AlbumColumns {
     field public static final java.lang.String ALBUM = "album";
-    field public static final java.lang.String ALBUM_ART = "album_art";
+    field public static final deprecated java.lang.String ALBUM_ART = "album_art";
     field public static final java.lang.String ALBUM_ID = "album_id";
     field public static final java.lang.String ALBUM_KEY = "album_key";
     field public static final java.lang.String ARTIST = "artist";
@@ -36867,7 +36940,7 @@
   }
 
   public static abstract interface MediaStore.Audio.PlaylistsColumns {
-    field public static final java.lang.String DATA = "_data";
+    field public static final deprecated java.lang.String DATA = "_data";
     field public static final java.lang.String DATE_ADDED = "date_added";
     field public static final java.lang.String DATE_MODIFIED = "date_modified";
     field public static final java.lang.String NAME = "name";
@@ -36929,15 +37002,15 @@
 
   public static class MediaStore.Images.Thumbnails implements android.provider.BaseColumns {
     ctor public MediaStore.Images.Thumbnails();
-    method public static void cancelThumbnailRequest(android.content.ContentResolver, long);
-    method public static void cancelThumbnailRequest(android.content.ContentResolver, long, long);
+    method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long);
+    method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long, long);
     method public static android.net.Uri getContentUri(java.lang.String);
-    method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
-    method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
+    method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
+    method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
     method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
     method public static final android.database.Cursor queryMiniThumbnail(android.content.ContentResolver, long, int, java.lang.String[]);
     method public static final android.database.Cursor queryMiniThumbnails(android.content.ContentResolver, android.net.Uri, int, java.lang.String[]);
-    field public static final java.lang.String DATA = "_data";
+    field public static final deprecated java.lang.String DATA = "_data";
     field public static final java.lang.String DEFAULT_SORT_ORDER = "image_id ASC";
     field public static final android.net.Uri EXTERNAL_CONTENT_URI;
     field public static final int FULL_SCREEN_KIND = 2; // 0x2
@@ -36952,7 +37025,7 @@
   }
 
   public static abstract interface MediaStore.MediaColumns implements android.provider.BaseColumns {
-    field public static final java.lang.String DATA = "_data";
+    field public static final deprecated java.lang.String DATA = "_data";
     field public static final java.lang.String DATE_ADDED = "date_added";
     field public static final java.lang.String DATE_MODIFIED = "date_modified";
     field public static final java.lang.String DISPLAY_NAME = "_display_name";
@@ -36980,12 +37053,12 @@
 
   public static class MediaStore.Video.Thumbnails implements android.provider.BaseColumns {
     ctor public MediaStore.Video.Thumbnails();
-    method public static void cancelThumbnailRequest(android.content.ContentResolver, long);
-    method public static void cancelThumbnailRequest(android.content.ContentResolver, long, long);
+    method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long);
+    method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long, long);
     method public static android.net.Uri getContentUri(java.lang.String);
-    method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
-    method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
-    field public static final java.lang.String DATA = "_data";
+    method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
+    method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
+    field public static final deprecated java.lang.String DATA = "_data";
     field public static final java.lang.String DEFAULT_SORT_ORDER = "video_id ASC";
     field public static final android.net.Uri EXTERNAL_CONTENT_URI;
     field public static final int FULL_SCREEN_KIND = 2; // 0x2
@@ -45199,6 +45272,7 @@
     method public int getSpanTypeId();
     method public android.content.res.ColorStateList getTextColor();
     method public int getTextFontWeight();
+    method public android.os.LocaleList getTextLocales();
     method public int getTextSize();
     method public int getTextStyle();
     method public android.graphics.Typeface getTypeface();
@@ -51350,6 +51424,7 @@
     method public default android.view.textclassifier.TextClassification classifyText(android.view.textclassifier.TextClassification.Request);
     method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
     method public default void destroy();
+    method public default android.view.textclassifier.TextLanguage detectLanguage(android.view.textclassifier.TextLanguage.Request);
     method public default android.view.textclassifier.TextLinks generateLinks(android.view.textclassifier.TextLinks.Request);
     method public default int getMaxGenerateLinksTextLength();
     method public default boolean isDestroyed();
@@ -51390,6 +51465,39 @@
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR;
   }
 
+  public final class TextLanguage implements android.os.Parcelable {
+    method public int describeContents();
+    method public float getConfidenceScore(android.icu.util.ULocale);
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getId();
+    method public android.icu.util.ULocale getLocale(int);
+    method public int getLocaleHypothesisCount();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLanguage> CREATOR;
+  }
+
+  public static final class TextLanguage.Builder {
+    ctor public TextLanguage.Builder();
+    method public android.view.textclassifier.TextLanguage build();
+    method public android.view.textclassifier.TextLanguage.Builder putLocale(android.icu.util.ULocale, float);
+    method public android.view.textclassifier.TextLanguage.Builder setExtras(android.os.Bundle);
+    method public android.view.textclassifier.TextLanguage.Builder setId(java.lang.String);
+  }
+
+  public static final class TextLanguage.Request implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getText();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLanguage.Request> CREATOR;
+  }
+
+  public static final class TextLanguage.Request.Builder {
+    ctor public TextLanguage.Request.Builder(java.lang.CharSequence);
+    method public android.view.textclassifier.TextLanguage.Request build();
+    method public android.view.textclassifier.TextLanguage.Request.Builder setExtras(android.os.Bundle);
+  }
+
   public final class TextLinks implements android.os.Parcelable {
     method public int apply(android.text.Spannable, int, java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.view.textclassifier.TextLinks.TextLinkSpan>);
     method public int describeContents();
diff --git a/api/system-current.txt b/api/system-current.txt
index 580a760..26036ea 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4471,6 +4471,23 @@
     field public static final java.lang.String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
   }
 
+  public static final class Telephony.Carriers implements android.provider.BaseColumns {
+    field public static final java.lang.String APN_SET_ID = "apn_set_id";
+    field public static final int CARRIER_EDITED = 4; // 0x4
+    field public static final java.lang.String EDITED = "edited";
+    field public static final java.lang.String MAX_CONNS = "max_conns";
+    field public static final java.lang.String MAX_CONNS_TIME = "max_conns_time";
+    field public static final java.lang.String MODEM_COGNITIVE = "modem_cognitive";
+    field public static final java.lang.String MTU = "mtu";
+    field public static final int NO_SET_SET = 0; // 0x0
+    field public static final int UNEDITED = 0; // 0x0
+    field public static final int USER_DELETED = 2; // 0x2
+    field public static final java.lang.String USER_EDITABLE = "user_editable";
+    field public static final int USER_EDITED = 1; // 0x1
+    field public static final java.lang.String USER_VISIBLE = "user_visible";
+    field public static final java.lang.String WAIT_TIME = "wait_time";
+  }
+
   public final class TimeZoneRulesDataContract {
     field public static final java.lang.String AUTHORITY = "com.android.timezone";
   }
@@ -5486,6 +5503,7 @@
     method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method public boolean needsOtaServiceProvisioning();
     method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
+    method public void setCarrierDataEnabled(boolean);
     method public void setDataActivationState(int);
     method public deprecated void setDataEnabled(int, boolean);
     method public void setDataRoamingEnabled(boolean);
@@ -6372,7 +6390,8 @@
   }
 
   public static class MmTelFeature.MmTelCapabilities {
-    ctor public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
+    ctor public MmTelFeature.MmTelCapabilities();
+    ctor public deprecated MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
     ctor public MmTelFeature.MmTelCapabilities(int);
     method public final void addCapabilities(int);
     method public final boolean isCapable(int);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 10b7c0b..39d24ee 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -143,6 +143,7 @@
         BatteryCausedShutdown battery_caused_shutdown = 93;
         PhoneServiceStateChanged phone_service_state_changed = 94;
         PhoneStateChanged phone_state_changed = 95;
+        UserRestrictionChanged user_restriction_changed = 96;
     }
 
     // Pulled events will start at field 10000.
@@ -2356,10 +2357,10 @@
   optional string process_name = 2;
 
   // # of page-faults
-  optional int64 pgfault = 3;
+  optional int64 page_fault = 3;
 
   // # of major page-faults
-  optional int64 pgmajfault = 4;
+  optional int64 page_major_fault = 4;
 
   // RSS
   optional int64 rss_in_bytes = 5;
@@ -3049,3 +3050,17 @@
     // Process cpu time in system space, cumulative from boot/process start
     optional int64 system_time_millis = 4;
 }
+
+/**
+ * Logs when a user restriction was added or removed.
+ *
+ * Logged from:
+ *   frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+ */
+message UserRestrictionChanged {
+    // The raw string of the user restriction as defined in UserManager.
+    // Allowed values are defined in UserRestrictionsUtils#USER_RESTRICTIONS.
+    optional string restriction = 1;
+    // Whether the restriction is enabled or disabled.
+    optional bool enabled = 2;
+}
\ No newline at end of file
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 857c390..bbc3f35 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1383,62 +1383,6 @@
 Landroid/service/wallpaper/IWallpaperEngine;->setVisibility(Z)V
 Landroid/service/wallpaper/IWallpaperService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/wallpaper/IWallpaperService;
 Landroid/speech/IRecognitionListener;->onEvent(ILandroid/os/Bundle;)V
-Landroid/system/Int32Ref;->value:I
-Landroid/system/OsConstants;-><init>()V
-Landroid/system/OsConstants;->AF_NETLINK:I
-Landroid/system/OsConstants;->AF_PACKET:I
-Landroid/system/OsConstants;->ARPHRD_ETHER:I
-Landroid/system/OsConstants;->ARPHRD_LOOPBACK:I
-Landroid/system/OsConstants;->CAP_TO_INDEX(I)I
-Landroid/system/OsConstants;->CAP_TO_MASK(I)I
-Landroid/system/OsConstants;->ENONET:I
-Landroid/system/OsConstants;->ETH_P_ALL:I
-Landroid/system/OsConstants;->ETH_P_ARP:I
-Landroid/system/OsConstants;->ETH_P_IP:I
-Landroid/system/OsConstants;->ETH_P_IPV6:I
-Landroid/system/OsConstants;->EUSERS:I
-Landroid/system/OsConstants;->ICMP6_ECHO_REPLY:I
-Landroid/system/OsConstants;->ICMP6_ECHO_REQUEST:I
-Landroid/system/OsConstants;->ICMP_ECHO:I
-Landroid/system/OsConstants;->ICMP_ECHOREPLY:I
-Landroid/system/OsConstants;->initConstants()V
-Landroid/system/OsConstants;->IP_MULTICAST_ALL:I
-Landroid/system/OsConstants;->IP_RECVTOS:I
-Landroid/system/OsConstants;->MAP_POPULATE:I
-Landroid/system/OsConstants;->NETLINK_NETFILTER:I
-Landroid/system/OsConstants;->NETLINK_ROUTE:I
-Landroid/system/OsConstants;->O_DIRECT:I
-Landroid/system/OsConstants;->placeholder()I
-Landroid/system/OsConstants;->PR_CAP_AMBIENT:I
-Landroid/system/OsConstants;->PR_CAP_AMBIENT_RAISE:I
-Landroid/system/OsConstants;->RLIMIT_NOFILE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_IFADDR:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_MROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_ROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_RULE:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_IFADDR:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_IFINFO:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_MROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_PREFIX:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_ROUTE:I
-Landroid/system/OsConstants;->RTMGRP_LINK:I
-Landroid/system/OsConstants;->RTMGRP_NEIGH:I
-Landroid/system/OsConstants;->RTMGRP_NOTIFY:I
-Landroid/system/OsConstants;->RTMGRP_TC:I
-Landroid/system/OsConstants;->SO_DOMAIN:I
-Landroid/system/OsConstants;->SO_PROTOCOL:I
-Landroid/system/OsConstants;->SPLICE_F_MORE:I
-Landroid/system/OsConstants;->SPLICE_F_MOVE:I
-Landroid/system/OsConstants;->SPLICE_F_NONBLOCK:I
-Landroid/system/OsConstants;->TIOCOUTQ:I
-Landroid/system/OsConstants;->UDP_ENCAP:I
-Landroid/system/OsConstants;->UDP_ENCAP_ESPINUDP:I
-Landroid/system/OsConstants;->UDP_ENCAP_ESPINUDP_NON_IKE:I
-Landroid/system/OsConstants;->UNIX_PATH_MAX:I
-Landroid/system/OsConstants;->XATTR_CREATE:I
-Landroid/system/OsConstants;->XATTR_REPLACE:I
-Landroid/system/OsConstants;->_LINUX_CAPABILITY_VERSION_3:I
-Landroid/system/StructTimeval;->fromMillis(J)Landroid/system/StructTimeval;
 Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
 Landroid/telephony/TelephonyManager$MultiSimVariants;->values()[Landroid/telephony/TelephonyManager$MultiSimVariants;
 Landroid/util/Singleton;-><init>()V
@@ -2339,93 +2283,6 @@
 Lcom/google/android/mms/util/PduCache;->purgeAll()V
 Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
 Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
-Ldalvik/system/BaseDexClassLoader;-><init>(Ljava/lang/String;Ljava/io/File;Ljava/lang/String;Ljava/lang/ClassLoader;Z)V
-Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;)V
-Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;Z)V
-Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String;
-Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;-><init>(IILjava/lang/String;)V
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mMessage:Ljava/lang/String;
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mPolicyState:I
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mPolicyViolated:I
-Ldalvik/system/BlockGuard$Policy;->onNetwork()V
-Ldalvik/system/BlockGuard$Policy;->onReadFromDisk()V
-Ldalvik/system/BlockGuard;->getThreadPolicy()Ldalvik/system/BlockGuard$Policy;
-Ldalvik/system/BlockGuard;->LAX_POLICY:Ldalvik/system/BlockGuard$Policy;
-Ldalvik/system/BlockGuard;->setThreadPolicy(Ldalvik/system/BlockGuard$Policy;)V
-Ldalvik/system/BlockGuard;->threadPolicy:Ljava/lang/ThreadLocal;
-Ldalvik/system/CloseGuard$DefaultReporter;-><init>()V
-Ldalvik/system/CloseGuard$Reporter;->report(Ljava/lang/String;Ljava/lang/Throwable;)V
-Ldalvik/system/CloseGuard;-><init>()V
-Ldalvik/system/CloseGuard;->close()V
-Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
-Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
-Ldalvik/system/CloseGuard;->setEnabled(Z)V
-Ldalvik/system/CloseGuard;->setReporter(Ldalvik/system/CloseGuard$Reporter;)V
-Ldalvik/system/CloseGuard;->warnIfOpen()V
-Ldalvik/system/DexFile$DFEnum;->mNameList:[Ljava/lang/String;
-Ldalvik/system/DexFile;->getClassNameList(Ljava/lang/Object;)[Ljava/lang/String;
-Ldalvik/system/DexFile;->isBackedByOatFile()Z
-Ldalvik/system/DexFile;->loadClassBinaryName(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class;
-Ldalvik/system/DexFile;->loadDex(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ldalvik/system/DexFile;
-Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object;
-Ldalvik/system/DexFile;->mFileName:Ljava/lang/String;
-Ldalvik/system/DexFile;->mInternalCookie:Ljava/lang/Object;
-Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
-Ldalvik/system/DexFile;->openDexFileNative(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
-Ldalvik/system/DexPathList$Element;-><init>(Ldalvik/system/DexFile;Ljava/io/File;)V
-Ldalvik/system/DexPathList$Element;-><init>(Ljava/io/File;ZLjava/io/File;Ldalvik/system/DexFile;)V
-Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile;
-Ldalvik/system/DexPathList$Element;->path:Ljava/io/File;
-Ldalvik/system/DexPathList$NativeLibraryElement;-><init>(Ljava/io/File;)V
-Ldalvik/system/DexPathList$NativeLibraryElement;->path:Ljava/io/File;
-Ldalvik/system/DexPathList;-><init>(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)V
-Ldalvik/system/DexPathList;->addDexPath(Ljava/lang/String;Ljava/io/File;)V
-Ldalvik/system/DexPathList;->addNativePath(Ljava/util/Collection;)V
-Ldalvik/system/DexPathList;->definingContext:Ljava/lang/ClassLoader;
-Ldalvik/system/DexPathList;->dexElements:[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->dexElementsSuppressedExceptions:[Ljava/io/IOException;
-Ldalvik/system/DexPathList;->loadDexFile(Ljava/io/File;Ljava/io/File;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ldalvik/system/DexFile;
-Ldalvik/system/DexPathList;->makeDexElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;Ljava/lang/ClassLoader;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->makeInMemoryDexElements([Ljava/nio/ByteBuffer;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;)[Ldalvik/system/DexPathList$NativeLibraryElement;
-Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List;
-Ldalvik/system/DexPathList;->nativeLibraryPathElements:[Ldalvik/system/DexPathList$NativeLibraryElement;
-Ldalvik/system/DexPathList;->splitPaths(Ljava/lang/String;Z)Ljava/util/List;
-Ldalvik/system/DexPathList;->systemNativeLibraryDirectories:Ljava/util/List;
-Ldalvik/system/SocketTagger;->get()Ldalvik/system/SocketTagger;
-Ldalvik/system/SocketTagger;->tag(Ljava/net/Socket;)V
-Ldalvik/system/SocketTagger;->untag(Ljava/net/Socket;)V
-Ldalvik/system/VMDebug;->allowHiddenApiReflectionFrom(Ljava/lang/Class;)V
-Ldalvik/system/VMDebug;->dumpReferenceTables()V
-Ldalvik/system/VMDebug;->isDebuggerConnected()Z
-Ldalvik/system/VMRuntime;->addressOf(Ljava/lang/Object;)J
-Ldalvik/system/VMRuntime;->clearGrowthLimit()V
-Ldalvik/system/VMRuntime;->gcSoftReferences()V
-Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String;
-Ldalvik/system/VMRuntime;->getExternalBytesAllocated()J
-Ldalvik/system/VMRuntime;->getInstructionSet(Ljava/lang/String;)Ljava/lang/String;
-Ldalvik/system/VMRuntime;->getMinimumHeapSize()J
-Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;
-Ldalvik/system/VMRuntime;->is64Bit()Z
-Ldalvik/system/VMRuntime;->is64BitAbi(Ljava/lang/String;)Z
-Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object;
-Ldalvik/system/VMRuntime;->registerNativeAllocation(I)V
-Ldalvik/system/VMRuntime;->registerNativeFree(I)V
-Ldalvik/system/VMRuntime;->runFinalization(J)V
-Ldalvik/system/VMRuntime;->runFinalizationSync()V
-Ldalvik/system/VMRuntime;->setMinimumHeapSize(J)J
-Ldalvik/system/VMRuntime;->setTargetHeapUtilization(F)F
-Ldalvik/system/VMRuntime;->setTargetSdkVersion(I)V
-Ldalvik/system/VMRuntime;->trackExternalAllocation(J)Z
-Ldalvik/system/VMRuntime;->trackExternalFree(J)V
-Ldalvik/system/VMRuntime;->vmInstructionSet()Ljava/lang/String;
-Ldalvik/system/VMRuntime;->vmLibrary()Ljava/lang/String;
-Ldalvik/system/VMStack;->fillStackTraceElements(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I
-Ldalvik/system/VMStack;->getCallingClassLoader()Ljava/lang/ClassLoader;
-Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class;
-Ldalvik/system/VMStack;->getThreadStackTrace(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;
 Ljava/io/Console;->encoding()Ljava/lang/String;
 Ljava/io/File;->filePath:Ljava/nio/file/Path;
 Ljava/io/File;->fs:Ljava/io/FileSystem;
@@ -2501,18 +2358,6 @@
 Ljava/lang/Class;->objectSize:I
 Ljava/lang/Class;->status:I
 Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader;
-Ljava/lang/Daemons$Daemon;->isRunning()Z
-Ljava/lang/Daemons$Daemon;->start()V
-Ljava/lang/Daemons$Daemon;->stop()V
-Ljava/lang/Daemons$Daemon;->thread:Ljava/lang/Thread;
-Ljava/lang/Daemons$FinalizerDaemon;->finalizingObject:Ljava/lang/Object;
-Ljava/lang/Daemons$FinalizerDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerDaemon;
-Ljava/lang/Daemons$FinalizerWatchdogDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon;
-Ljava/lang/Daemons$ReferenceQueueDaemon;->INSTANCE:Ljava/lang/Daemons$ReferenceQueueDaemon;
-Ljava/lang/Daemons;->MAX_FINALIZE_NANOS:J
-Ljava/lang/Daemons;->requestHeapTrim()V
-Ljava/lang/Daemons;->start()V
-Ljava/lang/Daemons;->stop()V
 Ljava/lang/Double;->value:D
 Ljava/lang/Enum;->getSharedConstants(Ljava/lang/Class;)[Ljava/lang/Enum;
 Ljava/lang/Enum;->name:Ljava/lang/String;
@@ -2522,11 +2367,6 @@
 Ljava/lang/invoke/MethodHandles$Lookup;-><init>(Ljava/lang/Class;I)V
 Ljava/lang/Long;->value:J
 Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I
-Ljava/lang/ref/FinalizerReference;->add(Ljava/lang/Object;)V
-Ljava/lang/ref/FinalizerReference;->head:Ljava/lang/ref/FinalizerReference;
-Ljava/lang/ref/FinalizerReference;->next:Ljava/lang/ref/FinalizerReference;
-Ljava/lang/ref/FinalizerReference;->queue:Ljava/lang/ref/ReferenceQueue;
-Ljava/lang/ref/FinalizerReference;->remove(Ljava/lang/ref/FinalizerReference;)V
 Ljava/lang/ref/Reference;->getReferent()Ljava/lang/Object;
 Ljava/lang/ref/Reference;->referent:Ljava/lang/Object;
 Ljava/lang/ref/ReferenceQueue;->add(Ljava/lang/ref/Reference;)V
@@ -2669,12 +2509,6 @@
 Ljava/nio/charset/Charset;->defaultCharset:Ljava/nio/charset/Charset;
 Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
 Ljava/nio/DirectByteBuffer;-><init>(JI)V
-Ljava/nio/NIOAccess;->getBaseArray(Ljava/nio/Buffer;)Ljava/lang/Object;
-Ljava/nio/NIOAccess;->getBaseArrayOffset(Ljava/nio/Buffer;)I
-Ljava/nio/NIOAccess;->getBasePointer(Ljava/nio/Buffer;)J
-Ljava/nio/NioUtils;->freeDirectBuffer(Ljava/nio/ByteBuffer;)V
-Ljava/nio/NioUtils;->unsafeArray(Ljava/nio/ByteBuffer;)[B
-Ljava/nio/NioUtils;->unsafeArrayOffset(Ljava/nio/ByteBuffer;)I
 Ljava/security/KeyPairGenerator;->getInstance(Lsun/security/jca/GetInstance$Instance;Ljava/lang/String;)Ljava/security/KeyPairGenerator;
 Ljava/security/KeyStore;->keyStoreSpi:Ljava/security/KeyStoreSpi;
 Ljava/security/Signature;->getInstance(Lsun/security/jca/GetInstance$Instance;Ljava/lang/String;)Ljava/security/Signature;
@@ -2790,9 +2624,6 @@
 Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
 Ljavax/net/ssl/SSLSocketFactory;->createSocket(Ljava/net/Socket;Ljava/io/InputStream;Z)Ljava/net/Socket;
 Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
-Llibcore/icu/ICU;->addLikelySubtags(Ljava/util/Locale;)Ljava/util/Locale;
-Llibcore/util/BasicLruCache;->map:Ljava/util/LinkedHashMap;
-Llibcore/util/ZoneInfo;->mTransitions:[J
 Lorg/ccil/cowan/tagsoup/AttributesImpl;->data:[Ljava/lang/String;
 Lorg/ccil/cowan/tagsoup/AttributesImpl;->length:I
 Lorg/ccil/cowan/tagsoup/ElementType;->theAtts:Lorg/ccil/cowan/tagsoup/AttributesImpl;
@@ -2811,100 +2642,6 @@
 Lorg/ccil/cowan/tagsoup/Schema;->thePrefix:Ljava/lang/String;
 Lorg/ccil/cowan/tagsoup/Schema;->theRoot:Lorg/ccil/cowan/tagsoup/ElementType;
 Lorg/ccil/cowan/tagsoup/Schema;->theURI:Ljava/lang/String;
-Lorg/json/JSONArray;->values:Ljava/util/List;
-Lorg/json/JSONArray;->writeTo(Lorg/json/JSONStringer;)V
-Lorg/json/JSONObject;->append(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
-Lorg/json/JSONObject;->checkName(Ljava/lang/String;)Ljava/lang/String;
-Lorg/json/JSONObject;->keySet()Ljava/util/Set;
-Lorg/json/JSONObject;->nameValuePairs:Ljava/util/LinkedHashMap;
-Lorg/json/JSONObject;->NEGATIVE_ZERO:Ljava/lang/Double;
-Lorg/json/JSONObject;->writeTo(Lorg/json/JSONStringer;)V
-Lorg/json/JSONStringer;-><init>(I)V
-Lorg/json/JSONStringer;->beforeKey()V
-Lorg/json/JSONStringer;->beforeValue()V
-Lorg/json/JSONStringer;->close(Lorg/json/JSONStringer$Scope;Lorg/json/JSONStringer$Scope;Ljava/lang/String;)Lorg/json/JSONStringer;
-Lorg/json/JSONStringer;->indent:Ljava/lang/String;
-Lorg/json/JSONStringer;->newline()V
-Lorg/json/JSONStringer;->open(Lorg/json/JSONStringer$Scope;Ljava/lang/String;)Lorg/json/JSONStringer;
-Lorg/json/JSONStringer;->out:Ljava/lang/StringBuilder;
-Lorg/json/JSONStringer;->peek()Lorg/json/JSONStringer$Scope;
-Lorg/json/JSONStringer;->replaceTop(Lorg/json/JSONStringer$Scope;)V
-Lorg/json/JSONStringer;->stack:Ljava/util/List;
-Lorg/json/JSONStringer;->string(Ljava/lang/String;)V
-Lorg/json/JSONTokener;->in:Ljava/lang/String;
-Lorg/json/JSONTokener;->nextCleanInternal()I
-Lorg/json/JSONTokener;->nextToInternal(Ljava/lang/String;)Ljava/lang/String;
-Lorg/json/JSONTokener;->pos:I
-Lorg/json/JSONTokener;->readArray()Lorg/json/JSONArray;
-Lorg/json/JSONTokener;->readEscapeCharacter()C
-Lorg/json/JSONTokener;->readLiteral()Ljava/lang/Object;
-Lorg/json/JSONTokener;->readObject()Lorg/json/JSONObject;
-Lorg/json/JSONTokener;->skipToEndOfLine()V
-Lorg/w3c/dom/ls/LSSerializerFilter;->getWhatToShow()I
-Lorg/w3c/dom/traversal/NodeFilter;->acceptNode(Lorg/w3c/dom/Node;)S
-Lorg/w3c/dom/traversal/NodeIterator;->detach()V
-Lorg/w3c/dom/traversal/NodeIterator;->nextNode()Lorg/w3c/dom/Node;
-Lorg/xml/sax/ext/Attributes2Impl;->declared:[Z
-Lorg/xml/sax/ext/Attributes2Impl;->specified:[Z
-Lorg/xml/sax/ext/Locator2Impl;->encoding:Ljava/lang/String;
-Lorg/xml/sax/ext/Locator2Impl;->version:Ljava/lang/String;
-Lorg/xml/sax/helpers/AttributesImpl;->badIndex(I)V
-Lorg/xml/sax/helpers/AttributesImpl;->data:[Ljava/lang/String;
-Lorg/xml/sax/helpers/AttributesImpl;->ensureCapacity(I)V
-Lorg/xml/sax/helpers/AttributesImpl;->length:I
-Lorg/xml/sax/helpers/LocatorImpl;->columnNumber:I
-Lorg/xml/sax/helpers/LocatorImpl;->lineNumber:I
-Lorg/xml/sax/helpers/LocatorImpl;->publicId:Ljava/lang/String;
-Lorg/xml/sax/helpers/LocatorImpl;->systemId:Ljava/lang/String;
-Lorg/xml/sax/helpers/NamespaceSupport;->contextPos:I
-Lorg/xml/sax/helpers/NamespaceSupport;->contexts:[Lorg/xml/sax/helpers/NamespaceSupport$Context;
-Lorg/xml/sax/helpers/NamespaceSupport;->currentContext:Lorg/xml/sax/helpers/NamespaceSupport$Context;
-Lorg/xml/sax/helpers/NamespaceSupport;->EMPTY_ENUMERATION:Ljava/util/Enumeration;
-Lorg/xml/sax/helpers/NamespaceSupport;->namespaceDeclUris:Z
-Lorg/xml/sax/helpers/ParserAdapter;->attAdapter:Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;
-Lorg/xml/sax/helpers/ParserAdapter;->atts:Lorg/xml/sax/helpers/AttributesImpl;
-Lorg/xml/sax/helpers/ParserAdapter;->checkNotParsing(Ljava/lang/String;Ljava/lang/String;)V
-Lorg/xml/sax/helpers/ParserAdapter;->contentHandler:Lorg/xml/sax/ContentHandler;
-Lorg/xml/sax/helpers/ParserAdapter;->dtdHandler:Lorg/xml/sax/DTDHandler;
-Lorg/xml/sax/helpers/ParserAdapter;->entityResolver:Lorg/xml/sax/EntityResolver;
-Lorg/xml/sax/helpers/ParserAdapter;->errorHandler:Lorg/xml/sax/ErrorHandler;
-Lorg/xml/sax/helpers/ParserAdapter;->locator:Lorg/xml/sax/Locator;
-Lorg/xml/sax/helpers/ParserAdapter;->makeException(Ljava/lang/String;)Lorg/xml/sax/SAXParseException;
-Lorg/xml/sax/helpers/ParserAdapter;->nameParts:[Ljava/lang/String;
-Lorg/xml/sax/helpers/ParserAdapter;->namespaces:Z
-Lorg/xml/sax/helpers/ParserAdapter;->nsSupport:Lorg/xml/sax/helpers/NamespaceSupport;
-Lorg/xml/sax/helpers/ParserAdapter;->parser:Lorg/xml/sax/Parser;
-Lorg/xml/sax/helpers/ParserAdapter;->parsing:Z
-Lorg/xml/sax/helpers/ParserAdapter;->prefixes:Z
-Lorg/xml/sax/helpers/ParserAdapter;->processName(Ljava/lang/String;ZZ)[Ljava/lang/String;
-Lorg/xml/sax/helpers/ParserAdapter;->reportError(Ljava/lang/String;)V
-Lorg/xml/sax/helpers/ParserAdapter;->setup(Lorg/xml/sax/Parser;)V
-Lorg/xml/sax/helpers/ParserAdapter;->setupParser()V
-Lorg/xml/sax/helpers/ParserAdapter;->uris:Z
-Lorg/xml/sax/helpers/XMLFilterImpl;->contentHandler:Lorg/xml/sax/ContentHandler;
-Lorg/xml/sax/helpers/XMLFilterImpl;->dtdHandler:Lorg/xml/sax/DTDHandler;
-Lorg/xml/sax/helpers/XMLFilterImpl;->entityResolver:Lorg/xml/sax/EntityResolver;
-Lorg/xml/sax/helpers/XMLFilterImpl;->errorHandler:Lorg/xml/sax/ErrorHandler;
-Lorg/xml/sax/helpers/XMLFilterImpl;->locator:Lorg/xml/sax/Locator;
-Lorg/xml/sax/helpers/XMLFilterImpl;->parent:Lorg/xml/sax/XMLReader;
-Lorg/xml/sax/helpers/XMLFilterImpl;->setupParse()V
-Lorg/xml/sax/helpers/XMLReaderAdapter;->documentHandler:Lorg/xml/sax/DocumentHandler;
-Lorg/xml/sax/helpers/XMLReaderAdapter;->qAtts:Lorg/xml/sax/helpers/XMLReaderAdapter$AttributesAdapter;
-Lorg/xml/sax/helpers/XMLReaderAdapter;->setup(Lorg/xml/sax/XMLReader;)V
-Lorg/xml/sax/helpers/XMLReaderAdapter;->setupXMLReader()V
-Lorg/xml/sax/helpers/XMLReaderAdapter;->xmlReader:Lorg/xml/sax/XMLReader;
-Lorg/xml/sax/helpers/XMLReaderFactory;->loadClass(Ljava/lang/ClassLoader;Ljava/lang/String;)Lorg/xml/sax/XMLReader;
-Lorg/xml/sax/InputSource;->byteStream:Ljava/io/InputStream;
-Lorg/xml/sax/InputSource;->characterStream:Ljava/io/Reader;
-Lorg/xml/sax/InputSource;->encoding:Ljava/lang/String;
-Lorg/xml/sax/InputSource;->publicId:Ljava/lang/String;
-Lorg/xml/sax/InputSource;->systemId:Ljava/lang/String;
-Lorg/xml/sax/SAXException;->exception:Ljava/lang/Exception;
-Lorg/xml/sax/SAXParseException;->columnNumber:I
-Lorg/xml/sax/SAXParseException;->init(Ljava/lang/String;Ljava/lang/String;II)V
-Lorg/xml/sax/SAXParseException;->lineNumber:I
-Lorg/xml/sax/SAXParseException;->publicId:Ljava/lang/String;
-Lorg/xml/sax/SAXParseException;->systemId:Ljava/lang/String;
 Lsun/misc/Cleaner;->clean()V
 Lsun/misc/Unsafe;->addressSize()I
 Lsun/misc/Unsafe;->allocateInstance(Ljava/lang/Class;)Ljava/lang/Object;
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 45e38cf9..e5e64d3 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -122,14 +122,6 @@
 Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
 Landroid/os/UserManager;->isAdminUser()Z
 Landroid/R$styleable;->CheckBoxPreference:[I
-Landroid/system/NetlinkSocketAddress;-><init>(II)V
-Landroid/system/Os;->bind(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V
-Landroid/system/Os;->connect(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V
-Landroid/system/Os;->sendto(Ljava/io/FileDescriptor;[BIIILjava/net/SocketAddress;)I
-Landroid/system/Os;->setsockoptIfreq(Ljava/io/FileDescriptor;IILjava/lang/String;)V
-Landroid/system/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Landroid/system/PacketSocketAddress;-><init>(I[B)V
-Landroid/system/PacketSocketAddress;-><init>(SI)V
 Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
 Landroid/telephony/ims/compat/ImsService;-><init>()V
 Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 56ca98f..1a8a32e 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2354,6 +2354,7 @@
 android.nfc.NfcAdapter$CreateNdefMessageCallback
 android.nfc.NfcManager
 android.opengl.EGL14
+android.opengl.EGL15
 android.opengl.EGLConfig
 android.opengl.EGLContext
 android.opengl.EGLDisplay
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d00650d..069effd 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -250,4 +250,34 @@
     public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
             ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
             boolean aboveSystem, String reason);
+
+    /**
+     * Sends {@link android.content.Intent#ACTION_CONFIGURATION_CHANGED} with all the appropriate
+     * flags.
+     */
+    public abstract void broadcastGlobalConfigurationChanged(int changes, boolean initLocale);
+
+    /**
+     * Sends {@link android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS} with all the appropriate
+     * flags.
+     */
+    public abstract void broadcastCloseSystemDialogs(String reason);
+
+    /**
+     * Kills all background processes, except those matching any of the specified properties.
+     *
+     * @param minTargetSdk the target SDK version at or above which to preserve processes,
+     *                     or {@code -1} to ignore the target SDK
+     * @param maxProcState the process state at or below which to preserve processes,
+     *                     or {@code -1} to ignore the process state
+     */
+    public abstract void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState);
+
+    /** Starts a given process. */
+    public abstract void startProcess(String processName, ApplicationInfo info,
+            boolean knownToBeDead, String hostingType, ComponentName hostingName);
+
+    /** Starts up the starting activity process for debugging if needed. */
+    public abstract void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
+            ProfilerInfo profilerInfo);
 }
diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java
index ceb828b..0e428ae 100644
--- a/core/java/android/app/FragmentTransition.java
+++ b/core/java/android/app/FragmentTransition.java
@@ -1013,6 +1013,11 @@
                     replaceTargets(sharedElementTransition, sharedElementsIn, null);
                 }
             }
+
+            @Override
+            public void onTransitionEnd(Transition transition) {
+                transition.removeListener(this);
+            }
         });
     }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b289a3e..92daf08 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -51,6 +51,7 @@
 import android.graphics.Bitmap;
 import android.net.ProxyInfo;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
@@ -5786,7 +5787,8 @@
         }
         if (mService != null) {
             try {
-                return mService.checkDeviceIdentifierAccess(packageName, userId);
+                return mService.checkDeviceIdentifierAccess(packageName, userId,
+                        Binder.getCallingPid(), Binder.getCallingUid());
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index cf0cad8..ce1f4ef 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -153,7 +153,7 @@
     void clearProfileOwner(in ComponentName who);
     boolean hasUserSetupCompleted();
 
-    boolean checkDeviceIdentifierAccess(in String packageName, int userHandle);
+    boolean checkDeviceIdentifierAccess(in String packageName, int userHandle, int pid, int uid);
 
     void setDeviceOwnerLockScreenInfo(in ComponentName who, CharSequence deviceOwnerInfo);
     CharSequence getDeviceOwnerLockScreenInfo();
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index fda2f89..cb996f3 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -183,7 +184,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index e7c8944..c447868 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -23,6 +23,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -138,7 +139,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 9b7c173..d616b8f 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -57,6 +57,7 @@
     private int mAuthRetryState;
     private int mConnState;
     private final Object mStateLock = new Object();
+    private final Object mDeviceBusyLock = new Object();
     @UnsupportedAppUsage
     private Boolean mDeviceBusy = false;
     @UnsupportedAppUsage
@@ -282,7 +283,7 @@
                         }
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
                 }
@@ -357,7 +358,7 @@
                         return;
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
 
@@ -413,7 +414,7 @@
                         return;
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
 
@@ -496,7 +497,7 @@
                         return;
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
 
@@ -547,7 +548,7 @@
                         return;
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
 
@@ -596,7 +597,7 @@
                         return;
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
 
@@ -1098,7 +1099,7 @@
         BluetoothDevice device = service.getDevice();
         if (device == null) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
@@ -1132,7 +1133,7 @@
         if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
         if (mService == null || mClientIf == 0) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
@@ -1178,7 +1179,7 @@
         BluetoothDevice device = service.getDevice();
         if (device == null) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
@@ -1221,7 +1222,7 @@
         BluetoothDevice device = service.getDevice();
         if (device == null) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
@@ -1262,7 +1263,7 @@
         BluetoothDevice device = service.getDevice();
         if (device == null) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
@@ -1330,7 +1331,7 @@
         if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
         if (mService == null || mClientIf == 0) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index ec18d42..549c1fa 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -428,7 +429,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index b967fb2..22d41d9 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -24,6 +24,7 @@
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -491,7 +492,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 606f00a..47c4ee6 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -29,6 +29,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -205,7 +206,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent);
             return;
         }
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index 3bc8544..e44f36e 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -24,6 +24,7 @@
 import android.content.ServiceConnection;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -454,7 +455,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 0ca39f1..58a2522 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -25,6 +25,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -279,7 +280,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 98c23c6..fc5f830 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -110,7 +111,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 559a59b..1c82e19 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -25,6 +25,7 @@
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -128,7 +129,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 58be732..8923d73 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -26,6 +26,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -150,7 +151,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index ae264e1..a601df0 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -24,6 +24,7 @@
 import android.content.ServiceConnection;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -164,7 +165,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 1446adc..cbc96c0 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -23,6 +23,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -116,7 +117,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 1b73206..ebf6bed 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -148,7 +149,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
             return false;
         }
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 2a03787..145c9273 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -822,6 +822,48 @@
     }
 
     /**
+     * Opaque token representing the identity of an incoming IPC.
+     */
+    public final class CallingIdentity {
+        /** {@hide} */
+        public final long binderToken;
+        /** {@hide} */
+        public final String callingPackage;
+
+        /** {@hide} */
+        public CallingIdentity(long binderToken, String callingPackage) {
+            this.binderToken = binderToken;
+            this.callingPackage = callingPackage;
+        }
+    }
+
+    /**
+     * Reset the identity of the incoming IPC on the current thread.
+     * <p>
+     * Internally this calls {@link Binder#clearCallingIdentity()} and also
+     * clears any value stored in {@link #getCallingPackage()}.
+     *
+     * @return Returns an opaque token that can be used to restore the original
+     *         calling identity by passing it to
+     *         {@link #restoreCallingIdentity}.
+     */
+    public final @NonNull CallingIdentity clearCallingIdentity() {
+        return new CallingIdentity(Binder.clearCallingIdentity(), setCallingPackage(null));
+    }
+
+    /**
+     * Restore the identity of the incoming IPC on the current thread back to a
+     * previously identity that was returned by {@link #clearCallingIdentity}.
+     * <p>
+     * Internally this calls {@link Binder#restoreCallingIdentity(long)} and
+     * also restores any value stored in {@link #getCallingPackage()}.
+     */
+    public final void restoreCallingIdentity(@NonNull CallingIdentity identity) {
+        Binder.restoreCallingIdentity(identity.binderToken);
+        mCallingPackage.set(identity.callingPackage);
+    }
+
+    /**
      * Change the authorities of the ContentProvider.
      * This is normally set for you from its manifest information when the provider is first
      * created.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index c19909d..599c2d2 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -35,6 +35,12 @@
 import android.database.CrossProcessCursorWrapper;
 import android.database.Cursor;
 import android.database.IContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.ImageInfo;
+import android.graphics.ImageDecoder.Source;
+import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -49,9 +55,11 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.DocumentsContract;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Size;
 
 import com.android.internal.util.MimeIconUtils;
 import com.android.internal.util.Preconditions;
@@ -68,6 +76,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -3188,4 +3197,62 @@
         }
         return query;
     }
+
+    /**
+     * Convenience method that efficiently loads a visual thumbnail for the
+     * given {@link Uri}. Internally calls
+     * {@link ContentProvider#openTypedAssetFile} on the remote provider, but
+     * also defensively resizes any returned content to match the requested
+     * target size.
+     *
+     * @param uri The item that should be visualized as a thumbnail.
+     * @param size The target area on the screen where this thumbnail will be
+     *            shown. This is passed to the provider as {@link #EXTRA_SIZE}
+     *            to help it avoid downloading or generating heavy resources.
+     * @param signal A signal to cancel the operation in progress.
+     * @return Valid {@link Bitmap} which is a visual thumbnail.
+     * @throws IOException If any trouble was encountered while generating or
+     *             loading the thumbnail, or if
+     *             {@link CancellationSignal#cancel()} was invoked.
+     */
+    public @NonNull Bitmap loadThumbnail(@NonNull Uri uri, @NonNull Size size,
+            @Nullable CancellationSignal signal) throws IOException {
+        Objects.requireNonNull(uri);
+        Objects.requireNonNull(size);
+
+        try (ContentProviderClient client = acquireContentProviderClient(uri)) {
+            return loadThumbnail(client, uri, size, signal, ImageDecoder.ALLOCATOR_DEFAULT);
+        }
+    }
+
+    /** {@hide} */
+    public static Bitmap loadThumbnail(@NonNull ContentProviderClient client, @NonNull Uri uri,
+            @NonNull Size size, @Nullable CancellationSignal signal, int allocator)
+            throws IOException {
+        Objects.requireNonNull(client);
+        Objects.requireNonNull(uri);
+        Objects.requireNonNull(size);
+
+        // Convert to Point, since that's what the API is defined as
+        final Bundle opts = new Bundle();
+        opts.putParcelable(EXTRA_SIZE, Point.convert(size));
+
+        return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
+            return client.openTypedAssetFileDescriptor(uri, "image/*", opts, signal);
+        }), (ImageDecoder decoder, ImageInfo info, Source source) -> {
+            decoder.setAllocator(allocator);
+
+            // One last-ditch check to see if we've been canceled.
+            if (signal != null) signal.throwIfCanceled();
+
+            // We requested a rough thumbnail size, but the remote size may have
+            // returned something giant, so defensively scale down as needed.
+            final int widthSample = info.getSize().getWidth() / size.getWidth();
+            final int heightSample = info.getSize().getHeight() / size.getHeight();
+            final int sample = Math.min(widthSample, heightSample);
+            if (sample > 1) {
+                decoder.setTargetSampleSize(sample);
+            }
+        });
+    }
 }
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 6609b76..5de89e3 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -105,12 +105,15 @@
      *
      * This method should be used if the caller wants to receive notifications even after the
      * process exits. The client must have an open connection with the Context Hub Service (i.e. it
-     * cannot have been closed through the {@link #close()} method). If registered successfully,
-     * intents will be delivered regarding events for the specified nanoapp from the attached
-     * Context Hub. Any unicast messages for this client will also be delivered. The intent will
-     * have an extra {@link #EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which will
-     * contain the type of the event. See {@link ContextHubManager.Event} for description of each
-     * event type.
+     * cannot have been closed through the {@link #close()} method). Only one PendingIntent can be
+     * registered at a time for a single ContextHubClient. If registered successfully, intents will
+     * be delivered regarding events for the specified nanoapp from the attached Context Hub. Any
+     * unicast messages for this client will also be delivered. The intent will have an extra
+     * {@link ContextHubManager.EXTRA_CONTEXT_HUB_INFO} of type {@link ContextHubInfo}, which
+     * describes the Context Hub the intent event was for. The intent will also have an extra
+     * {@link ContextHubManager.EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which
+     * will contain the type of the event. See {@link ContextHubManager.Event} for description of
+     * each event type, along with event-specific extra fields.
      *
      * When the intent is received, this client can be recreated through
      * {@link ContextHubManager.createClient(PendingIntent, ContextHubInfo,
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1fbfa40..4714587 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2816,10 +2816,11 @@
          * @param network The {@link Network} of the satisfying network.
          * @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
          * @param linkProperties The {@link LinkProperties} of the satisfying network.
+         * @param blocked Whether access to the {@link Network} is blocked due to system policy.
          * @hide
          */
         public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
-                LinkProperties linkProperties) {
+                LinkProperties linkProperties, boolean blocked) {
             // Internally only this method is called when a new network is available, and
             // it calls the callback in the same way and order that older versions used
             // to call so as not to change the behavior.
@@ -2830,6 +2831,7 @@
             }
             onCapabilitiesChanged(network, networkCapabilities);
             onLinkPropertiesChanged(network, linkProperties);
+            onBlockedStatusChanged(network, blocked);
         }
 
         /**
@@ -2837,7 +2839,8 @@
          * This callback may be called more than once if the {@link Network} that is
          * satisfying the request changes. This will always immediately be followed by a
          * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
-         * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}.
+         * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call to
+         * {@link #onBlockedStatusChanged(Network, boolean)}.
          *
          * @param network The {@link Network} of the satisfying network.
          */
@@ -2916,6 +2919,14 @@
          */
         public void onNetworkResumed(Network network) {}
 
+        /**
+         * Called when access to the specified network is blocked or unblocked.
+         *
+         * @param network The {@link Network} whose blocked status has changed.
+         * @param blocked The blocked status of this {@link Network}.
+         */
+        public void onBlockedStatusChanged(Network network, boolean blocked) {}
+
         private NetworkRequest networkRequest;
     }
 
@@ -2962,6 +2973,8 @@
     public static final int CALLBACK_SUSPENDED           = BASE + 9;
     /** @hide */
     public static final int CALLBACK_RESUMED             = BASE + 10;
+    /** @hide */
+    public static final int CALLBACK_BLK_CHANGED         = BASE + 11;
 
     /** @hide */
     public static String getCallbackName(int whichCallback) {
@@ -2976,6 +2989,7 @@
             case EXPIRE_LEGACY_REQUEST: return "EXPIRE_LEGACY_REQUEST";
             case CALLBACK_SUSPENDED:    return "CALLBACK_SUSPENDED";
             case CALLBACK_RESUMED:      return "CALLBACK_RESUMED";
+            case CALLBACK_BLK_CHANGED:  return "CALLBACK_BLK_CHANGED";
             default:
                 return Integer.toString(whichCallback);
         }
@@ -3022,7 +3036,7 @@
                 case CALLBACK_AVAILABLE: {
                     NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
                     LinkProperties lp = getObject(message, LinkProperties.class);
-                    callback.onAvailable(network, cap, lp);
+                    callback.onAvailable(network, cap, lp, message.arg1 != 0);
                     break;
                 }
                 case CALLBACK_LOSING: {
@@ -3055,6 +3069,10 @@
                     callback.onNetworkResumed(network);
                     break;
                 }
+                case CALLBACK_BLK_CHANGED: {
+                    boolean blocked = message.arg1 != 0;
+                    callback.onBlockedStatusChanged(network, blocked);
+                }
             }
         }
 
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 12b6f9e..0bdfca7 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1590,4 +1590,14 @@
         Preconditions.checkArgument(isValidCapability(capability),
                 "NetworkCapability " + capability + "out of range");
     }
+
+    /**
+     * Check if this {@code NetworkCapability} instance is metered.
+     *
+     * @return {@code true} if {@code NET_CAPABILITY_NOT_METERED} is not set on this instance.
+     * @hide
+     */
+    public boolean isMetered() {
+        return !hasCapability(NET_CAPABILITY_NOT_METERED);
+    }
 }
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index d912dd10..1a1d2d334 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -202,7 +202,9 @@
      * Return a network-type-specific integer describing the subtype
      * of the network.
      * @return the network subtype
+     * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
      */
+    @Deprecated
     public int getSubtype() {
         synchronized (this) {
             return mSubtype;
@@ -243,7 +245,9 @@
     /**
      * Return a human-readable name describing the subtype of the network.
      * @return the name of the network subtype
+     * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
      */
+    @Deprecated
     public String getSubtypeName() {
         synchronized (this) {
             return mSubtypeName;
@@ -278,7 +282,15 @@
      * connections and pass data.
      * <p>Always call this before attempting to perform data transactions.
      * @return {@code true} if network connectivity exists, {@code false} otherwise.
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes. See
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public boolean isConnected() {
         synchronized (this) {
             return mState == State.CONNECTED;
@@ -411,7 +423,15 @@
     /**
      * Reports the current fine-grained state of the network.
      * @return the fine-grained state
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes. See
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public DetailedState getDetailedState() {
         synchronized (this) {
             return mDetailedState;
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 40465ce..d09f33b 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1102,19 +1102,18 @@
         public String getHost() {
             @SuppressWarnings("StringEquality")
             boolean cached = (host != NOT_CACHED);
-            return cached ? host
-                    : (host = parseHost());
+            return cached ? host : (host = parseHost());
         }
 
         private String parseHost() {
-            String authority = getEncodedAuthority();
+            final String authority = getEncodedAuthority();
             if (authority == null) {
                 return null;
             }
 
             // Parse out user info and then port.
             int userInfoSeparator = authority.lastIndexOf('@');
-            int portSeparator = authority.indexOf(':', userInfoSeparator);
+            int portSeparator = findPortSeparator(authority);
 
             String encodedHost = portSeparator == NOT_FOUND
                     ? authority.substring(userInfoSeparator + 1)
@@ -1132,16 +1131,8 @@
         }
 
         private int parsePort() {
-            String authority = getEncodedAuthority();
-            if (authority == null) {
-                return -1;
-            }
-
-            // Make sure we look for the port separtor *after* the user info
-            // separator. We have URLs with a ':' in the user info.
-            int userInfoSeparator = authority.lastIndexOf('@');
-            int portSeparator = authority.indexOf(':', userInfoSeparator);
-
+            final String authority = getEncodedAuthority();
+            int portSeparator = findPortSeparator(authority);
             if (portSeparator == NOT_FOUND) {
                 return -1;
             }
@@ -1154,6 +1145,24 @@
                 return -1;
             }
         }
+
+        private int findPortSeparator(String authority) {
+            if (authority == null) {
+                return NOT_FOUND;
+            }
+
+            // Reverse search for the ':' character that breaks as soon as a char that is neither
+            // a colon nor an ascii digit is encountered. Thanks to the goodness of UTF-16 encoding,
+            // it's not possible that a surrogate matches one of these, so this loop can just
+            // look for characters rather than care about code points.
+            for (int i = authority.length() - 1; i >= 0; --i) {
+                final int character = authority.charAt(i);
+                if (':' == character) return i;
+                // Character.isDigit would include non-ascii digits
+                if (character < '0' || character > '9') return NOT_FOUND;
+            }
+            return NOT_FOUND;
+        }
     }
 
     /**
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 412a700..292543c 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -130,9 +130,9 @@
      * <a href="/training/articles/security-key-attestation.html">key attestation</a> to obtain
      * proof of the device's original identifiers.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. Profile owner access is
+     * deprecated and will be removed in a future release.
      *
      * @return The serial number if specified.
      */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 0d5d547..00b989e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -272,6 +272,10 @@
      *
      * Specifies if all users on the device are disallowed from enabling the
      * "Unknown Sources" setting, that allows installation of apps from unknown sources.
+     *
+     * This restriction can be enabled by the profile owner, in which case all accounts and
+     * profiles will be affected.
+     *
      * The default value is <code>false</code>.
      *
      * <p>Key for user restrictions.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 50ca4ab..df771df 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1538,6 +1538,9 @@
      * @hide
      */
     public File translateAppToSystem(File file, String packageName) {
+        // We can only translate absolute paths
+        if (!file.isAbsolute()) return file;
+
         try {
             return new File(mStorageManager.translateAppToSystem(file.getAbsolutePath(),
                     packageName, mContext.getUserId()));
@@ -1553,6 +1556,9 @@
      * @hide
      */
     public File translateSystemToApp(File file, String packageName) {
+        // We can only translate absolute paths
+        if (!file.isAbsolute()) return file;
+
         try {
             return new File(mStorageManager.translateSystemToApp(file.getAbsolutePath(),
                     packageName, mContext.getUserId()));
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 8c40e0e..954d18a 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -34,6 +34,7 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.ImageDecoder;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.media.ExifInterface;
@@ -53,6 +54,7 @@
 import android.system.Os;
 import android.util.DataUnit;
 import android.util.Log;
+import android.util.Size;
 
 import libcore.io.IoUtils;
 
@@ -136,10 +138,11 @@
     public static final String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
 
     /**
-     * Included in {@link AssetFileDescriptor#getExtras()} when returned
-     * thumbnail should be rotated.
+     * An extra number of degrees that an image should be rotated during the
+     * decode process to be presented correctly.
      *
-     * @see MediaStore.Images.ImageColumns#ORIENTATION
+     * @see AssetFileDescriptor#getExtras()
+     * @see android.provider.MediaStore.Images.ImageColumns#ORIENTATION
      */
     public static final String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
 
@@ -1093,75 +1096,10 @@
 
     /** {@hide} */
     @UnsupportedAppUsage
-    public static Bitmap getDocumentThumbnail(
-            ContentProviderClient client, Uri documentUri, Point size, CancellationSignal signal)
-            throws RemoteException, IOException {
-        final Bundle openOpts = new Bundle();
-        openOpts.putParcelable(ContentResolver.EXTRA_SIZE, size);
-
-        AssetFileDescriptor afd = null;
-        Bitmap bitmap = null;
-        try {
-            afd = client.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal);
-
-            final FileDescriptor fd = afd.getFileDescriptor();
-            final long offset = afd.getStartOffset();
-
-            // Try seeking on the returned FD, since it gives us the most
-            // optimal decode path; otherwise fall back to buffering.
-            BufferedInputStream is = null;
-            try {
-                Os.lseek(fd, offset, SEEK_SET);
-            } catch (ErrnoException e) {
-                is = new BufferedInputStream(new FileInputStream(fd), THUMBNAIL_BUFFER_SIZE);
-                is.mark(THUMBNAIL_BUFFER_SIZE);
-            }
-
-            // We requested a rough thumbnail size, but the remote size may have
-            // returned something giant, so defensively scale down as needed.
-            final BitmapFactory.Options opts = new BitmapFactory.Options();
-            opts.inJustDecodeBounds = true;
-            if (is != null) {
-                BitmapFactory.decodeStream(is, null, opts);
-            } else {
-                BitmapFactory.decodeFileDescriptor(fd, null, opts);
-            }
-
-            final int widthSample = opts.outWidth / size.x;
-            final int heightSample = opts.outHeight / size.y;
-
-            opts.inJustDecodeBounds = false;
-            opts.inSampleSize = Math.min(widthSample, heightSample);
-            if (is != null) {
-                is.reset();
-                bitmap = BitmapFactory.decodeStream(is, null, opts);
-            } else {
-                try {
-                    Os.lseek(fd, offset, SEEK_SET);
-                } catch (ErrnoException e) {
-                    e.rethrowAsIOException();
-                }
-                bitmap = BitmapFactory.decodeFileDescriptor(fd, null, opts);
-            }
-
-            // Transform the bitmap if requested. We use a side-channel to
-            // communicate the orientation, since EXIF thumbnails don't contain
-            // the rotation flags of the original image.
-            final Bundle extras = afd.getExtras();
-            final int orientation = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
-            if (orientation != 0) {
-                final int width = bitmap.getWidth();
-                final int height = bitmap.getHeight();
-
-                final Matrix m = new Matrix();
-                m.setRotate(orientation, width / 2, height / 2);
-                bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
-            }
-        } finally {
-            IoUtils.closeQuietly(afd);
-        }
-
-        return bitmap;
+    public static Bitmap getDocumentThumbnail(ContentProviderClient client, Uri documentUri,
+            Point size, CancellationSignal signal) throws IOException {
+        return ContentResolver.loadThumbnail(client, documentUri, Point.convert(size), signal,
+                ImageDecoder.ALLOCATOR_DEFAULT);
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 828fd73..f5660b9 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -29,7 +29,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.UriPermission;
-import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.graphics.Bitmap;
@@ -39,6 +38,7 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Environment;
+import android.os.OperationCanceledException;
 import android.os.RemoteException;
 import android.service.media.CameraPrewarmService;
 import android.util.ArrayMap;
@@ -402,7 +402,16 @@
          * access.
          * <p>
          * Type: TEXT
+         *
+         * @deprecated Apps may not have filesystem permissions to directly
+         *             access this path. Instead of trying to open this path
+         *             directly, apps should use
+         *             {@link ContentResolver#openFileDescriptor(Uri, String)}
+         *             to gain access. This value will always be {@code NULL}
+         *             for apps targeting
+         *             {@link android.os.Build.VERSION_CODES#Q} or higher.
          */
+        @Deprecated
         public static final String DATA = "_data";
 
         /**
@@ -641,6 +650,7 @@
      * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
      * to be accessed elsewhere.
      */
+    @Deprecated
     private static class InternalThumbnails implements BaseColumns {
         /**
          * Currently outstanding thumbnail requests that can be cancelled.
@@ -654,13 +664,14 @@
          *
          * @see #cancelThumbnail(ContentResolver, Uri)
          */
+        @Deprecated
         static @Nullable Bitmap getThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri,
                 int kind, @Nullable BitmapFactory.Options opts) {
-            final Bundle openOpts = new Bundle();
+            final Point size;
             if (kind == ThumbnailConstants.MICRO_KIND) {
-                openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MICRO_SIZE);
+                size = ThumbnailConstants.MICRO_SIZE;
             } else if (kind == ThumbnailConstants.MINI_KIND) {
-                openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MINI_SIZE);
+                size = ThumbnailConstants.MINI_SIZE;
             } else {
                 throw new IllegalArgumentException("Unsupported kind: " + kind);
             }
@@ -674,9 +685,8 @@
                 }
             }
 
-            try (AssetFileDescriptor afd = cr.openTypedAssetFileDescriptor(uri,
-                    "image/*", openOpts, signal)) {
-                return BitmapFactory.decodeFileDescriptor(afd.getFileDescriptor(), null, opts);
+            try {
+                return cr.loadThumbnail(uri, Point.convert(size), signal);
             } catch (IOException e) {
                 Log.w(TAG, "Failed to obtain thumbnail for " + uri, e);
                 return null;
@@ -693,6 +703,7 @@
          * Only the original process which made the request can cancel their own
          * requests.
          */
+        @Deprecated
         static void cancelThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri) {
             synchronized (sPending) {
                 final CancellationSignal signal = sPending.get(uri);
@@ -936,9 +947,8 @@
         }
 
         /**
-         * This class allows developers to query and get two kinds of thumbnails:
-         * MINI_KIND: 512 x 384 thumbnail
-         * MICRO_KIND: 96 x 96 thumbnail
+         * This class provides utility methods to obtain thumbnails for various
+         * {@link Images} items.
          */
         public static class Thumbnails implements BaseColumns {
             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
@@ -958,13 +968,19 @@
             }
 
             /**
-             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
-             * interrupted and return immediately. Only the original process which made the getThumbnail
-             * requests can cancel their own requests.
+             * Cancel any outstanding {@link #getThumbnail} requests, causing
+             * them to return by throwing a {@link OperationCanceledException}.
+             * <p>
+             * This method has no effect on
+             * {@link ContentResolver#loadThumbnail} calls, since they provide
+             * their own {@link CancellationSignal}.
              *
-             * @param cr ContentResolver
-             * @param origId original image id
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
+            @Deprecated
             public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
                 final Uri uri = ContentUris.withAppendedId(
                         Images.Media.EXTERNAL_CONTENT_URI, origId);
@@ -972,51 +988,66 @@
             }
 
             /**
-             * This method checks if the thumbnails of the specified image (origId) has been created.
-             * It will be blocked until the thumbnails are generated.
+             * Return thumbnail representing a specific image item. If a
+             * thumbnail doesn't exist, this method will block until it's
+             * generated. Callers are responsible for their own in-memory
+             * caching of returned values.
              *
-             * @param cr ContentResolver used to dispatch queries to MediaProvider.
-             * @param origId Original image id associated with thumbnail of interest.
-             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
-             * @param options this is only used for MINI_KIND when decoding the Bitmap
-             * @return A Bitmap instance. It could be null if the original image
-             *         associated with origId doesn't exist or memory is not enough.
+             * @param imageId the image item to obtain a thumbnail for.
+             * @param kind optimal thumbnail size desired.
+             * @return decoded thumbnail, or {@code null} if problem was
+             *         encountered.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+            @Deprecated
+            public static Bitmap getThumbnail(ContentResolver cr, long imageId, int kind,
                     BitmapFactory.Options options) {
                 final Uri uri = ContentUris.withAppendedId(
-                        Images.Media.EXTERNAL_CONTENT_URI, origId);
+                        Images.Media.EXTERNAL_CONTENT_URI, imageId);
                 return InternalThumbnails.getThumbnail(cr, uri, kind, options);
             }
 
             /**
-             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
-             * interrupted and return immediately. Only the original process which made the getThumbnail
-             * requests can cancel their own requests.
+             * Cancel any outstanding {@link #getThumbnail} requests, causing
+             * them to return by throwing a {@link OperationCanceledException}.
+             * <p>
+             * This method has no effect on
+             * {@link ContentResolver#loadThumbnail} calls, since they provide
+             * their own {@link CancellationSignal}.
              *
-             * @param cr ContentResolver
-             * @param origId original image id
-             * @param groupId the same groupId used in getThumbnail.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
+            @Deprecated
+            public static void cancelThumbnailRequest(ContentResolver cr, long origId,
+                    long groupId) {
                 cancelThumbnailRequest(cr, origId);
             }
 
             /**
-             * This method checks if the thumbnails of the specified image (origId) has been created.
-             * It will be blocked until the thumbnails are generated.
+             * Return thumbnail representing a specific image item. If a
+             * thumbnail doesn't exist, this method will block until it's
+             * generated. Callers are responsible for their own in-memory
+             * caching of returned values.
              *
-             * @param cr ContentResolver used to dispatch queries to MediaProvider.
-             * @param origId Original image id associated with thumbnail of interest.
-             * @param groupId the id of group to which this request belongs
-             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
-             * @param options this is only used for MINI_KIND when decoding the Bitmap
-             * @return A Bitmap instance. It could be null if the original image
-             *         associated with origId doesn't exist or memory is not enough.
+             * @param imageId the image item to obtain a thumbnail for.
+             * @param kind optimal thumbnail size desired.
+             * @return decoded thumbnail, or {@code null} if problem was
+             *         encountered.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
+            @Deprecated
+            public static Bitmap getThumbnail(ContentResolver cr, long imageId, long groupId,
                     int kind, BitmapFactory.Options options) {
-                return getThumbnail(cr, origId, kind, options);
+                return getThumbnail(cr, imageId, kind, options);
             }
 
             /**
@@ -1059,7 +1090,16 @@
              * access.
              * <p>
              * Type: TEXT
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#loadThumbnail}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
+            @Deprecated
             public static final String DATA = "_data";
 
             /**
@@ -1509,7 +1549,16 @@
              * access.
              * <p>
              * Type: TEXT
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#openFileDescriptor(Uri, String)}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
+            @Deprecated
             public static final String DATA = "_data";
 
             /**
@@ -1790,7 +1839,16 @@
             /**
              * Cached album art.
              * <P>Type: TEXT</P>
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#loadThumbnail}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
+            @Deprecated
             public static final String ALBUM_ART = "album_art";
         }
 
@@ -2009,20 +2067,24 @@
         }
 
         /**
-         * This class allows developers to query and get two kinds of thumbnails:
-         * MINI_KIND: 512 x 384 thumbnail
-         * MICRO_KIND: 96 x 96 thumbnail
-         *
+         * This class provides utility methods to obtain thumbnails for various
+         * {@link Video} items.
          */
         public static class Thumbnails implements BaseColumns {
             /**
-             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
-             * interrupted and return immediately. Only the original process which made the getThumbnail
-             * requests can cancel their own requests.
+             * Cancel any outstanding {@link #getThumbnail} requests, causing
+             * them to return by throwing a {@link OperationCanceledException}.
+             * <p>
+             * This method has no effect on
+             * {@link ContentResolver#loadThumbnail} calls, since they provide
+             * their own {@link CancellationSignal}.
              *
-             * @param cr ContentResolver
-             * @param origId original video id
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
+            @Deprecated
             public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
                 final Uri uri = ContentUris.withAppendedId(
                         Video.Media.EXTERNAL_CONTENT_URI, origId);
@@ -2030,51 +2092,66 @@
             }
 
             /**
-             * This method checks if the thumbnails of the specified image (origId) has been created.
-             * It will be blocked until the thumbnails are generated.
+             * Return thumbnail representing a specific video item. If a
+             * thumbnail doesn't exist, this method will block until it's
+             * generated. Callers are responsible for their own in-memory
+             * caching of returned values.
              *
-             * @param cr ContentResolver used to dispatch queries to MediaProvider.
-             * @param origId Original image id associated with thumbnail of interest.
-             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
-             * @param options this is only used for MINI_KIND when decoding the Bitmap
-             * @return A Bitmap instance. It could be null if the original image
-             *         associated with origId doesn't exist or memory is not enough.
+             * @param videoId the video item to obtain a thumbnail for.
+             * @param kind optimal thumbnail size desired.
+             * @return decoded thumbnail, or {@code null} if problem was
+             *         encountered.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+            @Deprecated
+            public static Bitmap getThumbnail(ContentResolver cr, long videoId, int kind,
                     BitmapFactory.Options options) {
                 final Uri uri = ContentUris.withAppendedId(
-                        Video.Media.EXTERNAL_CONTENT_URI, origId);
+                        Video.Media.EXTERNAL_CONTENT_URI, videoId);
                 return InternalThumbnails.getThumbnail(cr, uri, kind, options);
             }
 
             /**
-             * This method checks if the thumbnails of the specified image (origId) has been created.
-             * It will be blocked until the thumbnails are generated.
+             * Cancel any outstanding {@link #getThumbnail} requests, causing
+             * them to return by throwing a {@link OperationCanceledException}.
+             * <p>
+             * This method has no effect on
+             * {@link ContentResolver#loadThumbnail} calls, since they provide
+             * their own {@link CancellationSignal}.
              *
-             * @param cr ContentResolver used to dispatch queries to MediaProvider.
-             * @param origId Original image id associated with thumbnail of interest.
-             * @param groupId the id of group to which this request belongs
-             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND
-             * @param options this is only used for MINI_KIND when decoding the Bitmap
-             * @return A Bitmap instance. It could be null if the original image associated with
-             *         origId doesn't exist or memory is not enough.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
-                    int kind, BitmapFactory.Options options) {
-                return getThumbnail(cr, origId, kind, options);
+            @Deprecated
+            public static void cancelThumbnailRequest(ContentResolver cr, long videoId,
+                    long groupId) {
+                cancelThumbnailRequest(cr, videoId);
             }
 
             /**
-             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
-             * interrupted and return immediately. Only the original process which made the getThumbnail
-             * requests can cancel their own requests.
+             * Return thumbnail representing a specific video item. If a
+             * thumbnail doesn't exist, this method will block until it's
+             * generated. Callers are responsible for their own in-memory
+             * caching of returned values.
              *
-             * @param cr ContentResolver
-             * @param origId original video id
-             * @param groupId the same groupId used in getThumbnail.
+             * @param videoId the video item to obtain a thumbnail for.
+             * @param kind optimal thumbnail size desired.
+             * @return decoded thumbnail, or {@code null} if problem was
+             *         encountered.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
-                cancelThumbnailRequest(cr, origId);
+            @Deprecated
+            public static Bitmap getThumbnail(ContentResolver cr, long videoId, long groupId,
+                    int kind, BitmapFactory.Options options) {
+                return getThumbnail(cr, videoId, kind, options);
             }
 
             /**
@@ -2110,14 +2187,17 @@
             /**
              * Path to the thumbnail file on disk.
              * <p>
-             * Note that apps may not have filesystem permissions to directly
-             * access this path. Instead of trying to open this path directly,
-             * apps should use
-             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
-             * access.
-             * <p>
              * Type: TEXT
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#openFileDescriptor(Uri, String)}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
+            @Deprecated
             public static final String DATA = "_data";
 
             /**
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 10d7911..ec63cd9 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -1112,7 +1112,6 @@
 
         @Override
         protected void playImpl() {
-            dispatchOnStart();
             super.playImpl();
             try {
               mFileOutputStream.close();
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 0a2d65c..f9370a8 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -505,7 +505,7 @@
             }
             mWholeWidth += width;
         } else {
-            builder.addReplacementRun(mCachedPaint, start, end, width);
+            builder.appendReplacementRun(mCachedPaint, end - start, width);
         }
     }
 
@@ -520,7 +520,7 @@
                         mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
                         mWidths.getRawArray(), start);
             } else {
-                builder.addStyleRun(mCachedPaint, start, end, false /* isRtl */);
+                builder.appendStyleRun(mCachedPaint, end - start, false /* isRtl */);
             }
         } else {
             // If there is multiple bidi levels, split into individual bidi level and apply style.
@@ -536,7 +536,7 @@
                                 mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
                                 isRtl, mWidths.getRawArray(), levelStart);
                     } else {
-                        builder.addStyleRun(mCachedPaint, levelStart, levelEnd, isRtl);
+                        builder.appendStyleRun(mCachedPaint, levelEnd - levelStart, isRtl);
                     }
                     if (levelEnd == end) {
                         break;
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index 648bd1b..f846a35 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -23,6 +23,7 @@
 import android.graphics.LeakyTypefaceStorage;
 import android.graphics.Typeface;
 import android.graphics.fonts.Font;
+import android.os.LocaleList;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
@@ -66,6 +67,7 @@
     private final Typeface mTypeface;
 
     private final int mTextFontWeight;
+    private final LocaleList mTextLocales;
 
     private final float mShadowRadius;
     private final float mShadowDx;
@@ -149,6 +151,19 @@
         mTextFontWeight = a.getInt(com.android.internal.R.styleable
                 .TextAppearance_textFontWeight, -1);
 
+        final String localeString = a.getString(com.android.internal.R.styleable
+                .TextAppearance_textLocale);
+        if (localeString != null) {
+            LocaleList localeList = LocaleList.forLanguageTags(localeString);
+            if (!localeList.isEmpty()) {
+                mTextLocales = localeList;
+            } else {
+                mTextLocales = null;
+            }
+        } else {
+            mTextLocales = null;
+        }
+
         mShadowRadius = a.getFloat(com.android.internal.R.styleable
                 .TextAppearance_shadowRadius, 0.0f);
         mShadowDx = a.getFloat(com.android.internal.R.styleable
@@ -201,6 +216,7 @@
         mTypeface = null;
 
         mTextFontWeight = -1;
+        mTextLocales = null;
 
         mShadowRadius = 0.0f;
         mShadowDx = 0.0f;
@@ -233,6 +249,7 @@
         mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
 
         mTextFontWeight = src.readInt();
+        mTextLocales = src.readParcelable(LocaleList.class.getClassLoader());
 
         mShadowRadius = src.readFloat();
         mShadowDx = src.readFloat();
@@ -285,6 +302,7 @@
         LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest);
 
         dest.writeInt(mTextFontWeight);
+        dest.writeParcelable(mTextLocales, flags);
 
         dest.writeFloat(mShadowRadius);
         dest.writeFloat(mShadowDx);
@@ -349,6 +367,15 @@
     }
 
     /**
+     * Returns the {@link android.os.LocaleList} specified by this span, or <code>null</code>
+     * if it does not specify one.
+     */
+    @Nullable
+    public LocaleList getTextLocales() {
+        return mTextLocales;
+    }
+
+    /**
      * Returns the typeface specified by this span, or <code>null</code>
      * if it does not specify one.
      */
@@ -487,6 +514,10 @@
             ds.setTextSize(mTextSize);
         }
 
+        if (mTextLocales != null) {
+            ds.setTextLocales(mTextLocales);
+        }
+
         if (mHasElegantTextHeight) {
             ds.setElegantTextHeight(mElegantTextHeight);
         }
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 4608e20..0e5252e 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -315,6 +315,7 @@
                     ArrayList<Transition> currentTransitions =
                             runningTransitions.get(mSceneRoot);
                     currentTransitions.remove(transition);
+                    transition.removeListener(this);
                 }
             });
             mTransition.captureValues(mSceneRoot, false);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a401c6d..e370e11 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -59,7 +59,10 @@
 import android.view.autofill.AutofillManager;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.inputmethod.UnbindReason;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.view.IInputConnectionWrapper;
 import com.android.internal.view.IInputContext;
@@ -67,7 +70,6 @@
 import com.android.internal.view.IInputMethodManager;
 import com.android.internal.view.IInputMethodSession;
 import com.android.internal.view.InputBindResult;
-import com.android.internal.view.InputMethodClient;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -540,17 +542,16 @@
                         mCurId = res.id;
                         mBindSequence = res.sequence;
                     }
-                    startInputInner(InputMethodClient.START_INPUT_REASON_BOUND_TO_IMMS,
-                            null, 0, 0, 0);
+                    startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
                     return;
                 }
                 case MSG_UNBIND: {
                     final int sequence = msg.arg1;
-                    @InputMethodClient.UnbindReason
+                    @UnbindReason
                     final int reason = msg.arg2;
                     if (DEBUG) {
                         Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence +
-                                " reason=" + InputMethodClient.getUnbindReason(reason));
+                                " reason=" + InputMethodDebug.unbindReasonToString(reason));
                     }
                     final boolean startInput;
                     synchronized (mH) {
@@ -567,8 +568,7 @@
                     }
                     if (startInput) {
                         startInputInner(
-                                InputMethodClient.START_INPUT_REASON_UNBOUND_FROM_IMMS, null, 0, 0,
-                                0);
+                                StartInputReason.UNBOUND_FROM_IMMS, null, 0, 0, 0);
                     }
                     return;
                 }
@@ -597,9 +597,8 @@
                         // handling this message.
                         if (mServedView != null && canStartInput(mServedView)) {
                             if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
-                                final int reason = active ?
-                                        InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
-                                        InputMethodClient.START_INPUT_REASON_DEACTIVATED_BY_IMMS;
+                                final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
+                                        : StartInputReason.DEACTIVATED_BY_IMMS;
                                 startInputInner(reason, null, 0, 0, 0);
                             }
                         }
@@ -696,7 +695,7 @@
         }
 
         @Override
-        public void onUnbindMethod(int sequence, @InputMethodClient.UnbindReason int unbindReason) {
+        public void onUnbindMethod(int sequence, @UnbindReason int unbindReason) {
             mH.obtainMessage(MSG_UNBIND, sequence, unbindReason).sendToTarget();
         }
 
@@ -1386,11 +1385,10 @@
             mServedConnecting = true;
         }
 
-        startInputInner(InputMethodClient.START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API, null, 0,
-                0, 0);
+        startInputInner(StartInputReason.APP_CALLED_RESTART_INPUT_API, null, 0, 0, 0);
     }
 
-    boolean startInputInner(@InputMethodClient.StartInputReason final int startInputReason,
+    boolean startInputInner(@StartInputReason int startInputReason,
             @Nullable IBinder windowGainingFocus, int controlFlags, int softInputMode,
             int windowFlags) {
         final View view;
@@ -1400,7 +1398,7 @@
             // Make sure we have a window token for the served view.
             if (DEBUG) {
                 Log.v(TAG, "Starting input: view=" + dumpViewInfo(view) +
-                        " reason=" + InputMethodClient.getStartInputReason(startInputReason));
+                        " reason=" + InputMethodDebug.startInputReasonToString(startInputReason));
             }
             if (view == null) {
                 if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
@@ -1519,7 +1517,7 @@
                 if (res == null) {
                     Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
                             + " null. startInputReason="
-                            + InputMethodClient.getStartInputReason(startInputReason)
+                            + InputMethodDebug.startInputReasonToString(startInputReason)
                             + " editorInfo=" + tba
                             + " controlFlags=#" + Integer.toHexString(controlFlags));
                     return false;
@@ -1654,7 +1652,7 @@
     @UnsupportedAppUsage
     public void checkFocus() {
         if (checkFocusNoStartInput(false)) {
-            startInputInner(InputMethodClient.START_INPUT_REASON_CHECK_FOCUS, null, 0, 0, 0);
+            startInputInner(StartInputReason.CHECK_FOCUS, null, 0, 0, 0);
         }
     }
 
@@ -1717,7 +1715,7 @@
         boolean forceNewFocus = false;
         synchronized (mH) {
             if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
-                    + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
+                    + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
                     + " first=" + first + " flags=#"
                     + Integer.toHexString(windowFlags));
             if (mRestartOnNextWindowFocus) {
@@ -1744,8 +1742,8 @@
             // should be done in conjunction with telling the system service
             // about the window gaining focus, to help make the transition
             // smooth.
-            if (startInputInner(InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN,
-                    rootView.getWindowToken(), controlFlags, softInputMode, windowFlags)) {
+            if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(),
+                    controlFlags, softInputMode, windowFlags)) {
                 return;
             }
         }
@@ -1756,7 +1754,7 @@
             try {
                 if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
                 mService.startInputOrWindowGainedFocus(
-                        InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
+                        StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
                         rootView.getWindowToken(), controlFlags, softInputMode, windowFlags, null,
                         null, 0 /* missingMethodFlags */,
                         rootView.getContext().getApplicationInfo().targetSdkVersion);
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 9692579..2e92f14 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.os.LocaleList;
 import android.os.Looper;
@@ -212,34 +211,13 @@
         return suggestSelection(request);
     }
 
-    // TODO: Remove once apps can build against the latest sdk.
-    /** @hide */
-    @UnsupportedAppUsage
-    default TextSelection suggestSelection(
-            @NonNull CharSequence text,
-            @IntRange(from = 0) int selectionStartIndex,
-            @IntRange(from = 0) int selectionEndIndex,
-            @Nullable TextSelection.Options options) {
-        if (options == null) {
-            return suggestSelection(new TextSelection.Request.Builder(
-                    text, selectionStartIndex, selectionEndIndex).build());
-        } else if (options.getRequest() != null) {
-            return suggestSelection(options.getRequest());
-        } else {
-            return suggestSelection(
-                    new TextSelection.Request.Builder(text, selectionStartIndex, selectionEndIndex)
-                            .setDefaultLocales(options.getDefaultLocales())
-                            .build());
-        }
-    }
-
     /**
      * Classifies the specified text and returns a {@link TextClassification} object that can be
      * used to generate a widget for handling the classified text.
      *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
      * @param request the text classification request
@@ -262,7 +240,7 @@
      * {@link #classifyText(TextClassification.Request)}. If that method calls this method,
      * a stack overflow error will happen.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
      * @param text text providing context for the text to classify (which is specified
@@ -292,34 +270,13 @@
         return classifyText(request);
     }
 
-    // TODO: Remove once apps can build against the latest sdk.
-    /** @hide */
-    @UnsupportedAppUsage
-    default TextClassification classifyText(
-            @NonNull CharSequence text,
-            @IntRange(from = 0) int startIndex,
-            @IntRange(from = 0) int endIndex,
-            @Nullable TextClassification.Options options) {
-        if (options == null) {
-            return classifyText(
-                    new TextClassification.Request.Builder(text, startIndex, endIndex).build());
-        } else if (options.getRequest() != null) {
-            return classifyText(options.getRequest());
-        } else {
-            return classifyText(new TextClassification.Request.Builder(text, startIndex, endIndex)
-                    .setDefaultLocales(options.getDefaultLocales())
-                    .setReferenceTime(options.getReferenceTime())
-                    .build());
-        }
-    }
-
     /**
      * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
      * links information.
      *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
      * @param request the text links request
@@ -334,27 +291,10 @@
         return new TextLinks.Builder(request.getText().toString()).build();
     }
 
-    // TODO: Remove once apps can build against the latest sdk.
-    /** @hide */
-    @UnsupportedAppUsage
-    default TextLinks generateLinks(
-            @NonNull CharSequence text, @Nullable TextLinks.Options options) {
-        if (options == null) {
-            return generateLinks(new TextLinks.Request.Builder(text).build());
-        } else if (options.getRequest() != null) {
-            return generateLinks(options.getRequest());
-        } else {
-            return generateLinks(new TextLinks.Request.Builder(text)
-                    .setDefaultLocales(options.getDefaultLocales())
-                    .setEntityConfig(options.getEntityConfig())
-                    .build());
-        }
-    }
-
     /**
      * Returns the maximal length of text that can be processed by generateLinks.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
      * @see #generateLinks(TextLinks.Request)
@@ -365,9 +305,29 @@
     }
 
     /**
+     * Detects the language of the specified text.
+     *
+     * <p><strong>NOTE: </strong>Call on a worker thread.
+     *
+     *
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
+     * @param request the {@link TextLanguage} request.
+     * @return the {@link TextLanguage} result.
+     */
+    @WorkerThread
+    @NonNull
+    default TextLanguage detectLanguage(@NonNull TextLanguage.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+        return TextLanguage.EMPTY;
+    }
+
+    /**
      * Reports a selection event.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      */
     default void onSelectionEvent(@NonNull SelectionEvent event) {}
@@ -375,7 +335,7 @@
     /**
      * Destroys this TextClassifier.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to its methods should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to its methods should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
      * <p>Subsequent calls to this method are no-ops.
@@ -385,7 +345,7 @@
     /**
      * Returns whether or not this TextClassifier has been destroyed.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, caller should not interact
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, caller should not interact
      * with the classifier and an attempt to do so would throw an {@link IllegalStateException}.
      * However, this method should never throw an {@link IllegalStateException}.
      *
@@ -396,9 +356,7 @@
     }
 
     /** @hide **/
-    default void dump(@NonNull IndentingPrintWriter printWriter) {
-
-    }
+    default void dump(@NonNull IndentingPrintWriter printWriter) {}
 
     /**
      * Configuration object for specifying what entities to identify.
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
new file mode 100644
index 0000000..d28459e
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextLanguage.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 android.view.textclassifier;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.icu.util.ULocale;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Represents the result of language detection of a piece of text.
+ * <p>
+ * This contains a list of locales, each paired with a confidence score, sorted in decreasing
+ * order of those scores. E.g., for a given input text, the model may return
+ * {@code [<"en", 0.85>, <"fr", 0.15>]}. This sample result means the model reports that it is
+ * 85% likely that the entire text is in English and 15% likely that the entire text is in French,
+ * etc. It does not mean that 85% of the input is in English and 15% is in French.
+ */
+public final class TextLanguage implements Parcelable {
+
+    public static final Creator<TextLanguage> CREATOR = new Creator<TextLanguage>() {
+        @Override
+        public TextLanguage createFromParcel(Parcel in) {
+            return readFromParcel(in);
+        }
+
+        @Override
+        public TextLanguage[] newArray(int size) {
+            return new TextLanguage[size];
+        }
+    };
+
+    static final TextLanguage EMPTY = new Builder().build();
+
+    @Nullable private final String mId;
+    private final EntityConfidence mEntityConfidence;
+    private final Bundle mBundle;
+
+    private TextLanguage(
+            @Nullable String id,
+            EntityConfidence entityConfidence,
+            Bundle bundle) {
+        mId = id;
+        mEntityConfidence = entityConfidence;
+        mBundle = bundle;
+    }
+
+    /**
+     * Returns the id, if one exists, for this object.
+     */
+    @Nullable
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the number of possible locales for the processed text.
+     */
+    @IntRange(from = 0)
+    public int getLocaleHypothesisCount() {
+        return mEntityConfidence.getEntities().size();
+    }
+
+    /**
+     * Returns the language locale at the specified index. Locales are ordered from high
+     * confidence to low confidence.
+     *
+     * @throws IndexOutOfBoundsException if the specified index is out of range.
+     * @see #getLocaleCount() for the number of locales available.
+     */
+    @NonNull
+    public ULocale getLocale(int index) {
+        return ULocale.forLanguageTag(mEntityConfidence.getEntities().get(index));
+    }
+
+    /**
+     * Returns the confidence score for the specified language locale. The value ranges from
+     * 0 (low confidence) to 1 (high confidence). 0 indicates that the locale was not found for
+     * the processed text.
+     */
+    @FloatRange(from = 0.0, to = 1.0)
+    public float getConfidenceScore(@NonNull ULocale locale) {
+        return mEntityConfidence.getConfidenceScore(locale.toLanguageTag());
+    }
+
+    /**
+     * Returns a bundle containing non-structured extra information about this result.
+     *
+     * <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 mBundle.deepCopy();
+    }
+
+    @Override
+    public String toString() {
+        return String.format(
+                Locale.US,
+                "TextLanguage {id=%s, locales=%s, bundle=%s}",
+                mId, mEntityConfidence, mBundle);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+        mEntityConfidence.writeToParcel(dest, flags);
+        dest.writeBundle(mBundle);
+    }
+
+    private static TextLanguage readFromParcel(Parcel in) {
+        return new TextLanguage(
+                in.readString(),
+                EntityConfidence.CREATOR.createFromParcel(in),
+                in.readBundle());
+    }
+
+    /**
+     * Builder used to build TextLanguage objects.
+     */
+    public static final class Builder {
+
+        @Nullable private String mId;
+        private final Map<String, Float> mEntityConfidenceMap = new ArrayMap<>();
+        @Nullable private Bundle mBundle;
+
+        /**
+         * Sets a language locale for the processed text and assigns a confidence score. If the
+         * locale has already been set, this updates it.
+         *
+         * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence).
+         *      0 implies the locale does not exist for the processed text.
+         *      Values greater than 1 are clamped to 1.
+         */
+        @NonNull
+        public Builder putLocale(
+                @NonNull ULocale locale,
+                @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
+            Preconditions.checkNotNull(locale);
+            mEntityConfidenceMap.put(locale.toLanguageTag(), confidenceScore);
+            return this;
+        }
+
+        /**
+         * Sets an optional id for the TextLanguage object.
+         */
+        @NonNull
+        public Builder setId(@Nullable String id) {
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Sets a bundle containing non-structured extra information about the TextLanguage object.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle bundle) {
+            mBundle = Preconditions.checkNotNull(bundle);
+            return this;
+        }
+
+        /**
+         * Builds and returns a new TextLanguage object.
+         * <p>
+         * If necessary, this method will verify fields, clamp them, and make them immutable.
+         */
+        @NonNull
+        public TextLanguage build() {
+            mBundle = mBundle == null ? new Bundle() : mBundle.deepCopy();
+            return new TextLanguage(
+                    mId,
+                    new EntityConfidence(mEntityConfidenceMap),
+                    mBundle);
+        }
+    }
+
+    /**
+     * A request object for detecting the language of a piece of text.
+     */
+    public static final class Request implements Parcelable {
+
+        public static final Creator<Request> CREATOR = new Creator<Request>() {
+            @Override
+            public Request createFromParcel(Parcel in) {
+                return readFromParcel(in);
+            }
+
+            @Override
+            public Request[] newArray(int size) {
+                return new Request[size];
+            }
+        };
+
+        private final CharSequence mText;
+        private final Bundle mBundle;
+
+        private Request(CharSequence text, Bundle bundle) {
+            mText = text;
+            mBundle = bundle;
+        }
+
+        /**
+         * Returns the text to process.
+         */
+        @NonNull
+        public CharSequence getText() {
+            return mText;
+        }
+
+        /**
+         * Returns a bundle containing non-structured extra information about this request.
+         *
+         * <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 mBundle.deepCopy();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeCharSequence(mText);
+            dest.writeBundle(mBundle);
+        }
+
+        private static Request readFromParcel(Parcel in) {
+            return new Request(
+                    in.readCharSequence(),
+                    in.readBundle());
+        }
+
+        /**
+         * A builder for building TextLanguage requests.
+         */
+        public static final class Builder {
+
+            private final CharSequence mText;
+            @Nullable private Bundle mBundle;
+
+            /**
+             * Creates a builder to build TextLanguage requests.
+             *
+             * @param text the text to process.
+             */
+            public Builder(@NonNull CharSequence text) {
+                mText = Preconditions.checkNotNull(text);
+            }
+
+            /**
+             * Sets a bundle containing non-structured extra information about the request.
+             */
+            @NonNull
+            public Builder setExtras(@NonNull Bundle bundle) {
+                mBundle = Preconditions.checkNotNull(bundle);
+                return this;
+            }
+
+            /**
+             * Builds and returns a new TextLanguage request object.
+             * <p>
+             * If necessary, this method will verify fields, clamp them, and make them immutable.
+             */
+            @NonNull
+            public Request build() {
+                mBundle = mBundle == null ? new Bundle() : mBundle.deepCopy();
+                return new Request(mText.toString(), mBundle);
+            }
+        }
+    }
+}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 69d7202..300bb6f 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -299,7 +299,7 @@
     /**
      * The resource was blocked because it may trick the user into a billing agreement.
      *
-     * <p>This constant is only used when targetSdkVersion is greater than {@link
+     * <p>This constant is only used when targetSdkVersion is at least {@link
      * android.os.Build.VERSION_CODES#Q}. Otherwise, {@link #SAFE_BROWSING_THREAT_UNKNOWN} is used
      * instead.
      */
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f74c234..3dd6fd1 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3517,6 +3517,7 @@
         ColorStateList mTextColorHint = null;
         ColorStateList mTextColorLink = null;
         int mTextSize = -1;
+        LocaleList mTextLocales = null;
         String mFontFamily = null;
         Typeface mFontTypeface = null;
         boolean mFontFamilyExplicit = false;
@@ -3543,6 +3544,7 @@
                     + "    mTextColorHint:" + mTextColorHint + "\n"
                     + "    mTextColorLink:" + mTextColorLink + "\n"
                     + "    mTextSize:" + mTextSize + "\n"
+                    + "    mTextLocales:" + mTextLocales + "\n"
                     + "    mFontFamily:" + mFontFamily + "\n"
                     + "    mFontTypeface:" + mFontTypeface + "\n"
                     + "    mFontFamilyExplicit:" + mFontFamilyExplicit + "\n"
@@ -3579,6 +3581,8 @@
                 com.android.internal.R.styleable.TextAppearance_textColorLink);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textSize,
                 com.android.internal.R.styleable.TextAppearance_textSize);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_textLocale,
+                com.android.internal.R.styleable.TextAppearance_textLocale);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_typeface,
                 com.android.internal.R.styleable.TextAppearance_typeface);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontFamily,
@@ -3652,6 +3656,15 @@
                     attributes.mTextSize =
                             appearance.getDimensionPixelSize(attr, attributes.mTextSize);
                     break;
+                case com.android.internal.R.styleable.TextAppearance_textLocale:
+                    final String localeString = appearance.getString(attr);
+                    if (localeString != null) {
+                        final LocaleList localeList = LocaleList.forLanguageTags(localeString);
+                        if (!localeList.isEmpty()) {
+                            attributes.mTextLocales = localeList;
+                        }
+                    }
+                    break;
                 case com.android.internal.R.styleable.TextAppearance_typeface:
                     attributes.mTypefaceIndex = appearance.getInt(attr, attributes.mTypefaceIndex);
                     if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
@@ -3738,6 +3751,10 @@
             setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
         }
 
+        if (attributes.mTextLocales != null) {
+            setTextLocales(attributes.mTextLocales);
+        }
+
         if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
             attributes.mFontFamily = null;
         }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
new file mode 100644
index 0000000..a5dc3d1
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -0,0 +1,158 @@
+/*
+ * 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.internal.inputmethod;
+
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+
+/**
+ * Provides useful methods for debugging.
+ */
+public final class InputMethodDebug {
+    /**
+     * Not intended to be instantiated.
+     */
+    private InputMethodDebug() {
+    }
+
+    /**
+     * Converts {@link StartInputReason} to {@link String} for debug logging.
+     *
+     * @param reason integer constant for {@link StartInputReason}.
+     * @return {@link String} message corresponds for the given {@code reason}.
+     */
+    public static String startInputReasonToString(@StartInputReason int reason) {
+        switch (reason) {
+            case StartInputReason.UNSPECIFIED:
+                return "UNSPECIFIED";
+            case StartInputReason.WINDOW_FOCUS_GAIN:
+                return "WINDOW_FOCUS_GAIN";
+            case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY:
+                return "WINDOW_FOCUS_GAIN_REPORT_ONLY";
+            case StartInputReason.APP_CALLED_RESTART_INPUT_API:
+                return "APP_CALLED_RESTART_INPUT_API";
+            case StartInputReason.CHECK_FOCUS:
+                return "CHECK_FOCUS";
+            case StartInputReason.BOUND_TO_IMMS:
+                return "BOUND_TO_IMMS";
+            case StartInputReason.UNBOUND_FROM_IMMS:
+                return "UNBOUND_FROM_IMMS";
+            case StartInputReason.ACTIVATED_BY_IMMS:
+                return "ACTIVATED_BY_IMMS";
+            case StartInputReason.DEACTIVATED_BY_IMMS:
+                return "DEACTIVATED_BY_IMMS";
+            case StartInputReason.SESSION_CREATED_BY_IME:
+                return "SESSION_CREATED_BY_IME";
+            default:
+                return "Unknown=" + reason;
+        }
+    }
+
+    /**
+     * Converts {@link UnbindReason} to {@link String} for debug logging.
+     *
+     * @param reason integer constant for {@link UnbindReason}.
+     * @return {@link String} message corresponds for the given {@code reason}.
+     */
+    public static String unbindReasonToString(@UnbindReason int reason) {
+        switch (reason) {
+            case UnbindReason.UNSPECIFIED:
+                return "UNSPECIFIED";
+            case UnbindReason.SWITCH_CLIENT:
+                return "SWITCH_CLIENT";
+            case UnbindReason.SWITCH_IME:
+                return "SWITCH_IME";
+            case UnbindReason.DISCONNECT_IME:
+                return "DISCONNECT_IME";
+            case UnbindReason.NO_IME:
+                return "NO_IME";
+            case UnbindReason.SWITCH_IME_FAILED:
+                return "SWITCH_IME_FAILED";
+            case UnbindReason.SWITCH_USER:
+                return "SWITCH_USER";
+            default:
+                return "Unknown=" + reason;
+        }
+    }
+
+    /**
+     * Converts {@link SoftInputModeFlags} to {@link String} for debug logging.
+     *
+     * @param softInputMode integer constant for {@link SoftInputModeFlags}.
+     * @return {@link String} message corresponds for the given {@code softInputMode}.
+     */
+    public static String softInputModeToString(@SoftInputModeFlags int softInputMode) {
+        final StringBuilder sb = new StringBuilder();
+        final int state = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+        final int adjust = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+        final boolean isForwardNav =
+                (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
+
+        switch (state) {
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
+                sb.append("STATE_UNSPECIFIED");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
+                sb.append("STATE_UNCHANGED");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
+                sb.append("STATE_HIDDEN");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
+                sb.append("STATE_ALWAYS_HIDDEN");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
+                sb.append("STATE_VISIBLE");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
+                sb.append("STATE_ALWAYS_VISIBLE");
+                break;
+            default:
+                sb.append("STATE_UNKNOWN(");
+                sb.append(state);
+                sb.append(")");
+                break;
+        }
+
+        switch (adjust) {
+            case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED:
+                sb.append("|ADJUST_UNSPECIFIED");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE:
+                sb.append("|ADJUST_RESIZE");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN:
+                sb.append("|ADJUST_PAN");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING:
+                sb.append("|ADJUST_NOTHING");
+                break;
+            default:
+                sb.append("|ADJUST_UNKNOWN(");
+                sb.append(adjust);
+                sb.append(")");
+                break;
+        }
+
+        if (isForwardNav) {
+            // This is a special bit that is set by the system only during the window navigation.
+            sb.append("|IS_FORWARD_NAVIGATION");
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/StartInputReason.java b/core/java/com/android/internal/inputmethod/StartInputReason.java
new file mode 100644
index 0000000..a01c459
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/StartInputReason.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Describes the reason why {@link android.view.inputmethod.InputMethodManager} is calling
+ * {@link com.android.internal.view.IInputMethodManager#startInputOrWindowGainedFocus}.
+ */
+@Retention(SOURCE)
+@IntDef(value = {
+        StartInputReason.UNSPECIFIED,
+        StartInputReason.WINDOW_FOCUS_GAIN,
+        StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY,
+        StartInputReason.APP_CALLED_RESTART_INPUT_API,
+        StartInputReason.CHECK_FOCUS,
+        StartInputReason.BOUND_TO_IMMS,
+        StartInputReason.UNBOUND_FROM_IMMS,
+        StartInputReason.ACTIVATED_BY_IMMS,
+        StartInputReason.DEACTIVATED_BY_IMMS,
+        StartInputReason.SESSION_CREATED_BY_IME})
+public @interface StartInputReason {
+    /**
+     * Reason is not specified.
+     */
+    int UNSPECIFIED = 0;
+    /**
+     * {@link android.view.Window} gained focus and it made the focused {@link android.view.View}
+     * to (re)start a new connection.
+     */
+    int WINDOW_FOCUS_GAIN = 1;
+    /**
+     * {@link android.view.Window} gained focus but there is no {@link android.view.View} that is
+     * eligible to have IME focus. {@link android.view.inputmethod.InputMethodManager} just reports
+     * this window focus change event.
+     */
+    int WINDOW_FOCUS_GAIN_REPORT_ONLY = 2;
+    /**
+     * {@link android.view.inputmethod.InputMethodManager#restartInput(android.view.View)} is
+     * either explicitly called by the application or indirectly called by some Framework class
+     * (e.g. {@link android.widget.EditText}).
+     */
+    int APP_CALLED_RESTART_INPUT_API = 3;
+    /**
+     * {@link android.view.View} requested a new connection because of view focus change.
+     */
+    int CHECK_FOCUS = 4;
+    /**
+     * {@link android.view.inputmethod.InputMethodManager} is responding to
+     * {@link com.android.internal.view.IInputMethodClient#onBindMethod}.
+     */
+    int BOUND_TO_IMMS = 5;
+    /**
+     * {@link android.view.inputmethod.InputMethodManager} is responding to
+     * {@link com.android.internal.view.IInputMethodClient#onUnbindMethod}.
+     */
+    int UNBOUND_FROM_IMMS = 6;
+    /**
+     * {@link android.view.inputmethod.InputMethodManager} is responding to
+     * {@link com.android.internal.view.IInputMethodClient#setActive}.
+     */
+    int ACTIVATED_BY_IMMS = 7;
+    /**
+     * {@link android.view.inputmethod.InputMethodManager} is responding to
+     * {@link com.android.internal.view.IInputMethodClient#setActive}.
+     */
+    int DEACTIVATED_BY_IMMS = 8;
+    /**
+     * {@link com.android.server.inputmethod.InputMethodManagerService} is responding to
+     * {@link com.android.internal.view.IInputSessionCallback#sessionCreated}.
+     */
+    int SESSION_CREATED_BY_IME = 9;
+}
diff --git a/core/java/com/android/internal/inputmethod/UnbindReason.java b/core/java/com/android/internal/inputmethod/UnbindReason.java
new file mode 100644
index 0000000..f0f18f1
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/UnbindReason.java
@@ -0,0 +1,69 @@
+/*
+ * 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.internal.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Describes the reason why {@link com.android.server.inputmethod.InputMethodManagerService} is
+ * calling {@link com.android.internal.view.IInputMethodClient#onUnbindMethod}.
+ */
+@Retention(SOURCE)
+@IntDef(value = {
+        UnbindReason.UNSPECIFIED,
+        UnbindReason.SWITCH_CLIENT,
+        UnbindReason.SWITCH_IME,
+        UnbindReason.DISCONNECT_IME,
+        UnbindReason.NO_IME,
+        UnbindReason.SWITCH_IME_FAILED,
+        UnbindReason.SWITCH_USER})
+public @interface UnbindReason {
+    /**
+     * Reason is not specified.
+     */
+    int UNSPECIFIED = 0;
+    /**
+     * When a new IME client becomes active, the previous IME client will unbound from the current
+     * IME.
+     */
+    int SWITCH_CLIENT = 1;
+    /**
+     * Before a new IME becomes active, the current IME client be unbound from the previous IME.
+     */
+    int SWITCH_IME = 2;
+    /**
+     * When the current IME is disconnected, the current IME client will be unbound.
+     */
+    int DISCONNECT_IME = 3;
+    /**
+     * When the system loses the last enabled IME, the current IME client will be unbound.
+     */
+    int NO_IME = 4;
+    /**
+     * When the system failed to switch to another IME, the current IME client will be unbound.
+     */
+    int SWITCH_IME_FAILED = 5;
+    /**
+     * When a new user becomes foreground, the previous IME client will be unbound from the previous
+     * user's active IME.
+     */
+    int SWITCH_USER = 6;
+}
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index 46667d1..2442d0b 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -158,7 +158,7 @@
                                          PROC_WAKELOCKS_FORMAT,
                         nameStringArray, wlData, null);
 
-                name = nameStringArray[0];
+                name = nameStringArray[0].trim();
                 count = (int) wlData[1];
 
                 if (wakeup_sources) {
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 2618356..17b2bc4 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -24,7 +24,6 @@
  */
 oneway interface IInputMethodClient {
     void onBindMethod(in InputBindResult res);
-    // unbindReason corresponds to InputMethodClient.UnbindReason.
     void onUnbindMethod(int sequence, int unbindReason);
     void setActive(boolean active, boolean fullscreen);
     void reportFullscreenMode(boolean fullscreen);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 34e8501..29c55c2 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -54,7 +54,7 @@
     // has gained focus, and if 'attribute' is non-null then also does startInput.
     // @NonNull
     InputBindResult startInputOrWindowGainedFocus(
-            /* @InputMethodClient.StartInputReason */ int startInputReason,
+            /* @StartInputReason */ int startInputReason,
             in IInputMethodClient client, in IBinder windowToken, int controlFlags,
             /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
             int windowFlags, in EditorInfo attribute, IInputContext inputContext,
diff --git a/core/java/com/android/internal/view/InputMethodClient.java b/core/java/com/android/internal/view/InputMethodClient.java
deleted file mode 100644
index bbd33a2..0000000
--- a/core/java/com/android/internal/view/InputMethodClient.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * 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 com.android.internal.view;
-
-import android.annotation.IntDef;
-import android.view.WindowManager.LayoutParams;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-
-import java.lang.annotation.Retention;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-public final class InputMethodClient {
-    public static final int START_INPUT_REASON_UNSPECIFIED = 0;
-    public static final int START_INPUT_REASON_WINDOW_FOCUS_GAIN = 1;
-    public static final int START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY = 2;
-    public static final int START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API = 3;
-    public static final int START_INPUT_REASON_CHECK_FOCUS = 4;
-    public static final int START_INPUT_REASON_BOUND_TO_IMMS = 5;
-    public static final int START_INPUT_REASON_UNBOUND_FROM_IMMS = 6;
-    public static final int START_INPUT_REASON_ACTIVATED_BY_IMMS = 7;
-    public static final int START_INPUT_REASON_DEACTIVATED_BY_IMMS = 8;
-    public static final int START_INPUT_REASON_SESSION_CREATED_BY_IME = 9;
-
-    @Retention(SOURCE)
-    @IntDef({START_INPUT_REASON_UNSPECIFIED, START_INPUT_REASON_WINDOW_FOCUS_GAIN,
-            START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY,
-            START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API, START_INPUT_REASON_CHECK_FOCUS,
-            START_INPUT_REASON_BOUND_TO_IMMS, START_INPUT_REASON_ACTIVATED_BY_IMMS,
-            START_INPUT_REASON_DEACTIVATED_BY_IMMS, START_INPUT_REASON_SESSION_CREATED_BY_IME})
-    public @interface StartInputReason {}
-
-    public static String getStartInputReason(@StartInputReason final int reason) {
-        switch (reason) {
-            case START_INPUT_REASON_UNSPECIFIED:
-                return "UNSPECIFIED";
-            case START_INPUT_REASON_WINDOW_FOCUS_GAIN:
-                return "WINDOW_FOCUS_GAIN";
-            case START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY:
-                return "WINDOW_FOCUS_GAIN_REPORT_ONLY";
-            case START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API:
-                return "APP_CALLED_RESTART_INPUT_API";
-            case START_INPUT_REASON_CHECK_FOCUS:
-                return "CHECK_FOCUS";
-            case START_INPUT_REASON_BOUND_TO_IMMS:
-                return "BOUND_TO_IMMS";
-            case START_INPUT_REASON_UNBOUND_FROM_IMMS:
-                return "UNBOUND_FROM_IMMS";
-            case START_INPUT_REASON_ACTIVATED_BY_IMMS:
-                return "ACTIVATED_BY_IMMS";
-            case START_INPUT_REASON_DEACTIVATED_BY_IMMS:
-                return "DEACTIVATED_BY_IMMS";
-            case START_INPUT_REASON_SESSION_CREATED_BY_IME:
-                return "SESSION_CREATED_BY_IME";
-            default:
-                return "Unknown=" + reason;
-        }
-    }
-
-    public static final int UNBIND_REASON_UNSPECIFIED = 0;
-    public static final int UNBIND_REASON_SWITCH_CLIENT = 1;
-    public static final int UNBIND_REASON_SWITCH_IME = 2;
-    public static final int UNBIND_REASON_DISCONNECT_IME = 3;
-    public static final int UNBIND_REASON_NO_IME = 4;
-    public static final int UNBIND_REASON_SWITCH_IME_FAILED = 5;
-    public static final int UNBIND_REASON_SWITCH_USER = 6;
-
-    @Retention(SOURCE)
-    @IntDef({UNBIND_REASON_UNSPECIFIED, UNBIND_REASON_SWITCH_CLIENT, UNBIND_REASON_SWITCH_IME,
-            UNBIND_REASON_DISCONNECT_IME, UNBIND_REASON_NO_IME, UNBIND_REASON_SWITCH_IME_FAILED,
-            UNBIND_REASON_SWITCH_USER})
-    public @interface UnbindReason {}
-
-    public static String getUnbindReason(@UnbindReason final int reason) {
-        switch (reason) {
-            case UNBIND_REASON_UNSPECIFIED:
-                return "UNSPECIFIED";
-            case UNBIND_REASON_SWITCH_CLIENT:
-                return "SWITCH_CLIENT";
-            case UNBIND_REASON_SWITCH_IME:
-                return "SWITCH_IME";
-            case UNBIND_REASON_DISCONNECT_IME:
-                return "DISCONNECT_IME";
-            case UNBIND_REASON_NO_IME:
-                return "NO_IME";
-            case UNBIND_REASON_SWITCH_IME_FAILED:
-                return "SWITCH_IME_FAILED";
-            case UNBIND_REASON_SWITCH_USER:
-                return "SWITCH_USER";
-            default:
-                return "Unknown=" + reason;
-        }
-    }
-
-    public static String softInputModeToString(@SoftInputModeFlags final int softInputMode) {
-        final StringBuilder sb = new StringBuilder();
-        final int state = softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE;
-        final int adjust = softInputMode & LayoutParams.SOFT_INPUT_MASK_ADJUST;
-        final boolean isForwardNav =
-                (softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
-
-        switch (state) {
-            case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
-                sb.append("STATE_UNSPECIFIED");
-                break;
-            case LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
-                sb.append("STATE_UNCHANGED");
-                break;
-            case LayoutParams.SOFT_INPUT_STATE_HIDDEN:
-                sb.append("STATE_HIDDEN");
-                break;
-            case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
-                sb.append("STATE_ALWAYS_HIDDEN");
-                break;
-            case LayoutParams.SOFT_INPUT_STATE_VISIBLE:
-                sb.append("STATE_VISIBLE");
-                break;
-            case LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
-                sb.append("STATE_ALWAYS_VISIBLE");
-                break;
-            default:
-                sb.append("STATE_UNKNOWN(");
-                sb.append(state);
-                sb.append(")");
-                break;
-        }
-
-        switch (adjust) {
-            case LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED:
-                sb.append("|ADJUST_UNSPECIFIED");
-                break;
-            case LayoutParams.SOFT_INPUT_ADJUST_RESIZE:
-                sb.append("|ADJUST_RESIZE");
-                break;
-            case LayoutParams.SOFT_INPUT_ADJUST_PAN:
-                sb.append("|ADJUST_PAN");
-                break;
-            case LayoutParams.SOFT_INPUT_ADJUST_NOTHING:
-                sb.append("|ADJUST_NOTHING");
-                break;
-            default:
-                sb.append("|ADJUST_UNKNOWN(");
-                sb.append(adjust);
-                sb.append(")");
-                break;
-        }
-
-        if (isForwardNav) {
-            // This is a special bit that is set by the system only during the window navigation.
-            sb.append("|IS_FORWARD_NAVIGATION");
-        }
-
-        return sb.toString();
-    }
-}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 5e62fd3..59c29e2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -45,6 +45,7 @@
         "android_app_NativeActivity.cpp",
         "android_app_admin_SecurityLog.cpp",
         "android_opengl_EGL14.cpp",
+        "android_opengl_EGL15.cpp",
         "android_opengl_EGLExt.cpp",
         "android_opengl_GLES10.cpp",
         "android_opengl_GLES10Ext.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c05bad2..eada690 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -77,6 +77,7 @@
 extern int register_com_google_android_gles_jni_EGLImpl(JNIEnv* env);
 extern int register_com_google_android_gles_jni_GLImpl(JNIEnv* env);
 extern int register_android_opengl_jni_EGL14(JNIEnv* env);
+extern int register_android_opengl_jni_EGL15(JNIEnv* env);
 extern int register_android_opengl_jni_EGLExt(JNIEnv* env);
 extern int register_android_opengl_jni_GLES10(JNIEnv* env);
 extern int register_android_opengl_jni_GLES10Ext(JNIEnv* env);
@@ -1367,6 +1368,7 @@
     REG_JNI(register_com_google_android_gles_jni_EGLImpl),
     REG_JNI(register_com_google_android_gles_jni_GLImpl),
     REG_JNI(register_android_opengl_jni_EGL14),
+    REG_JNI(register_android_opengl_jni_EGL15),
     REG_JNI(register_android_opengl_jni_EGLExt),
     REG_JNI(register_android_opengl_jni_GLES10),
     REG_JNI(register_android_opengl_jni_GLES10Ext),
diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp
new file mode 100644
index 0000000..4a30bab
--- /dev/null
+++ b/core/jni/android_opengl_EGL15.cpp
@@ -0,0 +1,557 @@
+/*
+** 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.
+*/
+
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/misc.h>
+
+#include <assert.h>
+#include <EGL/egl.h>
+
+#include <ui/ANativeObjectBase.h>
+
+static int initialized = 0;
+
+// classes from EGL 1.4
+static jclass egldisplayClass;
+static jclass eglsurfaceClass;
+static jclass eglconfigClass;
+static jclass eglcontextClass;
+static jclass bufferClass;
+static jclass nioAccessClass;
+
+static jfieldID positionID;
+static jfieldID limitID;
+static jfieldID elementSizeShiftID;
+
+static jmethodID getBasePointerID;
+static jmethodID getBaseArrayID;
+static jmethodID getBaseArrayOffsetID;
+
+static jmethodID egldisplayGetHandleID;
+static jmethodID eglconfigGetHandleID;
+static jmethodID eglcontextGetHandleID;
+static jmethodID eglsurfaceGetHandleID;
+
+static jmethodID egldisplayConstructor;
+static jmethodID eglcontextConstructor;
+static jmethodID eglsurfaceConstructor;
+static jmethodID eglconfigConstructor;
+
+static jobject eglNoContextObject;
+static jobject eglNoDisplayObject;
+static jobject eglNoSurfaceObject;
+
+// classes from EGL 1.5
+static jclass eglimageClass;
+static jclass eglsyncClass;
+
+static jmethodID eglimageGetHandleID;
+static jmethodID eglsyncGetHandleID;
+
+static jmethodID eglimageConstructor;
+static jmethodID eglsyncConstructor;
+
+static jobject eglNoImageObject;
+static jobject eglNoSyncObject;
+
+/* Cache method IDs each time the class is loaded. */
+
+static void
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
+{
+    // EGL 1.4 Init
+    jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig");
+    eglconfigClass = (jclass) _env->NewGlobalRef(eglconfigClassLocal);
+    jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext");
+    eglcontextClass = (jclass) _env->NewGlobalRef(eglcontextClassLocal);
+    jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay");
+    egldisplayClass = (jclass) _env->NewGlobalRef(egldisplayClassLocal);
+    jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface");
+    eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal);
+
+    eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J");
+    eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J");
+    egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
+    eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
+
+
+    eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V");
+    eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V");
+    egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V");
+    eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V");
+
+    jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor, reinterpret_cast<jlong>(EGL_NO_CONTEXT));
+    eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject);
+    jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor, reinterpret_cast<jlong>(EGL_NO_DISPLAY));
+    eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject);
+    jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, reinterpret_cast<jlong>(EGL_NO_SURFACE));
+    eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject);
+
+
+    jclass eglClass = _env->FindClass("android/opengl/EGL15");
+    jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;");
+    _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject);
+
+    jfieldID noDisplayFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;");
+    _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject);
+
+    jfieldID noSurfaceFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;");
+    _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject);
+
+    // EGL 1.5 init
+    jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
+    nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
+
+    jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
+    bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal);
+
+    getBasePointerID = _env->GetStaticMethodID(nioAccessClass,
+            "getBasePointer", "(Ljava/nio/Buffer;)J");
+    getBaseArrayID = _env->GetStaticMethodID(nioAccessClass,
+            "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
+    getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass,
+            "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
+
+    positionID = _env->GetFieldID(bufferClass, "position", "I");
+    limitID = _env->GetFieldID(bufferClass, "limit", "I");
+    elementSizeShiftID =
+        _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
+
+    jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage");
+    eglimageClass = (jclass) _env->NewGlobalRef(eglimageClassLocal);
+    jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync");
+    eglsyncClass = (jclass) _env->NewGlobalRef(eglsyncClassLocal);
+
+    eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+    eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+    eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V");
+    eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V");
+
+    jfieldID noImageFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;");
+    _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject);
+
+    jfieldID noSyncFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;");
+    _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject);
+}
+
+static void *
+getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *offset)
+{
+    jint position;
+    jint limit;
+    jint elementSizeShift;
+    jlong pointer;
+
+    position = _env->GetIntField(buffer, positionID);
+    limit = _env->GetIntField(buffer, limitID);
+    elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+    *remaining = (limit - position) << elementSizeShift;
+    pointer = _env->CallStaticLongMethod(nioAccessClass,
+            getBasePointerID, buffer);
+    if (pointer != 0L) {
+        *array = NULL;
+        return reinterpret_cast<void*>(pointer);
+    }
+    eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+    eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+    *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
+            getBaseArrayID, buffer);
+    *offset = _env->CallStaticIntMethod(nioAccessClass,
+            getBaseArrayOffsetID, buffer);
+
+    return NULL;
+}
+
+static void
+releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
+{
+    _env->ReleasePrimitiveArrayCritical(array, data,
+                       commit ? 0 : JNI_ABORT);
+}
+
+static void *
+fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) {
+    if (obj == NULL){
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                          "Object is set to null.");
+    }
+
+    jlong handle = _env->CallLongMethod(obj, mid);
+    return reinterpret_cast<void*>(handle);
+}
+
+static jobject
+toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void * handle) {
+    if (cls == eglimageClass &&
+       (EGLImage)handle == EGL_NO_IMAGE) {
+           return eglNoImageObject;
+    }
+
+    return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle));
+}
+
+// --------------------------------------------------------------------------
+/* EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreateSync
+  (JNIEnv *_env, jobject _this, jobject dpy, jint type, jlongArray attrib_list_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    EGLSync _returnValue = (EGLSync) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+    jint _remaining;
+    EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+    if (!attrib_list_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "attrib_list == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+    attrib_list_base = (EGLAttrib *)
+        _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+    attrib_list = attrib_list_base + offset;
+
+    _returnValue = eglCreateSync(
+        (EGLDisplay)dpy_native,
+        (EGLenum)type,
+        (EGLAttrib *)attrib_list
+    );
+
+exit:
+    if (attrib_list_base) {
+        _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+            JNI_ABORT);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return toEGLHandle(_env, eglsyncClass, eglsyncConstructor, _returnValue);
+}
+
+/* EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync ) */
+static jboolean
+android_eglDestroySync
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject sync) {
+    EGLBoolean _returnValue = (EGLBoolean) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+    _returnValue = eglDestroySync(
+        (EGLDisplay)dpy_native,
+        (EGLSync)sync_native
+    );
+    return (jboolean)_returnValue;
+}
+
+/* EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout ) */
+static jint
+android_eglClientWaitSync
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint flags, jlong timeout) {
+    EGLint _returnValue = (EGLint) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+    _returnValue = eglClientWaitSync(
+        (EGLDisplay)dpy_native,
+        (EGLSync)sync_native,
+        (EGLint)flags,
+        (EGLTime)timeout
+    );
+    return (jint)_returnValue;
+}
+
+/* EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value ) */
+static jboolean
+android_eglGetSyncAttrib
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint attribute, jlongArray value_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    EGLBoolean _returnValue = (EGLBoolean) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+    EGLAttrib *value_base = (EGLAttrib *) 0;
+    jint _remaining;
+    EGLAttrib *value = (EGLAttrib *) 0;
+
+    if (!value_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "value == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _remaining = _env->GetArrayLength(value_ref) - offset;
+    value_base = (EGLAttrib *)
+        _env->GetLongArrayElements(value_ref, (jboolean *)0);
+    value = value_base + offset;
+
+    _returnValue = eglGetSyncAttrib(
+        (EGLDisplay)dpy_native,
+        (EGLSync)sync_native,
+        (EGLint)attribute,
+        (EGLAttrib *)value
+    );
+
+exit:
+    if (value_base) {
+        _env->ReleaseLongArrayElements(value_ref, (jlong*)value_base,
+            _exception ? JNI_ABORT: 0);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return (jboolean)_returnValue;
+}
+
+/* EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglGetPlatformDisplay
+  (JNIEnv *_env, jobject _this, jint platform, jlong native_display, jlongArray attrib_list_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    EGLDisplay _returnValue = (EGLDisplay) 0;
+    EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+    jint _remaining;
+    EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+    if (!attrib_list_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "attrib_list == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+    attrib_list_base = (EGLAttrib *)
+        _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+    attrib_list = attrib_list_base + offset;
+
+    _returnValue = eglGetPlatformDisplay(
+        (EGLenum)platform,
+        (void *)native_display,
+        (EGLAttrib *)attrib_list
+    );
+
+exit:
+    if (attrib_list_base) {
+        _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+            JNI_ABORT);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue);
+}
+
+/* EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreatePlatformWindowSurface
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_window_buf, jlongArray attrib_list_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    jarray _array = (jarray) 0;
+    jint _bufferOffset = (jint) 0;
+    EGLSurface _returnValue = (EGLSurface) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLConfig config_native = (EGLConfig) fromEGLHandle(_env, eglconfigGetHandleID, config);
+    jint _native_windowRemaining;
+    void *native_window = (void *) 0;
+    EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+    jint _attrib_listRemaining;
+    EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+    if (!native_window_buf) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "native_window == null";
+        goto exit;
+    }
+    native_window = (void *)getPointer(_env, native_window_buf, (jarray*)&_array, &_native_windowRemaining, &_bufferOffset);
+    if (!attrib_list_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "attrib_list == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _attrib_listRemaining = _env->GetArrayLength(attrib_list_ref) - offset;
+    attrib_list_base = (EGLAttrib *)
+        _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+    attrib_list = attrib_list_base + offset;
+
+    if (native_window == NULL) {
+        char * _native_windowBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0);
+        native_window = (void *) (_native_windowBase + _bufferOffset);
+    }
+    _returnValue = eglCreatePlatformWindowSurface(
+        (EGLDisplay)dpy_native,
+        (EGLConfig)config_native,
+        (void *)native_window,
+        (EGLAttrib *)attrib_list
+    );
+
+exit:
+    if (attrib_list_base) {
+        _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+            JNI_ABORT);
+    }
+    if (_array) {
+        releasePointer(_env, _array, native_window, _exception ? JNI_FALSE : JNI_TRUE);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue);
+}
+
+/* EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreatePlatformPixmapSurface
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_pixmap_buf, jlongArray attrib_list_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    jarray _array = (jarray) 0;
+    jint _bufferOffset = (jint) 0;
+    EGLSurface _returnValue = (EGLSurface) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLConfig config_native = (EGLConfig) fromEGLHandle(_env, eglconfigGetHandleID, config);
+    jint _native_pixmapRemaining;
+    void *native_pixmap = (void *) 0;
+    EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+    jint _attrib_listRemaining;
+    EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+    if (!native_pixmap_buf) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "native_pixmap == null";
+        goto exit;
+    }
+    native_pixmap = (void *)getPointer(_env, native_pixmap_buf, (jarray*)&_array, &_native_pixmapRemaining, &_bufferOffset);
+    if (!attrib_list_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "attrib_list == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _attrib_listRemaining = _env->GetArrayLength(attrib_list_ref) - offset;
+    attrib_list_base = (EGLAttrib *)
+        _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+    attrib_list = attrib_list_base + offset;
+
+    if (native_pixmap == NULL) {
+        char * _native_pixmapBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0);
+        native_pixmap = (void *) (_native_pixmapBase + _bufferOffset);
+    }
+    _returnValue = eglCreatePlatformPixmapSurface(
+        (EGLDisplay)dpy_native,
+        (EGLConfig)config_native,
+        (void *)native_pixmap,
+        (EGLAttrib *)attrib_list
+    );
+
+exit:
+    if (attrib_list_base) {
+        _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+            JNI_ABORT);
+    }
+    if (_array) {
+        releasePointer(_env, _array, native_pixmap, _exception ? JNI_FALSE : JNI_TRUE);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue);
+}
+
+/* EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags ) */
+static jboolean
+android_eglWaitSync
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint flags) {
+    EGLBoolean _returnValue = (EGLBoolean) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+    _returnValue = eglWaitSync(
+        (EGLDisplay)dpy_native,
+        (EGLSync)sync_native,
+        (EGLint)flags
+    );
+    return (jboolean)_returnValue;
+}
+
+static const char *classPathName = "android/opengl/EGL15";
+
+static const JNINativeMethod methods[] = {
+{"_nativeClassInit", "()V", (void*)nativeClassInit },
+{"eglCreateSync", "(Landroid/opengl/EGLDisplay;I[JI)Landroid/opengl/EGLSync;", (void *) android_eglCreateSync },
+{"eglDestroySync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;)Z", (void *) android_eglDestroySync },
+{"eglClientWaitSync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;IJ)I", (void *) android_eglClientWaitSync },
+{"eglGetSyncAttrib", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;I[JI)Z", (void *) android_eglGetSyncAttrib },
+{"eglGetPlatformDisplay", "(IJ[JI)Landroid/opengl/EGLDisplay;", (void *) android_eglGetPlatformDisplay },
+{"eglCreatePlatformWindowSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/nio/Buffer;[JI)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePlatformWindowSurface },
+{"eglCreatePlatformPixmapSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/nio/Buffer;[JI)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePlatformPixmapSurface },
+{"eglWaitSync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;I)Z", (void *) android_eglWaitSync },
+};
+
+int register_android_opengl_jni_EGL15(JNIEnv *_env)
+{
+    int err;
+    err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));
+    return err;
+}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index cdb65ed..9bedab5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4551,6 +4551,11 @@
         <attr name="typeface" />
         <!-- Font family (named by string or as a font resource reference) for the text. -->
         <attr name="fontFamily" />
+        <!-- Specifies the {@link android.os.LocaleList} for the text.
+             May be a string value, which is a comma-separated language tag list, such as "ja-JP,zh-CN".
+             When not specified or an empty string is given, it will fallback to the default one.
+             {@see android.os.LocaleList#forLanguageTags(String)} -->
+        <attr name="textLocale" format="string" />
         <!-- Color of the text selection highlight. -->
         <attr name="textColorHighlight" />
         <!-- Color of the hint text. -->
@@ -4642,6 +4647,13 @@
         <attr name="textFontWeight" />
         <!-- Font family (named by string or as a font resource reference) for the text. -->
         <attr name="fontFamily" />
+        <!-- Specifies the {@link android.os.LocaleList} for the text in this TextView.
+             If not given, the system default will be used.
+             May be a string value, which is a comma-separated language tag list, such as "ja-JP,zh-CN".
+             When not specified or an empty string is given, it will fallback to the default one.
+             {@see android.os.LocaleList#forLanguageTags(String)}
+             {@see android.text.TextView#setTextLocales(android.os.LocaleList)} -->
+        <attr name="textLocale" format="string" />
         <!-- Text color for links. -->
         <attr name="textColorLink" />
         <!-- Makes the cursor visible (the default) or invisible. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9551718..31212a6 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2915,6 +2915,7 @@
         <public name="minimumUiTimeout" />
         <public name="isLightTheme" />
         <public name="isSplitRequired" />
+        <public name="textLocale" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 6256d08..0036186 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -13,31 +13,149 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content;
 
-import android.content.ContentResolver;
-import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
-@Suppress  // Failing.
-public class ContentResolverTest extends AndroidTestCase {
-    private ContentResolver mContentResolver;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ImageDecoder;
+import android.graphics.Paint;
+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;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContentResolver = mContext.getContentResolver();
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ContentResolverTest {
+
+    private ContentResolver mResolver;
+    private IContentProvider mProvider;
+    private ContentProviderClient mClient;
+
+    private int mSize = 256_000;
+    private MemoryFile mImage;
+
+    @Before
+    public void setUp() throws Exception {
+        mResolver = InstrumentationRegistry.getInstrumentation().getTargetContext()
+                .getContentResolver();
+        mProvider = mock(IContentProvider.class);
+        mClient = new ContentProviderClient(mResolver, mProvider, false);
+
+        mImage = new MemoryFile("temp.png", mSize);
     }
 
-    @LargeTest
-    public void testCursorFinalizer() throws Exception {
-        // TODO: Want a test case that more predictably reproduce this issue. Selected
-        // 600 as this causes the problem 100% of the runs on current hw, it might not
-        // do so on some other configuration though.
-        for (int i = 0; i < 600; i++) {
-            mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
-        }
+    @After
+    public void tearDown() throws Exception {
+        mImage.close();
+        mImage = null;
+    }
+
+    private void initImage(int width, int height) throws Exception {
+        final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(bitmap);
+
+        canvas.drawColor(Color.RED);
+
+        final Paint paint = new Paint();
+        paint.setColor(Color.BLUE);
+        paint.setStyle(Paint.Style.FILL);
+        canvas.drawRect(0, 0, width / 2, height / 2, paint);
+
+        bitmap.compress(Bitmap.CompressFormat.PNG, 90, mImage.getOutputStream());
+
+        final AssetFileDescriptor afd = new AssetFileDescriptor(
+                new ParcelFileDescriptor(mImage.getFileDescriptor()), 0, mSize, null);
+        when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any())).thenReturn(afd);
+    }
+
+    private static void assertImageAspectAndContents(Bitmap bitmap) {
+        // And correct aspect ratio
+        final int before = (100 * 1280) / 960;
+        final int after = (100 * bitmap.getWidth()) / bitmap.getHeight();
+        assertEquals(before, after);
+
+        // And scaled correctly
+        final int halfX = bitmap.getWidth() / 2;
+        final int halfY = bitmap.getHeight() / 2;
+        assertEquals(Color.BLUE, bitmap.getPixel(halfX - 10, halfY - 10));
+        assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY - 10));
+        assertEquals(Color.RED, bitmap.getPixel(halfX - 10, halfY + 10));
+        assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY + 10));
+    }
+
+    @Test
+    public void testLoadThumbnail_Normal() throws Exception {
+        initImage(1280, 960);
+
+        Bitmap res = ContentResolver.loadThumbnail(mClient,
+                Uri.parse("content://com.example/"), new Size(1280, 960), null,
+                ImageDecoder.ALLOCATOR_SOFTWARE);
+
+        // Size should be untouched
+        assertEquals(1280, res.getWidth());
+        assertEquals(960, res.getHeight());
+
+        assertImageAspectAndContents(res);
+    }
+
+    @Test
+    public void testLoadThumbnail_Scaling() throws Exception {
+        initImage(1280, 960);
+
+        Bitmap res = ContentResolver.loadThumbnail(mClient,
+                Uri.parse("content://com.example/"), new Size(320, 240), null,
+                ImageDecoder.ALLOCATOR_SOFTWARE);
+
+        // Size should be much smaller
+        assertTrue(res.getWidth() <= 640);
+        assertTrue(res.getHeight() <= 480);
+
+        assertImageAspectAndContents(res);
+    }
+
+    @Test
+    public void testLoadThumbnail_Aspect() throws Exception {
+        initImage(1280, 960);
+
+        Bitmap res = ContentResolver.loadThumbnail(mClient,
+                Uri.parse("content://com.example/"), new Size(240, 320), null,
+                ImageDecoder.ALLOCATOR_SOFTWARE);
+
+        // Size should be much smaller
+        assertTrue(res.getWidth() <= 640);
+        assertTrue(res.getHeight() <= 480);
+
+        assertImageAspectAndContents(res);
+    }
+
+    @Test
+    public void testLoadThumbnail_Tiny() throws Exception {
+        initImage(32, 24);
+
+        Bitmap res = ContentResolver.loadThumbnail(mClient,
+                Uri.parse("content://com.example/"), new Size(320, 240), null,
+                ImageDecoder.ALLOCATOR_SOFTWARE);
+
+        // Size should be untouched
+        assertEquals(32, res.getWidth());
+        assertEquals(24, res.getHeight());
+
+        assertImageAspectAndContents(res);
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
new file mode 100644
index 0000000..75ca769
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for TextLanguage.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class TextLanguageTest {
+
+    private static final float EPSILON = 0.000001f;
+
+    @Test
+    public void testParcel() throws Exception {
+        final String bundleKey = "experiment.int";
+        final Bundle bundle = new Bundle();
+        bundle.putInt(bundleKey, 1234);
+
+        final TextLanguage reference = new TextLanguage.Builder()
+                .setId("id")
+                .setExtras(bundle)
+                .putLocale(ULocale.ENGLISH, 0.8f)
+                .putLocale(ULocale.GERMAN, 0.2f)
+                .build();
+
+        final Parcel parcel = Parcel.obtain();
+        reference.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        final TextLanguage result = TextLanguage.CREATOR.createFromParcel(parcel);
+
+        assertEquals("id", result.getId());
+        assertEquals(1234, result.getExtras().getInt(bundleKey));
+        assertEquals(2, result.getLocaleHypothesisCount());
+        assertEquals(ULocale.ENGLISH, result.getLocale(0));
+        assertEquals(0.8f, result.getConfidenceScore(ULocale.ENGLISH), EPSILON);
+        assertEquals(ULocale.GERMAN, result.getLocale(1));
+        assertEquals(0.2f, result.getConfidenceScore(ULocale.GERMAN), EPSILON);
+    }
+
+    @Test
+    public void testRequestParcel() throws Exception {
+        final String text = "This is random text";
+        final String bundleKey = "experiment.str";
+        final Bundle bundle = new Bundle();
+        bundle.putString(bundleKey, "bundle");
+
+        final TextLanguage.Request reference = new TextLanguage.Request.Builder(text)
+                .setExtras(bundle)
+                .build();
+
+        final Parcel parcel = Parcel.obtain();
+        reference.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        final TextLanguage.Request result = TextLanguage.Request.CREATOR.createFromParcel(parcel);
+
+        assertEquals(text, result.getText());
+        assertEquals("bundle", result.getExtras().getString(bundleKey));
+    }
+}
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index f291e27..ea93501 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Size;
 import android.util.proto.ProtoOutputStream;
 
 import java.io.PrintWriter;
@@ -168,4 +169,14 @@
         x = in.readInt();
         y = in.readInt();
     }
+
+    /** {@hide} */
+    public static @NonNull Point convert(@NonNull Size size) {
+        return new Point(size.getWidth(), size.getHeight());
+    }
+
+    /** {@hide} */
+    public static @NonNull Size convert(@NonNull Point point) {
+        return new Size(point.x, point.y);
+    }
 }
diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
index 8d38f96..1647909 100644
--- a/graphics/java/android/graphics/text/LineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -38,11 +38,14 @@
  * <pre>
  * <code>
  * Paint paint = new Paint();
+ * Paint bigPaint = new Paint();
+ * bigPaint.setTextSize(paint.getTextSize() * 2.0);
  * String text = "Hello, Android.";
  *
  * // Prepare the measured text
  * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
- *     .addStyleRun(paint, 0, text.length(), false)  // Use paint for whole paragraph.
+ *     .appendStyleRun(paint, 7, false)  // Use paint for "Hello, "
+ *     .appednStyleRun(bigPaint, 8, false)  // Use bigPaint for "Hello, "
  *     .build();
  *
  * LineBreaker lb = new LineBreaker.Builder()
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 852e66e..3efe655 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -31,6 +31,21 @@
 
 /**
  * Result of text shaping of the single paragraph string.
+ *
+ * <p>
+ * <pre>
+ * <code>
+ * Paint paint = new Paint();
+ * Paint bigPaint = new Paint();
+ * bigPaint.setTextSize(paint.getTextSize() * 2.0);
+ * String text = "Hello, Android.";
+ * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+ *      .appendStyleRun(paint, 7, false)  // Use paint for "Hello, "
+ *      .appendStyleRun(bigPaint, 8, false)  // Use bigPaint for "Hello, "
+ *      .build();
+ * </code>
+ * </pre>
+ * </p>
  */
 public class MeasuredText {
     private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
@@ -136,6 +151,19 @@
 
     /**
      * Helper class for creating a {@link MeasuredText}.
+     * <p>
+     * <pre>
+     * <code>
+     * Paint paint = new Paint();
+     * String text = "Hello, Android.";
+     * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+     *      .appendStyleRun(paint, text.length, false)
+     *      .build();
+     * </code>
+     * </pre>
+     * </p>
+     *
+     * Note: The appendStyle and appendReplacementRun should be called to cover the text length.
      */
     public static class Builder {
         private long mNativePtr;
@@ -143,6 +171,7 @@
         private final @NonNull char[] mText;
         private boolean mComputeHyphenation = false;
         private boolean mComputeLayout = true;
+        private int mCurrentOffset = 0;
 
         /**
          * Construct a builder.
@@ -159,35 +188,50 @@
         }
 
         /**
-         * Apply styles to the given range.
+         * Apply styles to the given length.
+         *
+         * Keeps an internal offset which increases at every append. The initial value for this
+         * offset is zero. After the style is applied the internal offset is moved to {@code offset
+         * + length}, and next call will start from this new position.
          *
          * @param paint a paint
-         * @param start an inclusive start index of the range
-         * @param end an exclusive end index of the range
+         * @param length a length to be applied with a given paint, can not exceed the length of the
+         *               text
          * @param isRtl true if the text is in RTL context, otherwise false.
          */
-        public Builder addStyleRun(@NonNull Paint paint,
-                @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl) {
+        public Builder appendStyleRun(@NonNull Paint paint, @IntRange(from = 0) int length,
+                boolean isRtl) {
             Preconditions.checkNotNull(paint);
-            nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl);
+            Preconditions.checkArgument(length > 0, "length can not be negative");
+            final int end = mCurrentOffset + length;
+            Preconditions.checkArgument(end <= mText.length, "Style exceeds the text length");
+            nAddStyleRun(mNativePtr, paint.getNativeInstance(), mCurrentOffset, end, isRtl);
+            mCurrentOffset = end;
             return this;
         }
 
         /**
-         * Used to inform the text layout that the given range is replaced with the object of given
+         * Used to inform the text layout that the given length is replaced with the object of given
          * width.
          *
-         * Informs the layout engine that the given range should not be processed, instead the
+         * Keeps an internal offset which increases at every append. The initial value for this
+         * offset is zero. After the style is applied the internal offset is moved to {@code offset
+         * + length}, and next call will start from this new position.
+         *
+         * Informs the layout engine that the given length should not be processed, instead the
          * provided width should be used for calculating the width of that range.
          *
-         * @param start an inclusive start index of the range
-         * @param end an exclusive end index of the range
+         * @param length a length to be replaced with the object, can not exceed the length of the
+         *               text
          * @param width a replacement width of the range
          */
-        public Builder addReplacementRun(@NonNull Paint paint,
-                @IntRange(from = 0) int start, @IntRange(from = 0) int end,
-                @FloatRange(from = 0) float width) {
-            nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width);
+        public Builder appendReplacementRun(@NonNull Paint paint,
+                @IntRange(from = 0) int length, @FloatRange(from = 0) float width) {
+            Preconditions.checkArgument(length > 0, "length can not be negative");
+            final int end = mCurrentOffset + length;
+            Preconditions.checkArgument(end <= mText.length, "Replacement exceeds the text length");
+            nAddReplacementRun(mNativePtr, paint.getNativeInstance(), mCurrentOffset, end, width);
+            mCurrentOffset = end;
             return this;
         }
 
@@ -230,9 +274,14 @@
          *
          * Once you called build() method, you can't reuse the Builder class again.
          * @throws IllegalStateException if this Builder is reused.
+         * @throws IllegalStateException if the whole text is not covered by one or more runs (style
+         *                               or replacement)
          */
         public MeasuredText build() {
             ensureNativePtrNoReuse();
+            if (mCurrentOffset != mText.length) {
+                throw new IllegalStateException("Style info has not been provided for all text.");
+            }
             try {
                 long ptr = nBuildMeasuredText(mNativePtr, mText, mComputeHyphenation,
                         mComputeLayout);
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index b04194f..753557c 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -224,6 +224,7 @@
             break;
         case PixelStorageType::Heap:
             free(mPixelStorage.heap.address);
+            mallopt(M_PURGE, 0);
             break;
         case PixelStorageType::Hardware:
             auto buffer = mPixelStorage.hardware.buffer;
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 0022c93..c6e4c154b 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -141,7 +141,7 @@
     // select only flags that might affect text layout
     flags &= (SkPaint::kAntiAlias_Flag | SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag |
               SkPaint::kSubpixelText_Flag | SkPaint::kEmbeddedBitmapText_Flag |
-              SkPaint::kAutoHinting_Flag | SkPaint::kVerticalText_Flag);
+              SkPaint::kAutoHinting_Flag);
     flags |= (hinting << 16);
     return flags;
 }
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 6d122d7..ee12b91 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -1192,15 +1192,17 @@
     public static final int SECURITY_LEVEL_HW_SECURE_ALL = 5;
 
     /**
-     * The maximum security level supported by the device. This is the default
-     * security level when a session is opened.
+     * Indicates that the maximum security level supported by the device should
+     * be used when opening a session. This is the default security level
+     * selected when a session is opened.
      * @hide
      */
     public static final int SECURITY_LEVEL_MAX = 6;
 
     /**
-     * The maximum security level supported by the device. This is the default
-     * security level when a session is opened.
+     * Returns a value that may be passed as a parameter to {@link #openSession(int)}
+     * requesting that the session be opened at the maximum security level of
+     * the device.
      */
     public static final int getMaxSecurityLevel() {
         return SECURITY_LEVEL_MAX;
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index e67b100..474b671 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -538,8 +538,13 @@
         MtpPropertyGroup propertyGroup;
         for (MtpStorageManager.MtpObject obj : objs) {
             if (property == 0xffffffff) {
+                if (format == 0 && handle != 0 && handle != 0xffffffff) {
+                    // return properties based on the object's format
+                    format = obj.getFormat();
+                }
                 // Get all properties supported by this object
-                propertyGroup = mPropertyGroupsByFormat.get(obj.getFormat());
+                // format should be the same between get & put
+                propertyGroup = mPropertyGroupsByFormat.get(format);
                 if (propertyGroup == null) {
                     int[] propertyList = getSupportedObjectProperties(format);
                     propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName,
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 3870124..fa9ab1f7 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -128,6 +128,7 @@
         "libmediametrics",
         "libmediaplayer2",
         "libmediaplayer2-protos",
+        "libmediandk_utils",
         "libmediautils",
         "libnetd_client",  // for setNetworkForUser
         "libprotobuf-cpp-lite",
diff --git a/opengl/java/android/opengl/EGL15.java b/opengl/java/android/opengl/EGL15.java
new file mode 100644
index 0000000..9aae6ad
--- /dev/null
+++ b/opengl/java/android/opengl/EGL15.java
@@ -0,0 +1,149 @@
+/*
+** 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.opengl;
+
+/**
+ * EGL 1.5
+ *
+ */
+public class EGL15 {
+
+    public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT            = 0x00000001;
+    public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT   = 0x00000002;
+    public static final int EGL_OPENGL_ES3_BIT                             = 0x00000040;
+    public static final int EGL_SYNC_FLUSH_COMMANDS_BIT                    = 0x0001;
+    public static final int EGL_GL_COLORSPACE_SRGB                         = 0x3089;
+    public static final int EGL_GL_COLORSPACE_LINEAR                       = 0x308A;
+    public static final int EGL_CONTEXT_MAJOR_VERSION                      = 0x3098;
+    public static final int EGL_CL_EVENT_HANDLE                            = 0x309C;
+    public static final int EGL_GL_COLORSPACE                              = 0x309D;
+    public static final int EGL_GL_TEXTURE_2D                              = 0x30B1;
+    public static final int EGL_GL_TEXTURE_3D                              = 0x30B2;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X             = 0x30B3;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X             = 0x30B4;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y             = 0x30B5;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y             = 0x30B6;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z             = 0x30B7;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z             = 0x30B8;
+    public static final int EGL_GL_RENDERBUFFER                            = 0x30B9;
+    public static final int EGL_GL_TEXTURE_LEVEL                           = 0x30BC;
+    public static final int EGL_GL_TEXTURE_ZOFFSET                         = 0x30BD;
+    public static final int EGL_IMAGE_PRESERVED                            = 0x30D2;
+    public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE               = 0x30F0;
+    public static final int EGL_SYNC_STATUS                                = 0x30F1;
+    public static final int EGL_SIGNALED                                   = 0x30F2;
+    public static final int EGL_UNSIGNALED                                 = 0x30F3;
+    public static final int EGL_TIMEOUT_EXPIRED                            = 0x30F5;
+    public static final int EGL_CONDITION_SATISFIED                        = 0x30F6;
+    public static final int EGL_SYNC_TYPE                                  = 0x30F7;
+    public static final int EGL_SYNC_CONDITION                             = 0x30F8;
+    public static final int EGL_SYNC_FENCE                                 = 0x30F9;
+    public static final int EGL_CONTEXT_MINOR_VERSION                      = 0x30FB;
+    public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK                = 0x30FD;
+    public static final int EGL_SYNC_CL_EVENT                              = 0x30FE;
+    public static final int EGL_SYNC_CL_EVENT_COMPLETE                     = 0x30FF;
+    public static final int EGL_CONTEXT_OPENGL_DEBUG                       = 0x31B0;
+    public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE          = 0x31B1;
+    public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS               = 0x31B2;
+    public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 0x31BD;
+    public static final int EGL_NO_RESET_NOTIFICATION                      = 0x31BE;
+    public static final int EGL_LOSE_CONTEXT_ON_RESET                      = 0x31BF;
+    public static final int EGL_PLATFORM_ANDROID_KHR                       = 0x3141;
+    public static final long EGL_FOREVER                                   = 0xFFFFFFFFFFFFFFFFL;
+    public static final EGLImage EGL_NO_IMAGE                              = null;
+    public static final EGLSync EGL_NO_SYNC                                = null;
+    public static final EGLContext EGL_NO_CONTEXT                          = null;
+    public static final EGLDisplay EGL_NO_DISPLAY                          = null;
+    public static final EGLSurface EGL_NO_SURFACE                          = null;
+
+    native private static void _nativeClassInit();
+    static {
+        _nativeClassInit();
+    }
+    // C function EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list )
+
+    public static native EGLSync eglCreateSync(
+        EGLDisplay dpy,
+        int type,
+        long[] attrib_list,
+        int offset
+    );
+
+    // C function EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync )
+
+    public static native boolean eglDestroySync(
+        EGLDisplay dpy,
+        EGLSync sync
+    );
+
+    // C function EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout )
+
+    public static native int eglClientWaitSync(
+        EGLDisplay dpy,
+        EGLSync sync,
+        int flags,
+        long timeout
+    );
+
+    // C function EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value )
+
+    public static native boolean eglGetSyncAttrib(
+        EGLDisplay dpy,
+        EGLSync sync,
+        int attribute,
+        long[] value,
+        int offset
+    );
+
+    // C function EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+
+    public static native EGLDisplay eglGetPlatformDisplay(
+        int platform,
+        long native_display,
+        long[] attrib_list,
+        int offset
+    );
+
+    // C function EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list )
+
+    public static native EGLSurface eglCreatePlatformWindowSurface(
+        EGLDisplay dpy,
+        EGLConfig config,
+        java.nio.Buffer native_window,
+        long[] attrib_list,
+        int offset
+    );
+
+    // C function EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list )
+
+    public static native EGLSurface eglCreatePlatformPixmapSurface(
+        EGLDisplay dpy,
+        EGLConfig config,
+        java.nio.Buffer native_pixmap,
+        long[] attrib_list,
+        int offset
+    );
+
+    // C function EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags )
+
+    public static native boolean eglWaitSync(
+        EGLDisplay dpy,
+        EGLSync sync,
+        int flags
+    );
+
+}
diff --git a/opengl/java/android/opengl/EGLImage.java b/opengl/java/android/opengl/EGLImage.java
new file mode 100644
index 0000000..731ce72
--- /dev/null
+++ b/opengl/java/android/opengl/EGLImage.java
@@ -0,0 +1,37 @@
+/*
+**
+** 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.opengl;
+
+/**
+ * Wrapper class for native EGLImage objects.
+ *
+ */
+public class EGLImage extends EGLObjectHandle {
+    private EGLImage(long handle) {
+        super(handle);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EGLImage)) return false;
+
+        EGLImage that = (EGLImage) o;
+        return getNativeHandle() == that.getNativeHandle();
+    }
+}
diff --git a/opengl/java/android/opengl/EGLSync.java b/opengl/java/android/opengl/EGLSync.java
new file mode 100644
index 0000000..472f9e7
--- /dev/null
+++ b/opengl/java/android/opengl/EGLSync.java
@@ -0,0 +1,37 @@
+/*
+**
+** 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.opengl;
+
+/**
+ * Wrapper class for native EGLSync objects.
+ *
+ */
+public class EGLSync extends EGLObjectHandle {
+    private EGLSync(long handle) {
+        super(handle);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EGLSync)) return false;
+
+        EGLSync that = (EGLSync) o;
+        return getNativeHandle() == that.getNativeHandle();
+    }
+}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index 33cb5964..4518d79 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -30,7 +30,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.carrierdefaultapp.R;
+
 /**
  * This util class provides common logic for carrier actions
  */
@@ -102,7 +102,7 @@
                 SubscriptionManager.getDefaultVoiceSubscriptionId());
         logd("onDisableAllMeteredApns subId: " + subId);
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
-        telephonyMgr.carrierActionSetMeteredApnsEnabled(subId, !ENABLE);
+        telephonyMgr.createForSubscriptionId(subId).setCarrierDataEnabled(!ENABLE);
     }
 
     private static void onEnableAllMeteredApns(Intent intent, Context context) {
@@ -110,7 +110,7 @@
                 SubscriptionManager.getDefaultVoiceSubscriptionId());
         logd("onEnableAllMeteredApns subId: " + subId);
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
-        telephonyMgr.carrierActionSetMeteredApnsEnabled(subId, ENABLE);
+        telephonyMgr.createForSubscriptionId(subId).setCarrierDataEnabled(ENABLE);
     }
 
     private static void onEnableDefaultURLHandler(Context context) {
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
index f9dbcd4..5d84d64 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
@@ -104,6 +104,6 @@
         assertNotNull(pendingIntent);
 
         Rlog.d(TAG, "verify carrier action: disable all metered apns");
-        verify(mTelephonyMgr).carrierActionSetMeteredApnsEnabled(eq(subId), eq(false));
+        verify(mTelephonyMgr).setCarrierDataEnabled(eq(false));
     }
 }
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 3333e15..8f33a70 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -25,7 +25,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread;
-import android.app.AlarmManager;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -224,8 +223,9 @@
     }
 
     /** A convenience helper for creating an adjustment for an SBN. */
+    @VisibleForTesting
     @Nullable
-    private Adjustment createEnqueuedNotificationAdjustment(
+    Adjustment createEnqueuedNotificationAdjustment(
             @NonNull NotificationEntry entry,
             @NonNull ArrayList<Notification.Action> smartActions,
             @NonNull ArrayList<CharSequence> smartReplies) {
@@ -237,7 +237,9 @@
             signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
         }
         if (mNotificationCategorizer.shouldSilence(entry)) {
-            signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
+            final int importance = entry.getImportance() < IMPORTANCE_LOW ? entry.getImportance()
+                    : IMPORTANCE_LOW;
+            signals.putInt(KEY_IMPORTANCE, importance);
         }
 
         return new Adjustment(
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 2820232..2eb005a 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -66,6 +66,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.FileOutputStream;
+import java.util.ArrayList;
 
 public class AssistantTest extends ServiceTestCase<Assistant> {
 
@@ -466,4 +467,12 @@
 
         assertFalse(mAssistant.mLiveNotifications.containsKey(sbn.getKey()));
     }
+
+    @Test
+    public void testAssistantNeverIncreasesImportanceWhenSuggestingSilent() throws Exception {
+        StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "min notif!", null);
+        Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(new NotificationEntry(
+                mPackageManager, sbn, P1C3), new ArrayList<>(), new ArrayList<>());
+        assertEquals(IMPORTANCE_MIN, adjust.getSignals().getInt(Adjustment.KEY_IMPORTANCE));
+    }
 }
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2fbf42f..6378309 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -137,15 +137,9 @@
     <integer name="quick_settings_brightness_dialog_short_timeout">2000</integer>
     <integer name="quick_settings_brightness_dialog_long_timeout">4000</integer>
 
-    <!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
-    <bool name="config_show4GForLTE">true</bool>
-
     <!-- Show indicator for Wifi on but not connected. -->
     <bool name="config_showWifiIndicatorWhenEnabled">false</bool>
 
-    <!-- Should "LTE"/"4G" be shown instead of "LTE+"/"4G+" when on NETWORK_TYPE_LTE_CA? -->
-    <bool name="config_hideLtePlus">false</bool>
-
     <!-- The number of milliseconds before the heads up notification auto-dismisses. -->
     <integer name="heads_up_notification_decay">5000</integer>
 
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 0215fda..3fe9944 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -87,6 +87,8 @@
     private Runnable mWatchLongPress;
     private final long mLongPressTimeout;
 
+    protected boolean mSwipingInProgress;
+
     final private int[] mTmpPos = new int[2];
     private final int mFalsingThreshold;
     private boolean mTouchAboveFalsingThreshold;
@@ -127,6 +129,10 @@
         mDisableHwLayers = disableHwLayers;
     }
 
+    public boolean isSwipingInProgress() {
+        return mSwipingInProgress;
+    }
+
     private float getPos(MotionEvent ev) {
         return mSwipeDirection == X ? ev.getX() : ev.getY();
     }
@@ -318,6 +324,7 @@
                     if (Math.abs(delta) > mPagingTouchSlop
                             && Math.abs(delta) > Math.abs(deltaPerpendicular)) {
                         if (mCallback.canChildBeDragged(mCurrView)) {
+                            mSwipingInProgress = true;
                             mCallback.onBeginDrag(mCurrView);
                             mDragging = true;
                             mInitialTouchPos = getPos(ev);
@@ -437,6 +444,7 @@
                     wasRemoved = row.isRemoved();
                 }
                 if (!mCancelled || wasRemoved) {
+                    mSwipingInProgress = false;
                     mCallback.onChildDismissed(animView);
                 }
                 if (endAction != null) {
@@ -626,6 +634,7 @@
                                 !swipedFastEnough() /* useAccelerateInterpolator */);
                     } else {
                         // snappity
+                        mSwipingInProgress = false;
                         mCallback.onDragCancelled(mCurrView);
                         snapChild(mCurrView, 0 /* leftTarget */, velocity);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 81208c4..53ebe74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -24,7 +24,10 @@
 public interface VisibilityLocationProvider {
 
     /**
-     * @return whether the view is in a visible location right now.
+     * Returns whether an ExpandableNotificationRow is in a visible location or not.
+     *
+     * @param row
+     * @return true if row is in a visible location
      */
     boolean isInVisibleLocation(ExpandableNotificationRow row);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index fa75c71..cfb6d99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -22,6 +22,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.NotificationData;
@@ -31,7 +32,8 @@
  * Interface representing the entity that contains notifications. It can have
  * notification views added and removed from it, and will manage displaying them to the user.
  */
-public interface NotificationListContainer {
+public interface NotificationListContainer extends ExpandableView.OnHeightChangedListener,
+        VisibilityLocationProvider {
 
     /**
      * Called when a child is being transferred.
@@ -128,14 +130,6 @@
     ViewGroup getViewParentForNotification(NotificationData.Entry entry);
 
     /**
-     * Called when the height of an expandable view changes.
-     *
-     * @param view view whose height changed
-     * @param animate whether this change should be animated
-     */
-    void onHeightChanged(ExpandableView view, boolean animate);
-
-    /**
      * Resets the currently exposed menu view.
      *
      * @param animate whether to animate the closing/change of menu view
@@ -158,13 +152,6 @@
      */
     void cleanUpViewState(View view);
 
-    /**
-     * Returns whether an ExpandableNotificationRow is in a visible location or not.
-     *
-     * @param row
-     * @return true if row is in a visible location
-     */
-    boolean isInVisibleLocation(ExpandableNotificationRow row);
 
     /**
      * Sets a listener to listen for changes in notification locations.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index bac42ff..0bc54a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -25,6 +25,8 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.TimeAnimator;
 import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.WallpaperManager;
 import android.content.Context;
@@ -43,10 +45,6 @@
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.graphics.ColorUtils;
-
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -69,6 +67,8 @@
 import android.widget.OverScroller;
 import android.widget.ScrollView;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.statusbar.IStatusBarService;
@@ -84,6 +84,7 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
@@ -117,7 +118,7 @@
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone.AnimationStateHandler;
+import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
@@ -142,10 +143,8 @@
 /**
  * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
  */
-public class NotificationStackScrollLayout extends ViewGroup
-        implements ExpandHelper.Callback, ScrollAdapter, OnHeightChangedListener,
-        OnGroupChangeListener, VisibilityLocationProvider, NotificationListContainer,
-        ConfigurationListener, DragDownCallback, AnimationStateHandler, Dumpable {
+public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter,
+        NotificationListContainer, ConfigurationListener, Dumpable {
 
     public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
     private static final String TAG = "StackScroller";
@@ -160,7 +159,6 @@
 
     private ExpandHelper mExpandHelper;
     private final NotificationSwipeHelper mSwipeHelper;
-    private boolean mSwipingInProgress;
     private int mCurrentStackHeight = Integer.MAX_VALUE;
     private final Paint mBackgroundPaint = new Paint();
     private final boolean mShouldDrawNotificationBackground;
@@ -344,7 +342,7 @@
     private float mDimAmount;
     private ValueAnimator mDimAnimator;
     private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
-    private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
+    private final Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
             mDimAnimator = null;
@@ -485,12 +483,12 @@
         mBgColor = context.getColor(R.color.notification_shade_background_color);
         int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
         int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
-        mExpandHelper = new ExpandHelper(getContext(), this,
+        mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
                 minHeight, maxHeight);
         mExpandHelper.setEventSource(this);
         mExpandHelper.setScrollAdapter(this);
-        mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, new SwipeHelperCallback(),
-                getContext(), new NotificationMenuListener());
+        mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback,
+                getContext(), mMenuEventListener);
         mStackScrollAlgorithm = createStackScrollAlgorithm(context);
         initView(context);
         mFalsingManager = FalsingManager.getInstance(context);
@@ -530,7 +528,7 @@
 
         inflateEmptyShadeView();
         inflateFooterView();
-        mVisualStabilityManager.setVisibilityLocationProvider(this);
+        mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
         setLongPressListener(mEntryManager.getNotificationLongClicker());
     }
 
@@ -589,7 +587,7 @@
         return false;
     }
 
-  @ShadeViewRefactor(RefactorComponent.INPUT)
+  @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
   public RemoteInputController.Delegate createDelegate() {
         return new RemoteInputController.Delegate() {
             public void setRemoteInputActive(NotificationData.Entry entry,
@@ -628,7 +626,7 @@
     }
 
     @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public NotificationSwipeActionHelper getSwipeActionHelper() {
         return mSwipeHelper;
     }
@@ -1245,11 +1243,6 @@
         return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
-        mLongPressListener = listener;
-    }
-
     @ShadeViewRefactor(RefactorComponent.ADAPTER)
     public void setQsContainer(ViewGroup qsContainer) {
         mQsContainer = qsContainer;
@@ -1273,7 +1266,7 @@
         return false;
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
         getLocationOnScreen(mTempInt2);
         float localTouchY = touchY - mTempInt2[1];
@@ -1303,16 +1296,8 @@
         return closestChild;
     }
 
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
-        getLocationOnScreen(mTempInt2);
-        return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public ExpandableView getChildAtPosition(float touchX, float touchY) {
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
+    private ExpandableView getChildAtPosition(float touchX, float touchY) {
         return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
 
     }
@@ -1325,7 +1310,7 @@
      * @param requireMinHeight Whether a minimum height is required for a child to be returned.
      * @return the child at the given location.
      */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private ExpandableView getChildAtPosition(float touchX, float touchY,
             boolean requireMinHeight) {
         // find the view under the pointer, accounting for GONE views
@@ -1365,71 +1350,9 @@
         return null;
     }
 
-    @Override
-    @ShadeViewRefactor(RefactorComponent.ADAPTER)
-    public boolean canChildBeExpanded(View v) {
-        return v instanceof ExpandableNotificationRow
-                && ((ExpandableNotificationRow) v).isExpandable()
-                && !((ExpandableNotificationRow) v).areGutsExposed()
-                && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
-    }
-
-    /* Only ever called as a consequence of an expansion gesture in the shade. */
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setUserExpandedChild(View v, boolean userExpanded) {
-        if (v instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-            if (userExpanded && onKeyguard()) {
-                // Due to a race when locking the screen while touching, a notification may be
-                // expanded even after we went back to keyguard. An example of this happens if
-                // you click in the empty space while expanding a group.
-
-                // We also need to un-user lock it here, since otherwise the content height
-                // calculated might be wrong. We also can't invert the two calls since
-                // un-userlocking it will trigger a layout switch in the content view.
-                row.setUserLocked(false);
-                updateContentHeight();
-                notifyHeightChangeListener(row);
-                return;
-            }
-            row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
-            row.onExpandedByGesture(userExpanded);
-        }
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setExpansionCancelled(View v) {
-        if (v instanceof ExpandableNotificationRow) {
-            ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
-        }
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setUserLockedChild(View v, boolean userLocked) {
-        if (v instanceof ExpandableNotificationRow) {
-            ((ExpandableNotificationRow) v).setUserLocked(userLocked);
-        }
-        cancelLongPress();
-        requestDisallowInterceptTouchEvent(true);
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void expansionStateChanged(boolean isExpanding) {
-        mExpandingNotification = isExpanding;
-        if (!mExpandedInThisMotion) {
-            mMaxScrollAfterExpand = mOwnScrollY;
-            mExpandedInThisMotion = true;
-        }
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    public int getMaxExpandHeight(ExpandableView view) {
-        return view.getMaxContentHeight();
+    private ExpandableView getChildAtRawPosition(float touchX, float touchY) {
+        getLocationOnScreen(mTempInt2);
+        return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -1526,14 +1449,6 @@
         return mStatusBarState == StatusBarState.KEYGUARD;
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void setSwipingInProgress(boolean isSwiped) {
-        mSwipingInProgress = isSwiped;
-        if (isSwiped) {
-            requestDisallowInterceptTouchEvent(true);
-        }
-    }
-
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onConfigurationChanged(Configuration newConfig) {
@@ -1567,249 +1482,6 @@
         return this;
     }
 
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean onTouchEvent(MotionEvent ev) {
-        boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
-                || ev.getActionMasked() == MotionEvent.ACTION_UP;
-        handleEmptySpaceClick(ev);
-        boolean expandWantsIt = false;
-        if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
-            if (isCancelOrUp) {
-                mExpandHelper.onlyObserveMovements(false);
-            }
-            boolean wasExpandingBefore = mExpandingNotification;
-            expandWantsIt = mExpandHelper.onTouchEvent(ev);
-            if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
-                    && !mDisallowScrollingInThisMotion) {
-                dispatchDownEventToScroller(ev);
-            }
-        }
-        boolean scrollerWantsIt = false;
-        if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
-                && !mDisallowScrollingInThisMotion) {
-            scrollerWantsIt = onScrollTouch(ev);
-        }
-        boolean horizontalSwipeWantsIt = false;
-        if (!mIsBeingDragged
-                && !mExpandingNotification
-                && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion
-                && !mDisallowDismissInThisMotion) {
-            horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
-        }
-
-        // Check if we need to clear any snooze leavebehinds
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
-                && guts.getGutsContent() instanceof NotificationSnooze) {
-            NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
-            if ((ns.isExpanded() && isCancelOrUp)
-                    || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
-                // If the leavebehind is expanded we clear it on the next up event, otherwise we
-                // clear it on the next non-horizontal swipe or expand event.
-                checkSnoozeLeavebehind();
-            }
-        }
-        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
-            mCheckForLeavebehind = true;
-        }
-        return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void dispatchDownEventToScroller(MotionEvent ev) {
-        MotionEvent downEvent = MotionEvent.obtain(ev);
-        downEvent.setAction(MotionEvent.ACTION_DOWN);
-        onScrollTouch(downEvent);
-        downEvent.recycle();
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean onGenericMotionEvent(MotionEvent event) {
-        if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
-                || mDisallowScrollingInThisMotion) {
-            return false;
-        }
-        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_SCROLL: {
-                    if (!mIsBeingDragged) {
-                        final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
-                        if (vscroll != 0) {
-                            final int delta = (int) (vscroll * getVerticalScrollFactor());
-                            final int range = getScrollRange();
-                            int oldScrollY = mOwnScrollY;
-                            int newScrollY = oldScrollY - delta;
-                            if (newScrollY < 0) {
-                                newScrollY = 0;
-                            } else if (newScrollY > range) {
-                                newScrollY = range;
-                            }
-                            if (newScrollY != oldScrollY) {
-                                setOwnScrollY(newScrollY);
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return super.onGenericMotionEvent(event);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private boolean onScrollTouch(MotionEvent ev) {
-        if (!isScrollingEnabled()) {
-            return false;
-        }
-        if (isInsideQsContainer(ev) && !mIsBeingDragged) {
-            return false;
-        }
-        mForcedScroll = null;
-        initVelocityTrackerIfNotExists();
-        mVelocityTracker.addMovement(ev);
-
-        final int action = ev.getAction();
-
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_DOWN: {
-                if (getChildCount() == 0 || !isInContentBounds(ev)) {
-                    return false;
-                }
-                boolean isBeingDragged = !mScroller.isFinished();
-                setIsBeingDragged(isBeingDragged);
-                /*
-                 * If being flinged and user touches, stop the fling. isFinished
-                 * will be false if being flinged.
-                 */
-                if (!mScroller.isFinished()) {
-                    mScroller.forceFinished(true);
-                }
-
-                // Remember where the motion event started
-                mLastMotionY = (int) ev.getY();
-                mDownX = (int) ev.getX();
-                mActivePointerId = ev.getPointerId(0);
-                break;
-            }
-            case MotionEvent.ACTION_MOVE:
-                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (activePointerIndex == -1) {
-                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
-                    break;
-                }
-
-                final int y = (int) ev.getY(activePointerIndex);
-                final int x = (int) ev.getX(activePointerIndex);
-                int deltaY = mLastMotionY - y;
-                final int xDiff = Math.abs(x - mDownX);
-                final int yDiff = Math.abs(deltaY);
-                if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
-                    setIsBeingDragged(true);
-                    if (deltaY > 0) {
-                        deltaY -= mTouchSlop;
-                    } else {
-                        deltaY += mTouchSlop;
-                    }
-                }
-                if (mIsBeingDragged) {
-                    // Scroll to follow the motion event
-                    mLastMotionY = y;
-                    int range = getScrollRange();
-                    if (mExpandedInThisMotion) {
-                        range = Math.min(range, mMaxScrollAfterExpand);
-                    }
-
-                    float scrollAmount;
-                    if (deltaY < 0) {
-                        scrollAmount = overScrollDown(deltaY);
-                    } else {
-                        scrollAmount = overScrollUp(deltaY, range);
-                    }
-
-                    // Calling customOverScrollBy will call onCustomOverScrolled, which
-                    // sets the scrolling if applicable.
-                    if (scrollAmount != 0.0f) {
-                        // The scrolling motion could not be compensated with the
-                        // existing overScroll, we have to scroll the view
-                        customOverScrollBy((int) scrollAmount, mOwnScrollY,
-                                range, getHeight() / 2);
-                        // If we're scrolling, leavebehinds should be dismissed
-                        checkSnoozeLeavebehind();
-                    }
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-                if (mIsBeingDragged) {
-                    final VelocityTracker velocityTracker = mVelocityTracker;
-                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                    int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-
-                    if (shouldOverScrollFling(initialVelocity)) {
-                        onOverScrollFling(true, initialVelocity);
-                    } else {
-                        if (getChildCount() > 0) {
-                            if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
-                                float currentOverScrollTop = getCurrentOverScrollAmount(true);
-                                if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
-                                    fling(-initialVelocity);
-                                } else {
-                                    onOverScrollFling(false, initialVelocity);
-                                }
-                            } else {
-                                if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
-                                        getScrollRange())) {
-                                    animateScroll();
-                                }
-                            }
-                        }
-                    }
-                    mActivePointerId = INVALID_POINTER;
-                    endDrag();
-                }
-
-                break;
-            case MotionEvent.ACTION_CANCEL:
-                if (mIsBeingDragged && getChildCount() > 0) {
-                    if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
-                        animateScroll();
-                    }
-                    mActivePointerId = INVALID_POINTER;
-                    endDrag();
-                }
-                break;
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                final int index = ev.getActionIndex();
-                mLastMotionY = (int) ev.getY(index);
-                mDownX = (int) ev.getX(index);
-                mActivePointerId = ev.getPointerId(index);
-                break;
-            }
-            case MotionEvent.ACTION_POINTER_UP:
-                onSecondaryPointerUp(ev);
-                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
-                mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
-                break;
-        }
-        return true;
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    protected boolean isInsideQsContainer(MotionEvent ev) {
-        return ev.getY() < mQsContainer.getBottom();
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void onOverScrollFling(boolean open, int initialVelocity) {
-        if (mOverscrollTopChangedListener != null) {
-            mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
-        }
-        mDontReportNextOverScroll = true;
-        setOverScrollAmount(0.0f, true, false);
-    }
-
     /**
      * Perform a scroll upwards and adapt the overscroll amounts accordingly
      *
@@ -1817,7 +1489,7 @@
      * @return The amount of scrolling to be performed by the scroller,
      * not handled by the overScroll amount.
      */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private float overScrollUp(int deltaY, int range) {
         deltaY = Math.max(deltaY, 0);
         float currentTopAmount = getCurrentOverScrollAmount(true);
@@ -1876,24 +1548,6 @@
         return scrollAmount;
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void onSecondaryPointerUp(MotionEvent ev) {
-        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
-                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-        final int pointerId = ev.getPointerId(pointerIndex);
-        if (pointerId == mActivePointerId) {
-            // This was our active pointer going up. Choose a new
-            // active pointer and adjust accordingly.
-            // TODO: Make this decision more intelligent.
-            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-            mLastMotionY = (int) ev.getY(newPointerIndex);
-            mActivePointerId = ev.getPointerId(newPointerIndex);
-            if (mVelocityTracker != null) {
-                mVelocityTracker.clear();
-            }
-        }
-    }
-
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void initVelocityTrackerIfNotExists() {
         if (mVelocityTracker == null) {
@@ -2636,7 +2290,7 @@
      *                  numbers mean that the finger/cursor is moving down the screen,
      *                  which means we want to scroll towards the top.
      */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void fling(int velocityY) {
         if (getChildCount() > 0) {
             int scrollRange = getScrollRange();
@@ -2674,7 +2328,7 @@
      * @return Whether a fling performed on the top overscroll edge lead to the expanded
      * overScroll view (i.e QS).
      */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private boolean shouldOverScrollFling(int initialVelocity) {
         float topOverScroll = getCurrentOverScrollAmount(true);
         return mScrolledToTopOnFirstDown
@@ -2757,7 +2411,7 @@
         return Math.max(desiredPadding, mIntrinsicPadding);
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private float getRubberBandFactor(boolean onTop) {
         if (!onTop) {
             return RUBBER_BAND_FACTOR_NORMAL;
@@ -2777,99 +2431,13 @@
      * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
      * overscroll view (e.g. expand QS).
      */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private boolean isRubberbanded(boolean onTop) {
         return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
                 || !mScrolledToTopOnFirstDown;
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void endDrag() {
-        setIsBeingDragged(false);
 
-        recycleVelocityTracker();
-
-        if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
-            setOverScrollAmount(0, true /* onTop */, true /* animate */);
-        }
-        if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
-            setOverScrollAmount(0, false /* onTop */, true /* animate */);
-        }
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
-        ev.offsetLocation(sourceView.getX(), sourceView.getY());
-        ev.offsetLocation(-targetView.getX(), -targetView.getY());
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        initDownStates(ev);
-        handleEmptySpaceClick(ev);
-        boolean expandWantsIt = false;
-        if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
-            expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
-        }
-        boolean scrollWantsIt = false;
-        if (!mSwipingInProgress && !mExpandingNotification) {
-            scrollWantsIt = onInterceptTouchEventScroll(ev);
-        }
-        boolean swipeWantsIt = false;
-        if (!mIsBeingDragged
-                && !mExpandingNotification
-                && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion
-                && !mDisallowDismissInThisMotion) {
-            swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
-        }
-        // Check if we need to clear any snooze leavebehinds
-        boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
-                !expandWantsIt && !scrollWantsIt) {
-            mCheckForLeavebehind = false;
-            mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
-                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-        }
-        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
-            mCheckForLeavebehind = true;
-        }
-        return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void handleEmptySpaceClick(MotionEvent ev) {
-        switch (ev.getActionMasked()) {
-            case MotionEvent.ACTION_MOVE:
-                if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
-                        || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
-                    mTouchIsClick = false;
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-                if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
-                        isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
-                    mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
-                }
-                break;
-        }
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void initDownStates(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mExpandedInThisMotion = false;
-            mOnlyScrollingInThisMotion = !mScroller.isFinished();
-            mDisallowScrollingInThisMotion = false;
-            mDisallowDismissInThisMotion = false;
-            mTouchIsClick = true;
-            mInitialTouchX = ev.getX();
-            mInitialTouchY = ev.getY();
-        }
-    }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setChildTransferInProgress(boolean childTransferInProgress) {
@@ -2896,15 +2464,6 @@
         mCurrentStackScrollState.removeViewStateForView(child);
     }
 
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        super.requestDisallowInterceptTouchEvent(disallowIntercept);
-        if (disallowIntercept) {
-            cancelLongPress();
-        }
-    }
-
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void onViewRemovedInternal(View child, ViewGroup container) {
         if (mChangePositionInProgress) {
@@ -3600,6 +3159,385 @@
         mGoToFullShadeNeedsAnimation = false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
+    protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
+        return new StackScrollAlgorithm(context);
+    }
+
+    /**
+     * @return Whether a y coordinate is inside the content.
+     */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    public boolean isInContentBounds(float y) {
+        return y < getHeight() - getEmptyBottomMargin();
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
+        mLongPressListener = listener;
+    }
+
+    @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public boolean onTouchEvent(MotionEvent ev) {
+        boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
+                || ev.getActionMasked() == MotionEvent.ACTION_UP;
+        handleEmptySpaceClick(ev);
+        boolean expandWantsIt = false;
+        boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+        if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
+            if (isCancelOrUp) {
+                mExpandHelper.onlyObserveMovements(false);
+            }
+            boolean wasExpandingBefore = mExpandingNotification;
+            expandWantsIt = mExpandHelper.onTouchEvent(ev);
+            if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
+                    && !mDisallowScrollingInThisMotion) {
+                dispatchDownEventToScroller(ev);
+            }
+        }
+        boolean scrollerWantsIt = false;
+        if (mIsExpanded && !swipingInProgress && !mExpandingNotification
+                && !mDisallowScrollingInThisMotion) {
+            scrollerWantsIt = onScrollTouch(ev);
+        }
+        boolean horizontalSwipeWantsIt = false;
+        if (!mIsBeingDragged
+                && !mExpandingNotification
+                && !mExpandedInThisMotion
+                && !mOnlyScrollingInThisMotion
+                && !mDisallowDismissInThisMotion) {
+            horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+        }
+
+        // Check if we need to clear any snooze leavebehinds
+        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+        if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
+                && guts.getGutsContent() instanceof NotificationSnooze) {
+            NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
+            if ((ns.isExpanded() && isCancelOrUp)
+                    || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
+                // If the leavebehind is expanded we clear it on the next up event, otherwise we
+                // clear it on the next non-horizontal swipe or expand event.
+                checkSnoozeLeavebehind();
+            }
+        }
+        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+            mCheckForLeavebehind = true;
+        }
+        return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void dispatchDownEventToScroller(MotionEvent ev) {
+        MotionEvent downEvent = MotionEvent.obtain(ev);
+        downEvent.setAction(MotionEvent.ACTION_DOWN);
+        onScrollTouch(downEvent);
+        downEvent.recycle();
+    }
+
+    @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        if (!isScrollingEnabled() || !mIsExpanded || mSwipeHelper.isSwipingInProgress() || mExpandingNotification
+                || mDisallowScrollingInThisMotion) {
+            return false;
+        }
+        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_SCROLL: {
+                    if (!mIsBeingDragged) {
+                        final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+                        if (vscroll != 0) {
+                            final int delta = (int) (vscroll * getVerticalScrollFactor());
+                            final int range = getScrollRange();
+                            int oldScrollY = mOwnScrollY;
+                            int newScrollY = oldScrollY - delta;
+                            if (newScrollY < 0) {
+                                newScrollY = 0;
+                            } else if (newScrollY > range) {
+                                newScrollY = range;
+                            }
+                            if (newScrollY != oldScrollY) {
+                                setOwnScrollY(newScrollY);
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return super.onGenericMotionEvent(event);
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private boolean onScrollTouch(MotionEvent ev) {
+        if (!isScrollingEnabled()) {
+            return false;
+        }
+        if (isInsideQsContainer(ev) && !mIsBeingDragged) {
+            return false;
+        }
+        mForcedScroll = null;
+        initVelocityTrackerIfNotExists();
+        mVelocityTracker.addMovement(ev);
+
+        final int action = ev.getAction();
+
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN: {
+                if (getChildCount() == 0 || !isInContentBounds(ev)) {
+                    return false;
+                }
+                boolean isBeingDragged = !mScroller.isFinished();
+                setIsBeingDragged(isBeingDragged);
+                /*
+                 * If being flinged and user touches, stop the fling. isFinished
+                 * will be false if being flinged.
+                 */
+                if (!mScroller.isFinished()) {
+                    mScroller.forceFinished(true);
+                }
+
+                // Remember where the motion event started
+                mLastMotionY = (int) ev.getY();
+                mDownX = (int) ev.getX();
+                mActivePointerId = ev.getPointerId(0);
+                break;
+            }
+            case MotionEvent.ACTION_MOVE:
+                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (activePointerIndex == -1) {
+                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
+                    break;
+                }
+
+                final int y = (int) ev.getY(activePointerIndex);
+                final int x = (int) ev.getX(activePointerIndex);
+                int deltaY = mLastMotionY - y;
+                final int xDiff = Math.abs(x - mDownX);
+                final int yDiff = Math.abs(deltaY);
+                if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
+                    setIsBeingDragged(true);
+                    if (deltaY > 0) {
+                        deltaY -= mTouchSlop;
+                    } else {
+                        deltaY += mTouchSlop;
+                    }
+                }
+                if (mIsBeingDragged) {
+                    // Scroll to follow the motion event
+                    mLastMotionY = y;
+                    int range = getScrollRange();
+                    if (mExpandedInThisMotion) {
+                        range = Math.min(range, mMaxScrollAfterExpand);
+                    }
+
+                    float scrollAmount;
+                    if (deltaY < 0) {
+                        scrollAmount = overScrollDown(deltaY);
+                    } else {
+                        scrollAmount = overScrollUp(deltaY, range);
+                    }
+
+                    // Calling customOverScrollBy will call onCustomOverScrolled, which
+                    // sets the scrolling if applicable.
+                    if (scrollAmount != 0.0f) {
+                        // The scrolling motion could not be compensated with the
+                        // existing overScroll, we have to scroll the view
+                        customOverScrollBy((int) scrollAmount, mOwnScrollY,
+                                range, getHeight() / 2);
+                        // If we're scrolling, leavebehinds should be dismissed
+                        checkSnoozeLeavebehind();
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                if (mIsBeingDragged) {
+                    final VelocityTracker velocityTracker = mVelocityTracker;
+                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                    int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
+
+                    if (shouldOverScrollFling(initialVelocity)) {
+                        onOverScrollFling(true, initialVelocity);
+                    } else {
+                        if (getChildCount() > 0) {
+                            if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+                                float currentOverScrollTop = getCurrentOverScrollAmount(true);
+                                if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
+                                    fling(-initialVelocity);
+                                } else {
+                                    onOverScrollFling(false, initialVelocity);
+                                }
+                            } else {
+                                if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+                                        getScrollRange())) {
+                                    animateScroll();
+                                }
+                            }
+                        }
+                    }
+                    mActivePointerId = INVALID_POINTER;
+                    endDrag();
+                }
+
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                if (mIsBeingDragged && getChildCount() > 0) {
+                    if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
+                        animateScroll();
+                    }
+                    mActivePointerId = INVALID_POINTER;
+                    endDrag();
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                final int index = ev.getActionIndex();
+                mLastMotionY = (int) ev.getY(index);
+                mDownX = (int) ev.getX(index);
+                mActivePointerId = ev.getPointerId(index);
+                break;
+            }
+            case MotionEvent.ACTION_POINTER_UP:
+                onSecondaryPointerUp(ev);
+                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
+                mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
+                break;
+        }
+        return true;
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    protected boolean isInsideQsContainer(MotionEvent ev) {
+        return ev.getY() < mQsContainer.getBottom();
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void onOverScrollFling(boolean open, int initialVelocity) {
+        if (mOverscrollTopChangedListener != null) {
+            mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
+        }
+        mDontReportNextOverScroll = true;
+        setOverScrollAmount(0.0f, true, false);
+    }
+
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void onSecondaryPointerUp(MotionEvent ev) {
+        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
+                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+        final int pointerId = ev.getPointerId(pointerIndex);
+        if (pointerId == mActivePointerId) {
+            // This was our active pointer going up. Choose a new
+            // active pointer and adjust accordingly.
+            // TODO: Make this decision more intelligent.
+            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+            mLastMotionY = (int) ev.getY(newPointerIndex);
+            mActivePointerId = ev.getPointerId(newPointerIndex);
+            if (mVelocityTracker != null) {
+                mVelocityTracker.clear();
+            }
+        }
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void endDrag() {
+        setIsBeingDragged(false);
+
+        recycleVelocityTracker();
+
+        if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
+            setOverScrollAmount(0, true /* onTop */, true /* animate */);
+        }
+        if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
+            setOverScrollAmount(0, false /* onTop */, true /* animate */);
+        }
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
+        ev.offsetLocation(sourceView.getX(), sourceView.getY());
+        ev.offsetLocation(-targetView.getX(), -targetView.getY());
+    }
+
+    @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        initDownStates(ev);
+        handleEmptySpaceClick(ev);
+        boolean expandWantsIt = false;
+        boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+        if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
+            expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
+        }
+        boolean scrollWantsIt = false;
+        if (!swipingInProgress && !mExpandingNotification) {
+            scrollWantsIt = onInterceptTouchEventScroll(ev);
+        }
+        boolean swipeWantsIt = false;
+        if (!mIsBeingDragged
+                && !mExpandingNotification
+                && !mExpandedInThisMotion
+                && !mOnlyScrollingInThisMotion
+                && !mDisallowDismissInThisMotion) {
+            swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
+        }
+        // Check if we need to clear any snooze leavebehinds
+        boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
+        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+        if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
+                !expandWantsIt && !scrollWantsIt) {
+            mCheckForLeavebehind = false;
+            mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
+                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
+        }
+        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+            mCheckForLeavebehind = true;
+        }
+        return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void handleEmptySpaceClick(MotionEvent ev) {
+        switch (ev.getActionMasked()) {
+            case MotionEvent.ACTION_MOVE:
+                if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
+                        || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
+                    mTouchIsClick = false;
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
+                        isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
+                    mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
+                }
+                break;
+        }
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void initDownStates(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mExpandedInThisMotion = false;
+            mOnlyScrollingInThisMotion = !mScroller.isFinished();
+            mDisallowScrollingInThisMotion = false;
+            mDisallowDismissInThisMotion = false;
+            mTouchIsClick = true;
+            mInitialTouchX = ev.getX();
+            mInitialTouchY = ev.getY();
+        }
+    }
+
+    @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        super.requestDisallowInterceptTouchEvent(disallowIntercept);
+        if (disallowIntercept) {
+            cancelLongPress();
+        }
+    }
+
     @ShadeViewRefactor(RefactorComponent.INPUT)
     private boolean onInterceptTouchEventScroll(MotionEvent ev) {
         if (!isScrollingEnabled()) {
@@ -3710,11 +3648,6 @@
         return mIsBeingDragged;
     }
 
-    @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
-    protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
-        return new StackScrollAlgorithm(context);
-    }
-
     /**
      * @return Whether the specified motion event is actually happening over the content.
      */
@@ -3723,13 +3656,6 @@
         return isInContentBounds(event.getY());
     }
 
-    /**
-     * @return Whether a y coordinate is inside the content.
-     */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean isInContentBounds(float y) {
-        return y < getHeight() - getEmptyBottomMargin();
-    }
 
     @VisibleForTesting
     @ShadeViewRefactor(RefactorComponent.INPUT)
@@ -3742,6 +3668,83 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void requestDisallowLongPress() {
+        cancelLongPress();
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void requestDisallowDismiss() {
+        mDisallowDismissInThisMotion = true;
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void cancelLongPress() {
+        mSwipeHelper.cancelLongPress();
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
+        mOnEmptySpaceClickListener = listener;
+    }
+
+    /** @hide */
+    @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        if (super.performAccessibilityActionInternal(action, arguments)) {
+            return true;
+        }
+        if (!isEnabled()) {
+            return false;
+        }
+        int direction = -1;
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                // fall through
+            case android.R.id.accessibilityActionScrollDown:
+                direction = 1;
+                // fall through
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                // fall through
+            case android.R.id.accessibilityActionScrollUp:
+                final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
+                        - mShelf.getIntrinsicHeight();
+                final int targetScrollY = Math.max(0,
+                        Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
+                if (targetScrollY != mOwnScrollY) {
+                    mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
+                    animateScroll();
+                    return true;
+                }
+                break;
+        }
+        return false;
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void closeControlsIfOutsideTouch(MotionEvent ev) {
+        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+        NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
+        View translatingParentView = mSwipeHelper.getTranslatingParentView();
+        View view = null;
+        if (guts != null && !guts.getGutsContent().isLeavebehind()) {
+            // Only close visible guts if they're not a leavebehind.
+            view = guts;
+        } else if (menuRow != null && menuRow.isMenuVisible()
+                && translatingParentView != null) {
+            // Checking menu
+            view = translatingParentView;
+        }
+        if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
+            // Touch was outside visible guts / menu notification, close what's visible
+            mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
+                    false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
+            resetExposedMenuView(true /* animate */, true /* force */);
+        }
+    }
+
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onWindowFocusChanged(boolean hasWindowFocus) {
@@ -3760,21 +3763,6 @@
         }
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void requestDisallowLongPress() {
-        cancelLongPress();
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void requestDisallowDismiss() {
-        mDisallowDismissInThisMotion = true;
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void cancelLongPress() {
-        mSwipeHelper.cancelLongPress();
-    }
-
     @Override
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public boolean isScrolledToTop() {
@@ -3916,7 +3904,6 @@
     }
 
     @Override
-    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
         updateContentHeight();
         updateScrollPositionOnExpandInBottom(view);
@@ -3936,7 +3923,6 @@
     }
 
     @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onReset(ExpandableView view) {
         updateAnimationState(view);
         updateChronometerForChild(view);
@@ -3969,13 +3955,8 @@
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setOnHeightChangedListener(
-            ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
-        this.mOnHeightChangedListener = mOnHeightChangedListener;
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
-        mOnEmptySpaceClickListener = listener;
+            ExpandableView.OnHeightChangedListener onHeightChangedListener) {
+        this.mOnHeightChangedListener = onHeightChangedListener;
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4465,7 +4446,7 @@
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setGroupManager(NotificationGroupManager groupManager) {
         this.mGroupManager = groupManager;
-        mGroupManager.setOnGroupChangeListener(this);
+        mGroupManager.setOnGroupChangeListener(mOnGroupChangeListener);
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4508,33 +4489,6 @@
         return touchY > mTopPadding + mStackTranslation;
     }
 
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
-        boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
-                && (mIsExpanded || changedRow.isPinned());
-        if (animated) {
-            mExpandedGroupView = changedRow;
-            mNeedsAnimation = true;
-        }
-        changedRow.setChildrenExpanded(expanded, animated);
-        if (!mGroupExpandedForMeasure) {
-            onHeightChanged(changedRow, false /* needsAnimation */);
-        }
-        runAfterAnimationFinished(new Runnable() {
-            @Override
-            public void run() {
-                changedRow.onFinishedExpansionChange();
-            }
-        });
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
-        mStatusBar.requestNotificationUpdate();
-    }
-
     /** @hide */
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -4567,46 +4521,6 @@
         info.setClassName(ScrollView.class.getName());
     }
 
-    /** @hide */
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
-        if (super.performAccessibilityActionInternal(action, arguments)) {
-            return true;
-        }
-        if (!isEnabled()) {
-            return false;
-        }
-        int direction = -1;
-        switch (action) {
-            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
-                // fall through
-            case android.R.id.accessibilityActionScrollDown:
-                direction = 1;
-                // fall through
-            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
-                // fall through
-            case android.R.id.accessibilityActionScrollUp:
-                final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
-                        - mShelf.getIntrinsicHeight();
-                final int targetScrollY = Math.max(0,
-                        Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
-                if (targetScrollY != mOwnScrollY) {
-                    mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
-                    animateScroll();
-                    return true;
-                }
-                break;
-        }
-        return false;
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void onGroupsChanged() {
-        mStatusBar.requestNotificationUpdate();
-    }
-
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public void generateChildOrderChangedEvent() {
         if (mIsExpanded && mAnimationsEnabled) {
@@ -4649,7 +4563,7 @@
     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         mHeadsUpManager = headsUpManager;
         mHeadsUpManager.addListener(mRoundnessManager);
-        mHeadsUpManager.setAnimationStateHandler(this);
+        mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -5168,67 +5082,7 @@
         return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
     }
 
-    // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
-
-
-    /* Only ever called as a consequence of a lockscreen expansion gesture. */
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean onDraggedDown(View startingChild, int dragLengthY) {
-        if (mStatusBarState == StatusBarState.KEYGUARD
-                && hasActiveNotifications() && (!mStatusBar.isDozing() || mStatusBar.isPulsing())) {
-            mLockscreenGestureLogger.write(
-                    MetricsEvent.ACTION_LS_SHADE,
-                    (int) (dragLengthY / mDisplayMetrics.density),
-                    0 /* velocityDp - N/A */);
-
-            // We have notifications, go to locked shade.
-            mStatusBar.goToLockedShade(startingChild);
-            if (startingChild instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
-                row.onExpandedByGesture(true /* drag down is always an open */);
-            }
-            return true;
-        } else {
-            // abort gesture.
-            return false;
-        }
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void onDragDownReset() {
-        setDimmed(true /* dimmed */, true /* animated */);
-        resetScrollPosition();
-        resetCheckSnoozeLeavebehind();
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void onCrossedThreshold(boolean above) {
-        setDimmed(!above /* dimmed */, true /* animate */);
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void onTouchSlopExceeded() {
-        cancelLongPress();
-        checkSnoozeLeavebehind();
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void setEmptyDragAmount(float amount) {
-        mNotificationPanel.setEmptyDragAmount(amount);
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean isFalsingCheckNeeded() {
-        return mStatusBarState == StatusBarState.KEYGUARD;
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void updateSpeedBumpIndex() {
         int speedBumpIndex = 0;
         int currentIndex = 0;
@@ -5269,30 +5123,6 @@
         mSwipeHelper.resetExposedMenuView(animate, force);
     }
 
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void closeControlsIfOutsideTouch(MotionEvent ev) {
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
-        View translatingParentView = mSwipeHelper.getTranslatingParentView();
-        View view = null;
-        if (guts != null && !guts.getGutsContent().isLeavebehind()) {
-            // Only close visible guts if they're not a leavebehind.
-            view = guts;
-        } else if (menuRow != null && menuRow.isMenuVisible()
-                && translatingParentView != null) {
-            // Checking menu
-            view = translatingParentView;
-        }
-        if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
-            // Touch was outside visible guts / menu notification, close what's visible
-            mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
-                    false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-            resetExposedMenuView(true /* animate */, true /* force */);
-        }
-    }
-
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     static class AnimationEvent {
 
@@ -5614,9 +5444,9 @@
       }
     };
 
-    class NotificationMenuListener implements NotificationMenuRowPlugin.OnMenuEventListener {
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public void onMenuClicked(View view, int x, int y, MenuItem item) {
             if (mLongPressListener == null) {
                 return;
@@ -5630,7 +5460,6 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public void onMenuReset(View row) {
             View translatingParentView = mSwipeHelper.getTranslatingParentView();
             if (translatingParentView != null && row == translatingParentView) {
@@ -5640,7 +5469,6 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public void onMenuShown(View row) {
             if (row instanceof ExpandableNotificationRow) {
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
@@ -5649,9 +5477,11 @@
             }
             mSwipeHelper.onMenuShown(row);
         }
-    }
+    };
 
-    class SwipeHelperCallback implements NotificationSwipeHelper.NotificationCallback {
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
+            new NotificationSwipeHelper.NotificationCallback() {
         @Override
         public void onDismiss() {
             mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
@@ -5671,10 +5501,8 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public void onDragCancelled(View v) {
             mFalsingManager.onNotificatonStopDismissing();
-            setSwipingInProgress(false);
         }
 
         /**
@@ -5682,7 +5510,6 @@
          * re-invoking dismiss logic in case the notification has not made its way out yet).
          */
         @Override
-        @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
         public void onChildDismissed(View view) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
             if (!row.isDismissed()) {
@@ -5701,7 +5528,6 @@
          * @param view view (e.g. notification) to dismiss from the layout
          */
 
-        @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
         public void handleChildViewDismissed(View view) {
             if (mDismissAllInProgress) {
                 return;
@@ -5709,7 +5535,6 @@
 
             boolean isBlockingHelperShown = false;
 
-            setSwipingInProgress(false);
             if (mDragAnimPendingChildren.contains(view)) {
                 // We start the swipe and finish it in the same frame; we don't want a drag
                 // animation.
@@ -5743,13 +5568,11 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public boolean isAntiFalsingNeeded() {
             return onKeyguard();
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public View getChildAtPosition(MotionEvent ev) {
             View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(),
                     ev.getY());
@@ -5772,10 +5595,8 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public void onBeginDrag(View v) {
             mFalsingManager.onNotificatonStartDismissing();
-            setSwipingInProgress(true);
             mAmbientState.onBeginDrag(v);
             updateContinuousShadowDrawing();
             if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
@@ -5786,7 +5607,6 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
         public void onChildSnappedBack(View animView, float targetLeft) {
             mAmbientState.onDragFinished(animView);
             updateContinuousShadowDrawing();
@@ -5808,7 +5628,6 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public boolean updateSwipeProgress(View animView, boolean dismissable,
                 float swipeProgress) {
             // Returning true prevents alpha fading.
@@ -5816,7 +5635,6 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public float getFalsingThresholdFactor() {
             return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
         }
@@ -5825,5 +5643,197 @@
         public boolean canChildBeDismissed(View v) {
             return NotificationStackScrollLayout.this.canChildBeDismissed(v);
         }
+    };
+
+    // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private final DragDownCallback mDragDownCallback = new DragDownCallback() {
+
+        /* Only ever called as a consequence of a lockscreen expansion gesture. */
+        @Override
+        public boolean onDraggedDown(View startingChild, int dragLengthY) {
+            if (mStatusBarState == StatusBarState.KEYGUARD
+                    && hasActiveNotifications() && (!mStatusBar.isDozing()
+                    || mStatusBar.isPulsing())) {
+                mLockscreenGestureLogger.write(
+                        MetricsEvent.ACTION_LS_SHADE,
+                        (int) (dragLengthY / mDisplayMetrics.density),
+                        0 /* velocityDp - N/A */);
+
+                // We have notifications, go to locked shade.
+                mStatusBar.goToLockedShade(startingChild);
+                if (startingChild instanceof ExpandableNotificationRow) {
+                    ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
+                    row.onExpandedByGesture(true /* drag down is always an open */);
+                }
+                return true;
+            } else {
+                // abort gesture.
+                return false;
+            }
+        }
+
+        @Override
+        public void onDragDownReset() {
+            setDimmed(true /* dimmed */, true /* animated */);
+            resetScrollPosition();
+            resetCheckSnoozeLeavebehind();
+        }
+
+        @Override
+        public void onCrossedThreshold(boolean above) {
+            setDimmed(!above /* dimmed */, true /* animate */);
+        }
+
+        @Override
+        public void onTouchSlopExceeded() {
+            cancelLongPress();
+            checkSnoozeLeavebehind();
+        }
+
+        @Override
+        public void setEmptyDragAmount(float amount) {
+            mNotificationPanel.setEmptyDragAmount(amount);
+        }
+
+        @Override
+        public boolean isFalsingCheckNeeded() {
+            return mStatusBarState == StatusBarState.KEYGUARD;
+        }
+    };
+
+    public DragDownCallback getDragDownCallback() { return mDragDownCallback; }
+
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() {
+        @Override
+        public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
+            return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
+        }
+
+        @Override
+        public boolean isExpanded() {
+            return mIsExpanded;
+        }
+
+        @Override
+        public Context getContext() {
+            return mContext;
+        }
+    };
+
+    public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; }
+
+
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() {
+        @Override
+        public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
+            boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
+                    && (mIsExpanded || changedRow.isPinned());
+            if (animated) {
+                mExpandedGroupView = changedRow;
+                mNeedsAnimation = true;
+            }
+            changedRow.setChildrenExpanded(expanded, animated);
+            if (!mGroupExpandedForMeasure) {
+                onHeightChanged(changedRow, false /* needsAnimation */);
+            }
+            runAfterAnimationFinished(new Runnable() {
+                @Override
+                public void run() {
+                    changedRow.onFinishedExpansionChange();
+                }
+            });
+        }
+
+        @Override
+        public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
+            mStatusBar.requestNotificationUpdate();
+        }
+
+        @Override
+        public void onGroupsChanged() {
+            mStatusBar.requestNotificationUpdate();
+        }
+    };
+
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() {
+        @Override
+        public ExpandableView getChildAtPosition(float touchX, float touchY) {
+            return NotificationStackScrollLayout.this.getChildAtPosition(touchX, touchY);
+        }
+
+        @Override
+        public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
+            return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
+        }
+
+        @Override
+        public boolean canChildBeExpanded(View v) {
+            return v instanceof ExpandableNotificationRow
+                    && ((ExpandableNotificationRow) v).isExpandable()
+                    && !((ExpandableNotificationRow) v).areGutsExposed()
+                    && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
+        }
+
+        /* Only ever called as a consequence of an expansion gesture in the shade. */
+        @Override
+        public void setUserExpandedChild(View v, boolean userExpanded) {
+            if (v instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+                if (userExpanded && onKeyguard()) {
+                    // Due to a race when locking the screen while touching, a notification may be
+                    // expanded even after we went back to keyguard. An example of this happens if
+                    // you click in the empty space while expanding a group.
+
+                    // We also need to un-user lock it here, since otherwise the content height
+                    // calculated might be wrong. We also can't invert the two calls since
+                    // un-userlocking it will trigger a layout switch in the content view.
+                    row.setUserLocked(false);
+                    updateContentHeight();
+                    notifyHeightChangeListener(row);
+                    return;
+                }
+                row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
+                row.onExpandedByGesture(userExpanded);
+            }
+        }
+
+        @Override
+        public void setExpansionCancelled(View v) {
+            if (v instanceof ExpandableNotificationRow) {
+                ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
+            }
+        }
+
+        @Override
+        public void setUserLockedChild(View v, boolean userLocked) {
+            if (v instanceof ExpandableNotificationRow) {
+                ((ExpandableNotificationRow) v).setUserLocked(userLocked);
+            }
+            cancelLongPress();
+            requestDisallowInterceptTouchEvent(true);
+        }
+
+        @Override
+        public void expansionStateChanged(boolean isExpanding) {
+            mExpandingNotification = isExpanding;
+            if (!mExpandedInThisMotion) {
+                mMaxScrollAfterExpand = mOwnScrollY;
+                mExpandedInThisMotion = true;
+            }
+        }
+
+        @Override
+        public int getMaxExpandHeight(ExpandableView view) {
+            return view.getMaxContentHeight();
+        }
+    };
+
+    public ExpandHelper.Callback getExpandHelperCallback() {
+        return mExpandHelperCallback;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 028957d..599da3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -31,11 +31,9 @@
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.notification.ShadeViewRefactor;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 
-@ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.INPUT)
 class NotificationSwipeHelper extends SwipeHelper
         implements NotificationSwipeActionHelper {
     @VisibleForTesting
@@ -229,6 +227,7 @@
         if (mCallback.isExpanded()) {
             // We don't want to quick-dismiss when it's a heads up as this might lead to closing
             // of the panel early.
+            mSwipingInProgress = false;
             mCallback.handleChildViewDismissed(view);
         }
         mCallback.onDismiss();
@@ -248,6 +247,7 @@
     @Override
     public void snapChild(final View animView, final float targetLeft, float velocity) {
         superSnapChild(animView, targetLeft, velocity);
+        mSwipingInProgress = false;
         mCallback.onDragCancelled(animView);
         if (targetLeft == 0) {
             handleMenuCoveredOrDismissed();
@@ -354,6 +354,7 @@
 
     public void onMenuShown(View animView) {
         setExposedMenuView(getTranslatingParentView());
+        mSwipingInProgress = false;
         mCallback.onDragCancelled(animView);
         Handler handler = getHandler();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 4df1e3b..e4a5caa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -32,7 +32,7 @@
 public class HeadsUpTouchHelper implements Gefingerpoken {
 
     private HeadsUpManagerPhone mHeadsUpManager;
-    private NotificationStackScrollLayout mStackScroller;
+    private Callback mCallback;
     private int mTrackingPointer;
     private float mTouchSlop;
     private float mInitialTouchX;
@@ -44,12 +44,12 @@
     private ExpandableNotificationRow mPickedChild;
 
     public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
-            NotificationStackScrollLayout stackScroller,
+            Callback callback,
             NotificationPanelView notificationPanelView) {
         mHeadsUpManager = headsUpManager;
-        mStackScroller = stackScroller;
+        mCallback = callback;
         mPanel = notificationPanelView;
-        Context context = stackScroller.getContext();
+        Context context = mCallback.getContext();
         final ViewConfiguration configuration = ViewConfiguration.get(context);
         mTouchSlop = configuration.getScaledTouchSlop();
     }
@@ -75,13 +75,13 @@
                 mInitialTouchY = y;
                 mInitialTouchX = x;
                 setTrackingHeadsUp(false);
-                ExpandableView child = mStackScroller.getChildAtRawPosition(x, y);
+                ExpandableView child = mCallback.getChildAtRawPosition(x, y);
                 mTouchingHeadsUpView = false;
                 if (child instanceof ExpandableNotificationRow) {
                     mPickedChild = (ExpandableNotificationRow) child;
-                    mTouchingHeadsUpView = !mStackScroller.isExpanded()
+                    mTouchingHeadsUpView = !mCallback.isExpanded()
                             && mPickedChild.isHeadsUp() && mPickedChild.isPinned();
-                } else if (child == null && !mStackScroller.isExpanded()) {
+                } else if (child == null && !mCallback.isExpanded()) {
                     // We might touch above the visible heads up child, but then we still would
                     // like to capture it.
                     NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
@@ -174,4 +174,10 @@
         mPickedChild = null;
         mTouchingHeadsUpView = false;
     }
+
+    public interface Callback {
+        ExpandableView getChildAtRawPosition(float touchX, float touchY);
+        boolean isExpanded();
+        Context getContext();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 980ba87..e92656ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -60,7 +60,6 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
-import com.android.systemui.RecentsComponent;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -71,7 +70,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.NavigationBarCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.policy.DeadZone;
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 
@@ -140,6 +138,7 @@
     private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
     private final ContextualButtonGroup mContextualButtonGroup;
     private Configuration mConfiguration;
+    private Configuration mTmpLastConfiguration;
 
     private NavigationBarInflaterView mNavigationInflaterView;
     private RecentsOnboarding mRecentsOnboarding;
@@ -286,6 +285,7 @@
         mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
 
         mConfiguration = new Configuration();
+        mTmpLastConfiguration = new Configuration();
         mConfiguration.updateFrom(context.getResources().getConfiguration());
 
         mScreenPinningNotify = new ScreenPinningNotify(mContext);
@@ -445,13 +445,13 @@
     }
 
     private void reloadNavIcons() {
-        updateIcons(Configuration.EMPTY, mConfiguration);
+        updateIcons(Configuration.EMPTY);
     }
 
-    private void updateIcons(Configuration oldConfig, Configuration newConfig) {
-        final boolean orientationChange = oldConfig.orientation != newConfig.orientation;
-        final boolean densityChange = oldConfig.densityDpi != newConfig.densityDpi;
-        final boolean dirChange = oldConfig.getLayoutDirection() != newConfig.getLayoutDirection();
+    private void updateIcons(Configuration oldConfig) {
+        final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation;
+        final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi;
+        final boolean dirChange = oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection();
 
         if (orientationChange || densityChange) {
             mDockedIcon = getDrawable(R.drawable.ic_sysbar_docked);
@@ -485,7 +485,7 @@
     private void orientBackButton(KeyButtonDrawable drawable) {
         final boolean useAltBack =
                 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
-        final boolean isRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
         float degrees = useAltBack
                 ? (isRtl ? 270 : -90)
                 : (isRtl ? 180 : 0);
@@ -946,26 +946,27 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        boolean uiCarModeChanged = updateCarMode(newConfig);
+        mTmpLastConfiguration.updateFrom(mConfiguration);
+        mConfiguration.updateFrom(newConfig);
+        boolean uiCarModeChanged = updateCarMode();
         updateTaskSwitchHelper();
-        updateIcons(mConfiguration, newConfig);
+        updateIcons(mTmpLastConfiguration);
         updateRecentsIcon();
-        mRecentsOnboarding.onConfigurationChanged(newConfig);
-        if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi
-                || mConfiguration.getLayoutDirection() != newConfig.getLayoutDirection()) {
+        mRecentsOnboarding.onConfigurationChanged(mConfiguration);
+        if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi
+                || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) {
             // If car mode or density changes, we need to reset the icons.
             updateNavButtonIcons();
         }
-        mConfiguration.updateFrom(newConfig);
     }
 
     /**
      * If the configuration changed, update the carmode and return that it was updated.
      */
-    private boolean updateCarMode(Configuration newConfig) {
+    private boolean updateCarMode() {
         boolean uiCarModeChanged = false;
-        if (newConfig != null) {
-            int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
+        if (mConfiguration != null) {
+            int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK;
             final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR);
 
             if (isCarMode != mInCarMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 6d53cd3..7507702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2521,8 +2521,8 @@
     @Override
     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         super.setHeadsUpManager(headsUpManager);
-        mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller,
-                this);
+        mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
+                mNotificationStackScroller.getHeadsUpCallback(), this);
     }
 
     public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 45b32c7..ad9b9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -58,6 +58,7 @@
 import com.android.internal.view.FloatingActionMode;
 import com.android.internal.widget.FloatingToolbar;
 import com.android.systemui.Dependency;
+import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.DragDownHelper;
@@ -182,6 +183,11 @@
         }
     }
 
+    @VisibleForTesting
+    protected NotificationStackScrollLayout getStackScrollLayout() {
+        return mStackScrollLayout;
+    }
+
     @Override
     public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new LayoutParams(getContext(), attrs);
@@ -215,8 +221,11 @@
 
     public void setService(StatusBar service) {
         mService = service;
-        setDragDownHelper(new DragDownHelper(getContext(), this, mStackScrollLayout,
-                mStackScrollLayout));
+        NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+        ExpandHelper.Callback expandHelperCallback = stackScrollLayout.getExpandHelperCallback();
+        DragDownHelper.DragDownCallback dragDownCallback = stackScrollLayout.getDragDownCallback();
+        setDragDownHelper(new DragDownHelper(getContext(), this, expandHelperCallback,
+                dragDownCallback));
     }
 
     @VisibleForTesting
@@ -309,7 +318,7 @@
             }
         }
         if (isDown) {
-            mStackScrollLayout.closeControlsIfOutsideTouch(ev);
+            getStackScrollLayout().closeControlsIfOutsideTouch(ev);
         }
         if (mService.isDozing()) {
             mService.mDozeScrimController.extendPulse();
@@ -331,13 +340,14 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mService.isDozing() && !mStackScrollLayout.hasPulsingNotifications()) {
+        NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+        if (mService.isDozing() && !stackScrollLayout.hasPulsingNotifications()) {
             // Capture all touch events in always-on.
             return true;
         }
         boolean intercept = false;
         if (mNotificationPanel.isFullyExpanded()
-                && mStackScrollLayout.getVisibility() == View.VISIBLE
+                && stackScrollLayout.getVisibility() == View.VISIBLE
                 && mStatusBarStateController.getState() == StatusBarState.KEYGUARD
                 && !mService.isBouncerShowing()
                 && !mService.isDozing()) {
@@ -349,7 +359,7 @@
         if (intercept) {
             MotionEvent cancellation = MotionEvent.obtain(ev);
             cancellation.setAction(MotionEvent.ACTION_CANCEL);
-            mStackScrollLayout.onInterceptTouchEvent(cancellation);
+            stackScrollLayout.onInterceptTouchEvent(cancellation);
             mNotificationPanel.onInterceptTouchEvent(cancellation);
             cancellation.recycle();
         }
@@ -391,8 +401,9 @@
     }
 
     public void cancelExpandHelper() {
-        if (mStackScrollLayout != null) {
-            mStackScrollLayout.cancelExpandHelper();
+        NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+        if (stackScrollLayout != null) {
+            stackScrollLayout.cancelExpandHelper();
         }
     }
 
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 24a28cb..70a3589 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -461,6 +461,8 @@
                     MobileSignalController controller = mMobileSignalControllers.valueAt(i);
                     controller.handleBroadcast(intent);
                 }
+                mConfig = Config.readConfig(mContext);
+                mReceiverHandler.post(this::handleConfigurationChanged);
                 break;
             case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
                 // Avoid rebroadcast because SysUI is direct boot aware.
@@ -1042,18 +1044,23 @@
             config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G);
             config.alwaysShowCdmaRssi =
                     res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
-            config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
             config.hspaDataDistinguishable =
                     res.getBoolean(R.bool.config_hspa_data_distinguishable);
-            config.hideLtePlus = res.getBoolean(R.bool.config_hideLtePlus);
             config.inflateSignalStrengths = res.getBoolean(R.bool.config_inflateSignalStrength);
 
             CarrierConfigManager configMgr = (CarrierConfigManager)
                     context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            PersistableBundle b = configMgr.getConfig();
+            // Handle specific carrier config values for the default data SIM
+            int defaultDataSubId = SubscriptionManager.from(context)
+                    .getDefaultDataSubscriptionId();
+            PersistableBundle b = configMgr.getConfigForSubId(defaultDataSubId);
             if (b != null) {
                 config.alwaysShowDataRatIcon = b.getBoolean(
                         CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL);
+                config.show4gForLte = b.getBoolean(
+                        CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL);
+                config.hideLtePlus = b.getBoolean(
+                        CarrierConfigManager.KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL);
             }
             return config;
         }
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 15c18e9..6b4ccc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -218,6 +218,9 @@
         public void generateChildOrderChangedEvent() {}
 
         @Override
+        public void onReset(ExpandableView view) {}
+
+        @Override
         public int getContainerChildCount() {
             return mRows.size();
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index 445a194..46335dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -17,11 +17,11 @@
 package com.android.systemui.statusbar.phone;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.MotionEvent;
@@ -31,6 +31,7 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -43,11 +44,14 @@
     private StatusBarWindowView mView;
     private StatusBar mStatusBar;
     private DragDownHelper mDragDownHelper;
+    private NotificationStackScrollLayout mStackScrollLayout;
 
     @Before
     public void setUp() {
         mDependency.injectMockDependency(StatusBarStateController.class);
-        mView = new StatusBarWindowView(getContext(), null);
+        mView = spy(new StatusBarWindowView(getContext(), null));
+        mStackScrollLayout = mock(NotificationStackScrollLayout.class);
+        when(mView.getStackScrollLayout()).thenReturn(mStackScrollLayout);
         mStatusBar = mock(StatusBar.class);
         mView.setService(mStatusBar);
         mDragDownHelper = mock(DragDownHelper.class);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b750d79..1c8d99a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -35,6 +35,8 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.os.Process.INVALID_UID;
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
@@ -189,6 +191,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.ConcurrentModificationException;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -257,6 +260,14 @@
     @GuardedBy("mVpns")
     private LockdownVpnTracker mLockdownTracker;
 
+    /**
+     * Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal
+     * handler thread, they don't need a lock.
+     */
+    private SparseIntArray mUidRules = new SparseIntArray();
+    /** Flag indicating if background data is restricted. */
+    private boolean mRestrictBackground;
+
     final private Context mContext;
     // 0 is full bad, 100 is full good
     private int mDefaultInetConditionPublished = 0;
@@ -419,6 +430,16 @@
     // Handle private DNS validation status updates.
     private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
 
+    /**
+     * Used to handle onUidRulesChanged event from NetworkPolicyManagerService.
+     */
+    private static final int EVENT_UID_RULES_CHANGED = 39;
+
+    /**
+     * Used to handle onRestrictBackgroundChanged event from NetworkPolicyManagerService.
+     */
+    private static final int EVENT_DATA_SAVER_CHANGED = 40;
+
     private static String eventName(int what) {
         return sMagicDecoderRing.get(what, Integer.toString(what));
     }
@@ -780,6 +801,9 @@
         mKeyStore = KeyStore.getInstance();
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
 
+        // To ensure uid rules are synchronized with Network Policy, register for
+        // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
+        // reading existing policy from disk.
         try {
             mPolicyManager.registerListener(mPolicyListener);
         } catch (RemoteException e) {
@@ -910,7 +934,8 @@
         registerPrivateDnsSettingsCallbacks();
     }
 
-    private Tethering makeTethering() {
+    @VisibleForTesting
+    protected Tethering makeTethering() {
         // TODO: Move other elements into @Overridden getters.
         final TetheringDependencies deps = new TetheringDependencies() {
             @Override
@@ -1116,11 +1141,6 @@
         if (ignoreBlocked) {
             return false;
         }
-        // Networks are never blocked for system services
-        // TODO: consider moving this check to NetworkPolicyManagerInternal.isUidNetworkingBlocked.
-        if (isSystem(uid)) {
-            return false;
-        }
         synchronized (mVpns) {
             final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
             if (vpn != null && vpn.isBlockingUid(uid)) {
@@ -1150,6 +1170,17 @@
         mNetworkInfoBlockingLogs.log(action + " " + uid);
     }
 
+    private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net,
+            boolean blocked) {
+        if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
+            return;
+        }
+        String action = blocked ? "BLOCKED" : "UNBLOCKED";
+        log(String.format("Blocked status changed to %s for %d(%d) on netId %d", blocked,
+                nri.mUid, nri.request.requestId, net.netId));
+        mNetworkInfoBlockingLogs.log(action + " " + nri.mUid);
+    }
+
     /**
      * Apply any relevant filters to {@link NetworkState} for the given UID. For
      * example, this may mark the network as {@link DetailedState#BLOCKED} based
@@ -1651,10 +1682,17 @@
     private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
         @Override
         public void onUidRulesChanged(int uid, int uidRules) {
-            // TODO: notify UID when it has requested targeted updates
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_RULES_CHANGED, uid, uidRules));
         }
         @Override
         public void onRestrictBackgroundChanged(boolean restrictBackground) {
+            // caller is NPMS, since we only register with them
+            if (LOGD_BLOCKED_NETWORKINFO) {
+                log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
+            }
+            mHandler.sendMessage(mHandler.obtainMessage(
+                    EVENT_DATA_SAVER_CHANGED, restrictBackground ? 1 : 0, 0));
+
             // TODO: relocate this specific callback in Tethering.
             if (restrictBackground) {
                 log("onRestrictBackgroundChanged(true): disabling tethering");
@@ -1663,6 +1701,50 @@
         }
     };
 
+    void handleUidRulesChanged(int uid, int newRules) {
+        // skip update when we've already applied rules
+        final int oldRules = mUidRules.get(uid, RULE_NONE);
+        if (oldRules == newRules) return;
+
+        maybeNotifyNetworkBlockedForNewUidRules(uid, newRules);
+
+        if (newRules == RULE_NONE) {
+            mUidRules.delete(uid);
+        } else {
+            mUidRules.put(uid, newRules);
+        }
+    }
+
+    void handleRestrictBackgroundChanged(boolean restrictBackground) {
+        if (mRestrictBackground == restrictBackground) return;
+
+        for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            final boolean curMetered = nai.networkCapabilities.isMetered();
+            maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
+                    restrictBackground);
+        }
+
+        mRestrictBackground = restrictBackground;
+    }
+
+    private boolean isUidNetworkingWithVpnBlocked(int uid, int uidRules, boolean isNetworkMetered,
+            boolean isBackgroundRestricted) {
+        synchronized (mVpns) {
+            final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
+            // Because the return value of this function depends on the list of UIDs the
+            // always-on VPN blocks when in lockdown mode, when the always-on VPN changes that
+            // list all state depending on the return value of this function has to be recomputed.
+            // TODO: add a trigger when the always-on VPN sets its blocked UIDs to reevaluate and
+            // send the necessary onBlockedStatusChanged callbacks.
+            if (vpn != null && vpn.isBlockingUid(uid)) {
+                return true;
+            }
+        }
+
+        return mPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
+                isNetworkMetered, isBackgroundRestricted);
+    }
+
     /**
      * Require that the caller is either in the same user or has appropriate permission to interact
      * across users.
@@ -2118,6 +2200,28 @@
         pw.decreaseIndent();
         pw.println();
 
+        pw.print("Restrict background: ");
+        pw.println(mRestrictBackground);
+        pw.println();
+
+        pw.println("Status for known UIDs:");
+        pw.increaseIndent();
+        final int size = mUidRules.size();
+        for (int i = 0; i < size; i++) {
+            // Don't crash if the array is modified while dumping in bugreports.
+            try {
+                final int uid = mUidRules.keyAt(i);
+                final int uidRules = mUidRules.get(uid, RULE_NONE);
+                pw.println("UID=" + uid + " rules=" + uidRulesToString(uidRules));
+            } catch (ArrayIndexOutOfBoundsException e) {
+                pw.println("  ArrayIndexOutOfBoundsException");
+            } catch (ConcurrentModificationException e) {
+                pw.println("  ConcurrentModificationException");
+            }
+        }
+        pw.println();
+        pw.decreaseIndent();
+
         pw.println("Network Requests:");
         pw.increaseIndent();
         dumpNetworkRequests(pw);
@@ -3195,6 +3299,12 @@
                     handlePrivateDnsValidationUpdate(
                             (PrivateDnsValidationUpdate) msg.obj);
                     break;
+                case EVENT_UID_RULES_CHANGED:
+                    handleUidRulesChanged(msg.arg1, msg.arg2);
+                    break;
+                case EVENT_DATA_SAVER_CHANGED:
+                    handleRestrictBackgroundChanged(toBool(msg.arg1));
+                    break;
             }
         }
     }
@@ -3783,6 +3893,8 @@
     private void setLockdownTracker(LockdownVpnTracker tracker) {
         // Shutdown any existing tracker
         final LockdownVpnTracker existing = mLockdownTracker;
+        // TODO: Add a trigger when the always-on VPN enable/disable to reevaluate and send the
+        // necessary onBlockedStatusChanged callbacks.
         mLockdownTracker = null;
         if (existing != null) {
             existing.shutdown();
@@ -4893,12 +5005,20 @@
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
 
-        // Report changes that are interesting for network statistics tracking.
         if (prevNc != null) {
-            final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) !=
-                    newNc.hasCapability(NET_CAPABILITY_NOT_METERED);
+            final boolean oldMetered = prevNc.isMetered();
+            final boolean newMetered = newNc.isMetered();
+            final boolean meteredChanged = oldMetered != newMetered;
+
+            if (meteredChanged) {
+                maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground,
+                        mRestrictBackground);
+            }
+
             final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
                     newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+
+            // Report changes that are interesting for network statistics tracking.
             if (meteredChanged || roamingChanged) {
                 notifyIfacesChangedForNetworkStats();
             }
@@ -5028,6 +5148,8 @@
             case ConnectivityManager.CALLBACK_AVAILABLE: {
                 putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
                 putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+                // For this notification, arg1 contains the blocked status.
+                msg.arg1 = arg1;
                 break;
             }
             case ConnectivityManager.CALLBACK_LOSING: {
@@ -5045,6 +5167,10 @@
                 putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
                 break;
             }
+            case ConnectivityManager.CALLBACK_BLK_CHANGED: {
+                msg.arg1 = arg1;
+                break;
+            }
         }
         msg.what = notificationType;
         msg.setData(bundle);
@@ -5600,7 +5726,76 @@
             return;
         }
 
-        callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, 0);
+        final boolean metered = nai.networkCapabilities.isMetered();
+        final boolean blocked = isUidNetworkingWithVpnBlocked(nri.mUid, mUidRules.get(nri.mUid),
+                metered, mRestrictBackground);
+        callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
+    }
+
+    /**
+     * Notify of the blocked state apps with a registered callback matching a given NAI.
+     *
+     * Unlike other callbacks, blocked status is different between each individual uid. So for
+     * any given nai, all requests need to be considered according to the uid who filed it.
+     *
+     * @param nai The target NetworkAgentInfo.
+     * @param oldMetered True if the previous network capabilities is metered.
+     * @param newRestrictBackground True if data saver is enabled.
+     */
+    private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
+            boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground) {
+
+        for (int i = 0; i < nai.numNetworkRequests(); i++) {
+            NetworkRequest nr = nai.requestAt(i);
+            NetworkRequestInfo nri = mNetworkRequests.get(nr);
+            final int uidRules = mUidRules.get(nri.mUid);
+            final boolean oldBlocked, newBlocked;
+            // mVpns lock needs to be hold here to ensure that the active VPN cannot be changed
+            // between these two calls.
+            synchronized (mVpns) {
+                oldBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, oldMetered,
+                        oldRestrictBackground);
+                newBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, newMetered,
+                        newRestrictBackground);
+            }
+            if (oldBlocked != newBlocked) {
+                callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
+                        encodeBool(newBlocked));
+            }
+        }
+    }
+
+    /**
+     * Notify apps with a given UID of the new blocked state according to new uid rules.
+     * @param uid The uid for which the rules changed.
+     * @param newRules The new rules to apply.
+     */
+    private void maybeNotifyNetworkBlockedForNewUidRules(int uid, int newRules) {
+        for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            final boolean metered = nai.networkCapabilities.isMetered();
+            final boolean oldBlocked, newBlocked;
+            // TODO: Consider that doze mode or turn on/off battery saver would deliver lots of uid
+            // rules changed event. And this function actually loop through all connected nai and
+            // its requests. It seems that mVpns lock will be grabbed frequently in this case.
+            // Reduce the number of locking or optimize the use of lock are likely needed in future.
+            synchronized (mVpns) {
+                oldBlocked = isUidNetworkingWithVpnBlocked(
+                        uid, mUidRules.get(uid), metered, mRestrictBackground);
+                newBlocked = isUidNetworkingWithVpnBlocked(
+                        uid, newRules, metered, mRestrictBackground);
+            }
+            if (oldBlocked == newBlocked) {
+                return;
+            }
+            final int arg = encodeBool(newBlocked);
+            for (int i = 0; i < nai.numNetworkRequests(); i++) {
+                NetworkRequest nr = nai.requestAt(i);
+                NetworkRequestInfo nri = mNetworkRequests.get(nr);
+                if (nri != null && nri.mUid == uid) {
+                    callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg);
+                }
+            }
+        }
     }
 
     private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index b2be5e6..793a177 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -1270,26 +1270,36 @@
         public int onCommand(String cmd) {
             if ("vibrate".equals(cmd)) {
                 return runVibrate();
+            } else if ("prebaked".equals(cmd)) {
+                return runPrebaked();
             }
             return handleDefaultCommands(cmd);
         }
 
+        private boolean checkDoNotDisturb() {
+            try {
+                final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
+                        Settings.Global.ZEN_MODE);
+                if (zenMode != Settings.Global.ZEN_MODE_OFF) {
+                    try (PrintWriter pw = getOutPrintWriter();) {
+                        pw.print("Ignoring because device is on DND mode ");
+                        pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
+                                zenMode));
+                        return true;
+                    }
+                }
+            } catch (SettingNotFoundException e) {
+                // ignore
+            }
+
+            return false;
+        }
+
         private int runVibrate() {
             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
             try {
-                try {
-                    final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
-                            Settings.Global.ZEN_MODE);
-                    if (zenMode != Settings.Global.ZEN_MODE_OFF) {
-                        try (PrintWriter pw = getOutPrintWriter();) {
-                            pw.print("Ignoring because device is on DND mode ");
-                            pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
-                                    zenMode));
-                            return 0;
-                        }
-                    }
-                } catch (SettingNotFoundException e) {
-                    // ignore
+                if (checkDoNotDisturb()) {
+                    return 0;
                 }
 
                 final long duration = Long.parseLong(getNextArgRequired());
@@ -1311,6 +1321,30 @@
             }
         }
 
+        private int runPrebaked() {
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
+            try {
+                if (checkDoNotDisturb()) {
+                    return 0;
+                }
+
+                final int id = Integer.parseInt(getNextArgRequired());
+
+                String description = getNextArg();
+                if (description == null) {
+                    description = "Shell command";
+                }
+
+                VibrationEffect effect =
+                        VibrationEffect.get(id, false);
+                vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
+                        "Shell Command", mToken);
+                return 0;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            }
+        }
+
         @Override
         public void onHelp() {
             try (PrintWriter pw = getOutPrintWriter();) {
@@ -1321,6 +1355,9 @@
                 pw.println("  vibrate duration [description]");
                 pw.println("    Vibrates for duration milliseconds; ignored when device is on DND ");
                 pw.println("    (Do Not Disturb) mode.");
+                pw.println("  prebaked effect-id [description]");
+                pw.println("    Vibrates with prebaked effect; ignored when device is on DND ");
+                pw.println("    (Do Not Disturb) mode.");
                 pw.println("");
             }
         }
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index fcda83d..3939bee 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -339,7 +339,8 @@
                         Slog.w(TAG, uei.getSwitchStatePath() +
                                 " not found while attempting to determine initial switch state");
                     } catch (Exception e) {
-                        Slog.e(TAG, "" , e);
+                        Slog.e(TAG, "Error while attempting to determine initial switch state for "
+                                + uei.getDevName() , e);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7235312..4070bca 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3620,7 +3620,7 @@
                     nextTime = sr.executingStart;
                 }
             }
-            if (timeout != null && mAm.mLruProcesses.contains(proc)) {
+            if (timeout != null && mAm.mProcessList.mLruProcesses.contains(proc)) {
                 Slog.w(TAG, "Timeout executing service: " + timeout);
                 StringWriter sw = new StringWriter();
                 PrintWriter pw = new FastPrintWriter(sw, false, 1024);
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index fecb573..ede13ef 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -37,16 +37,16 @@
 import static com.android.server.am.ActivityDisplayProto.ID;
 import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
 import static com.android.server.am.ActivityDisplayProto.STACKS;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
+import static com.android.server.am.ActivityStackSupervisor.TAG_STATES;
+import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.am.ActivityStackSupervisor.TAG_STATES;
-import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
 
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
@@ -873,6 +873,52 @@
         return null;
     }
 
+    ActivityRecord topRunningActivity() {
+        return topRunningActivity(false /* considerKeyguardState */);
+    }
+
+    /**
+     * Returns the top running activity in the focused stack. In the case the focused stack has no
+     * such activity, the next focusable stack on this display is returned.
+     *
+     * @param considerKeyguardState Indicates whether the locked state should be considered. if
+     *                              {@code true} and the keyguard is locked, only activities that
+     *                              can be shown on top of the keyguard will be considered.
+     * @return The top running activity. {@code null} if none is available.
+     */
+    ActivityRecord topRunningActivity(boolean considerKeyguardState) {
+        ActivityRecord topRunning = null;
+        final ActivityStack focusedStack = getFocusedStack();
+        if (focusedStack != null) {
+            topRunning = focusedStack.topRunningActivityLocked();
+        }
+
+        // Look in other focusable stacks.
+        if (topRunning == null) {
+            for (int i = mStacks.size() - 1; i >= 0; --i) {
+                final ActivityStack stack = mStacks.get(i);
+                // Only consider focusable stacks other than the current focused one.
+                if (stack == focusedStack || !stack.isFocusable()) {
+                    continue;
+                }
+                topRunning = stack.topRunningActivityLocked();
+                if (topRunning != null) {
+                    break;
+                }
+            }
+        }
+
+        // This activity can be considered the top running activity if we are not considering
+        // the locked state, the keyguard isn't locked, or we can show when locked.
+        if (topRunning != null && considerKeyguardState
+                && mSupervisor.getKeyguardController().isKeyguardLocked()
+                && !topRunning.canShowWhenLocked()) {
+            return null;
+        }
+
+        return topRunning;
+    }
+
     int getIndexOf(ActivityStack stack) {
         return mStacks.indexOf(stack);
     }
@@ -998,7 +1044,10 @@
      * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
      */
     boolean supportsSystemDecorations() {
-        return mDisplay.supportsSystemDecorations();
+        return mDisplay.supportsSystemDecorations()
+                // TODO (b/111363427): Remove this and set the new FLAG_SHOULD_SHOW_LAUNCHER flag
+                // (b/114338689) whenever vr 2d display id is set.
+                || mDisplayId == mSupervisor.mService.mVr2dDisplayId;
     }
 
     private boolean shouldDestroyContentOnRemove() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b1aea1f..d56b523 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -26,7 +26,6 @@
 import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
-import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
@@ -47,8 +46,6 @@
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
 import static android.os.Process.BLUETOOTH_UID;
 import static android.os.Process.FIRST_APPLICATION_UID;
-import static android.os.Process.FIRST_ISOLATED_UID;
-import static android.os.Process.LAST_ISOLATED_UID;
 import static android.os.Process.NFC_UID;
 import static android.os.Process.PHONE_UID;
 import static android.os.Process.PROC_CHAR;
@@ -67,9 +64,7 @@
 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_BACKGROUND;
 import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
-import static android.os.Process.getFreeMemory;
 import static android.os.Process.getPidsForCommands;
 import static android.os.Process.getTotalMemory;
 import static android.os.Process.getUidForPid;
@@ -84,7 +79,6 @@
 import static android.os.Process.setProcessGroup;
 import static android.os.Process.setThreadPriority;
 import static android.os.Process.setThreadScheduler;
-import static android.os.Process.startWebView;
 import static android.os.Process.zygoteProcess;
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
 import static android.provider.Settings.Global.DEBUG_APP;
@@ -193,6 +187,7 @@
 import android.app.PendingIntent;
 import android.app.ProcessMemoryState;
 import android.app.ProfilerInfo;
+import android.app.WaitResult;
 import android.app.WindowConfiguration.ActivityType;
 import android.app.WindowConfiguration.WindowingMode;
 import android.app.backup.IBackupManager;
@@ -262,7 +257,6 @@
 import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
 import android.os.Process;
-import android.os.Process.ProcessStartResult;
 import android.os.RemoteCallback;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -278,7 +272,6 @@
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.os.storage.StorageManager;
-import android.os.storage.StorageManagerInternal;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -288,7 +281,6 @@
 import android.util.DebugUtils;
 import android.util.EventLog;
 import android.util.Log;
-import android.util.LongSparseArray;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
@@ -324,7 +316,6 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
-import com.android.internal.os.Zygote;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
@@ -355,7 +346,6 @@
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.dex.DexManager;
 import com.android.server.uri.GrantUri;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.utils.PriorityDump;
@@ -404,24 +394,24 @@
      */
     public static final int TOP_APP_PRIORITY_BOOST = -10;
 
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
+    static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
     private 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;
     private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
-    private static final String TAG_LRU = TAG + POSTFIX_LRU;
+    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;
     private static final String TAG_POWER = TAG + POSTFIX_POWER;
     private static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
-    private static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
+    static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
     private static final String TAG_PROVIDER = TAG + POSTFIX_PROVIDER;
-    private static final String TAG_PSS = TAG + POSTFIX_PSS;
+    static final String TAG_PSS = TAG + POSTFIX_PSS;
     private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
-    private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
+    static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
 
     // Mock "pretend we're idle now" broadcast action to the job scheduler; declared
     // here so that while the job scheduler can depend on AMS, the other way around
@@ -455,6 +445,12 @@
     // before we decide it must be hung.
     static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
 
+    /**
+     * How long we wait for an provider to be published. Should be longer than
+     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
+     */
+    static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;
+
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real, when the process was
     // started with a wrapper for instrumentation (such as Valgrind) because it
@@ -483,7 +479,7 @@
     private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
 
     // Necessary ApplicationInfo flags to mark an app as persistent
-    private static final int PERSISTENT_MASK =
+    static final int PERSISTENT_MASK =
             ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
 
     // Intent sent when remote bugreport collection has been completed
@@ -621,46 +617,12 @@
     final ProcessList mProcessList = new ProcessList();
 
     /**
-     * All of the applications we currently have running organized by name.
-     * The keys are strings of the application package name (as
-     * returned by the package manager), and the keys are ApplicationRecord
-     * objects.
-     */
-    final MyProcessMap mProcessNames = new MyProcessMap();
-    final class MyProcessMap extends ProcessMap<ProcessRecord> {
-        @Override
-        public ProcessRecord put(String name, int uid, ProcessRecord value) {
-            final ProcessRecord r = super.put(name, uid, value);
-            mAtmInternal.onProcessAdded(r.getWindowProcessController());
-            return r;
-        }
-
-        @Override
-        public ProcessRecord remove(String name, int uid) {
-            final ProcessRecord r = super.remove(name, uid);
-            mAtmInternal.onProcessRemoved(name, uid);
-            return r;
-        }
-    }
-
-    /**
      * Tracking long-term execution of processes to look for abuse and other
      * bad app behavior.
      */
     final ProcessStatsService mProcessStats;
 
     /**
-     * The currently running isolated processes.
-     */
-    final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
-
-    /**
-     * Counter for assigning isolated process uids, to avoid frequently reusing the
-     * same ones.
-     */
-    int mNextIsolatedProcessUid = 0;
-
-    /**
      * Non-persistent appId whitelist for background restrictions
      */
     int[] mBackgroundAppIdWhitelist = new int[] {
@@ -761,28 +723,6 @@
     final ArrayList<ProcessRecord> mPersistentStartingProcesses = new ArrayList<ProcessRecord>();
 
     /**
-     * Processes that are being forcibly torn down.
-     */
-    final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
-
-    /**
-     * List of running applications, sorted by recent usage.
-     * The first entry in the list is the least recently used.
-     */
-    final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
-
-    /**
-     * Where in mLruProcesses that the processes hosting activities start.
-     */
-    int mLruProcessActivityStart = 0;
-
-    /**
-     * Where in mLruProcesses that the processes hosting services start.
-     * This is after (lower index) than mLruProcessesActivityStart.
-     */
-    int mLruProcessServiceStart = 0;
-
-    /**
      * List of processes that should gc as soon as things are idle.
      */
     final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
@@ -1098,11 +1038,6 @@
     int mAdjSeq = 0;
 
     /**
-     * Current sequence id for process LRU updating.
-     */
-    int mLruSeq = 0;
-
-    /**
      * Keep track of the non-cached/empty process we last found, to help
      * determine how to distribute cached/empty processes next time.
      */
@@ -1214,31 +1149,6 @@
 
     private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
 
-    /**
-     * A global counter for generating sequence numbers.
-     * This value will be used when incrementing sequence numbers in individual uidRecords.
-     *
-     * Having a global counter ensures that seq numbers are monotonically increasing for a
-     * particular uid even when the uidRecord is re-created.
-     */
-    @GuardedBy("this")
-    @VisibleForTesting
-    long mProcStateSeqCounter = 0;
-
-    /**
-     * A global counter for generating sequence numbers to uniquely identify pending process starts.
-     */
-    @GuardedBy("this")
-    private long mProcStartSeqCounter = 0;
-
-    /**
-     * Contains {@link ProcessRecord} objects for pending process starts.
-     *
-     * Mapping: {@link #mProcStartSeqCounter} -> {@link ProcessRecord}
-     */
-    @GuardedBy("this")
-    private final LongSparseArray<ProcessRecord> mPendingStarts = new LongSparseArray<>();
-
     private final Injector mInjector;
 
     static final class ProcessChangeItem {
@@ -1426,9 +1336,6 @@
 
     static final String SERVICE_RECORD_KEY = "servicerecord";
 
-    static ServiceThread sKillThread = null;
-    static KillHandler sKillHandler = null;
-
     long mLastMemUsageReportTime = 0;
 
     /**
@@ -1465,30 +1372,6 @@
         return mActivityTaskManager.getGlobalConfiguration();
     }
 
-    final class KillHandler extends Handler {
-        static final int KILL_PROCESS_GROUP_MSG = 4000;
-
-        public KillHandler(Looper looper) {
-            super(looper, null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case KILL_PROCESS_GROUP_MSG:
-                {
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
-                    Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                }
-                break;
-
-                default:
-                    super.handleMessage(msg);
-            }
-        }
-    }
-
     final class UiHandler extends Handler {
         public UiHandler() {
             super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -1602,8 +1485,8 @@
             } break;
             case UPDATE_TIME_ZONE: {
                 synchronized (ActivityManagerService.this) {
-                    for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                        ProcessRecord r = mLruProcesses.get(i);
+                    for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+                        ProcessRecord r = mProcessList.mLruProcesses.get(i);
                         if (r.thread != null) {
                             try {
                                 r.thread.updateTimeZone();
@@ -1616,16 +1499,7 @@
             } break;
             case CLEAR_DNS_CACHE_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                        ProcessRecord r = mLruProcesses.get(i);
-                        if (r.thread != null) {
-                            try {
-                                r.thread.clearDnsCache();
-                            } catch (RemoteException ex) {
-                                Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
-                            }
-                        }
-                    }
+                    mProcessList.clearAllDnsCacheLocked();
                 }
             } break;
             case UPDATE_HTTP_PROXY_MSG: {
@@ -1641,19 +1515,7 @@
                     pacFileUrl = proxy.getPacFileUrl();
                 }
                 synchronized (ActivityManagerService.this) {
-                    for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                        ProcessRecord r = mLruProcesses.get(i);
-                        // Don't dispatch to isolated processes as they can't access
-                        // ConnectivityManager and don't have network privileges anyway.
-                        if (r.thread != null && !r.isolated) {
-                            try {
-                                r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
-                            } catch (RemoteException ex) {
-                                Slog.w(TAG, "Failed to update http proxy for: " +
-                                        r.info.processName);
-                            }
-                        }
-                    }
+                    mProcessList.setAllHttpProxyLocked(host, port, exclList, pacFileUrl);
                 }
             } break;
             case PROC_START_TIMEOUT_MSG: {
@@ -1701,17 +1563,7 @@
                 // The user's time format preference might have changed.
                 // For convenience we re-use the Intent extra values.
                 synchronized (ActivityManagerService.this) {
-                    for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-                        ProcessRecord r = mLruProcesses.get(i);
-                        if (r.thread != null) {
-                            try {
-                                r.thread.updateTimePrefs(msg.arg1);
-                            } catch (RemoteException ex) {
-                                Slog.w(TAG, "Failed to update preferences for: "
-                                        + r.info.processName);
-                            }
-                        }
-                    }
+                    mProcessList.updateAllTimePrefsLocked(msg.arg1);
                 }
                 break;
             }
@@ -1835,17 +1687,7 @@
             } break;
             case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                        ProcessRecord r = mLruProcesses.get(i);
-                        if (r.thread != null) {
-                            try {
-                                r.thread.handleTrustStorageUpdate();
-                            } catch (RemoteException ex) {
-                                Slog.w(TAG, "Failed to handle trust storage update for: " +
-                                        r.info.processName);
-                            }
-                        }
-                    }
+                    mProcessList.handleAllTrustStorageUpdateLocked();
                 }
             } break;
             }
@@ -1985,7 +1827,9 @@
             mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
 
             synchronized (this) {
-                ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
+                ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,
+                        false,
+                        0);
                 app.setPersistent(true);
                 app.pid = MY_PID;
                 app.getWindowProcessController().setPid(MY_PID);
@@ -1994,7 +1838,7 @@
                 synchronized (mPidsSelfLocked) {
                     mPidsSelfLocked.put(app.pid, app);
                 }
-                updateLruProcessLocked(app, false, null);
+                mProcessList.updateLruProcessLocked(app, false, null);
                 updateOomAdjLocked();
             }
         } catch (PackageManager.NameNotFoundException e) {
@@ -2262,24 +2106,38 @@
 
     @VisibleForTesting
     public ActivityManagerService(Injector injector) {
+        this(injector, null /* handlerThread */);
+    }
+
+    /**
+     * Provides the basic functionality for activity task related tests when a handler thread is
+     * given to initialize the dependency members.
+     */
+    @VisibleForTesting
+    ActivityManagerService(Injector injector, ServiceThread handlerThread) {
+        final boolean hasHandlerThread = handlerThread != null;
         mInjector = injector;
         mContext = mInjector.getContext();
         mUiContext = null;
         mAppErrors = null;
-        mAppOpsService = mInjector.getAppOpsService(null, null);
+        mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */);
         mBatteryStatsService = null;
-        mConstants = null;
-        mHandler = null;
-        mHandlerThread = null;
-        mIntentFirewall = null;
+        mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null;
+        mHandlerThread = handlerThread;
+        mConstants = hasHandlerThread ? new ActivityManagerConstants(this, mHandler) : null;
+        mIntentFirewall = hasHandlerThread
+                ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
         mProcessCpuThread = null;
         mProcessStats = null;
         mProviderMap = null;
-        mServices = null;
+        // For the usage of {@link ActiveServices#cleanUpServices} that may be invoked from
+        // {@link ActivityStackSupervisor#cleanUpRemovedTaskLocked}.
+        mServices = hasHandlerThread ? new ActiveServices(this) : null;
         mSystemThread = null;
-        mUiHandler = injector.getUiHandler(null);
-        mUserController = null;
-        mPendingIntentController = null;
+        mUiHandler = injector.getUiHandler(null /* service */);
+        mUserController = hasHandlerThread ? new UserController(this) : null;
+        mPendingIntentController = hasHandlerThread
+                ? new PendingIntentController(handlerThread.getLooper(), mUserController) : null;
         mProcStartHandlerThread = null;
         mProcStartHandler = null;
         mHiddenApiBlacklist = null;
@@ -2312,13 +2170,7 @@
 
         mConstants = new ActivityManagerConstants(this, mHandler);
 
-        /* static; one-time init here */
-        if (sKillHandler == null) {
-            sKillThread = new ServiceThread(TAG + ":kill",
-                    THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
-            sKillThread.start();
-            sKillHandler = new KillHandler(sKillThread.getLooper());
-        }
+        mProcessList.init(this);
 
         mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                 "foreground", BROADCAST_FG_TIMEOUT, false);
@@ -2468,12 +2320,13 @@
         if (code == SYSPROPS_TRANSACTION) {
             // We need to tell all apps about the system property change.
             ArrayList<IBinder> procs = new ArrayList<IBinder>();
-            synchronized(this) {
-                final int NP = mProcessNames.getMap().size();
-                for (int ip=0; ip<NP; ip++) {
-                    SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+            synchronized (this) {
+                final int NP = mProcessList.mProcessNames.getMap().size();
+                for (int ip = 0; ip < NP; ip++) {
+                    SparseArray<ProcessRecord> apps =
+                            mProcessList.mProcessNames.getMap().valueAt(ip);
                     final int NA = apps.size();
-                    for (int ia=0; ia<NA; ia++) {
+                    for (int ia = 0; ia < NA; ia++) {
                         ProcessRecord app = apps.valueAt(ia);
                         if (app.thread != null) {
                             procs.add(app.thread.asBinder());
@@ -2723,329 +2576,21 @@
         mActivityTaskManager.unregisterTaskStackListener(listener);
     }
 
-    private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
-            String what, Object obj, ProcessRecord srcApp) {
-        app.lastActivityTime = now;
-
-        if (app.hasActivitiesOrRecentTasks()) {
-            // Don't want to touch dependent processes that are hosting activities.
-            return index;
-        }
-
-        int lrui = mLruProcesses.lastIndexOf(app);
-        if (lrui < 0) {
-            Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
-                    + what + " " + obj + " from " + srcApp);
-            return index;
-        }
-
-        if (lrui >= index) {
-            // Don't want to cause this to move dependent processes *back* in the
-            // list as if they were less frequently used.
-            return index;
-        }
-
-        if (lrui >= mLruProcessActivityStart) {
-            // Don't want to touch dependent processes that are hosting activities.
-            return index;
-        }
-
-        mLruProcesses.remove(lrui);
-        if (index > 0) {
-            index--;
-        }
-        if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
-                + " in LRU list: " + app);
-        mLruProcesses.add(index, app);
-        return index;
-    }
-
-    static void killProcessGroup(int uid, int pid) {
-        if (sKillHandler != null) {
-            sKillHandler.sendMessage(
-                    sKillHandler.obtainMessage(KillHandler.KILL_PROCESS_GROUP_MSG, uid, pid));
-        } else {
-            Slog.w(TAG, "Asked to kill process group before system bringup!");
-            Process.killProcessGroup(uid, pid);
-        }
+    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
+            ProcessRecord client) {
+        mProcessList.updateLruProcessLocked(app, activityChange, client);
     }
 
     final void removeLruProcessLocked(ProcessRecord app) {
-        int lrui = mLruProcesses.lastIndexOf(app);
-        if (lrui >= 0) {
-            if (!app.killed) {
-                if (app.isPersistent()) {
-                    Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
-                } else {
-                    Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
-                    if (app.pid > 0) {
-                        killProcessQuiet(app.pid);
-                        killProcessGroup(app.uid, app.pid);
-                    } else {
-                        app.pendingStart = false;
-                    }
-                }
-            }
-            if (lrui <= mLruProcessActivityStart) {
-                mLruProcessActivityStart--;
-            }
-            if (lrui <= mLruProcessServiceStart) {
-                mLruProcessServiceStart--;
-            }
-            mLruProcesses.remove(lrui);
-        }
-    }
-
-    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
-            ProcessRecord client) {
-        final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities()
-                || app.treatLikeActivity;
-        final boolean hasService = false; // not impl yet. app.services.size() > 0;
-        if (!activityChange && hasActivity) {
-            // The process has activities, so we are only allowing activity-based adjustments
-            // to move it.  It should be kept in the front of the list with other
-            // processes that have activities, and we don't want those to change their
-            // order except due to activity operations.
-            return;
-        }
-
-        mLruSeq++;
-        final long now = SystemClock.uptimeMillis();
-        app.lastActivityTime = now;
-
-        // First a quick reject: if the app is already at the position we will
-        // put it, then there is nothing to do.
-        if (hasActivity) {
-            final int N = mLruProcesses.size();
-            if (N > 0 && mLruProcesses.get(N-1) == app) {
-                if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
-                return;
-            }
-        } else {
-            if (mLruProcessServiceStart > 0
-                    && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
-                if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
-                return;
-            }
-        }
-
-        int lrui = mLruProcesses.lastIndexOf(app);
-
-        if (app.isPersistent() && lrui >= 0) {
-            // We don't care about the position of persistent processes, as long as
-            // they are in the list.
-            if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
-            return;
-        }
-
-        /* In progress: compute new position first, so we can avoid doing work
-           if the process is not actually going to move.  Not yet working.
-        int addIndex;
-        int nextIndex;
-        boolean inActivity = false, inService = false;
-        if (hasActivity) {
-            // Process has activities, put it at the very tipsy-top.
-            addIndex = mLruProcesses.size();
-            nextIndex = mLruProcessServiceStart;
-            inActivity = true;
-        } else if (hasService) {
-            // Process has services, put it at the top of the service list.
-            addIndex = mLruProcessActivityStart;
-            nextIndex = mLruProcessServiceStart;
-            inActivity = true;
-            inService = true;
-        } else  {
-            // Process not otherwise of interest, it goes to the top of the non-service area.
-            addIndex = mLruProcessServiceStart;
-            if (client != null) {
-                int clientIndex = mLruProcesses.lastIndexOf(client);
-                if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating "
-                        + app);
-                if (clientIndex >= 0 && addIndex > clientIndex) {
-                    addIndex = clientIndex;
-                }
-            }
-            nextIndex = addIndex > 0 ? addIndex-1 : addIndex;
-        }
-
-        Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act="
-                + mLruProcessActivityStart + "): " + app);
-        */
-
-        if (lrui >= 0) {
-            if (lrui < mLruProcessActivityStart) {
-                mLruProcessActivityStart--;
-            }
-            if (lrui < mLruProcessServiceStart) {
-                mLruProcessServiceStart--;
-            }
-            /*
-            if (addIndex > lrui) {
-                addIndex--;
-            }
-            if (nextIndex > lrui) {
-                nextIndex--;
-            }
-            */
-            mLruProcesses.remove(lrui);
-        }
-
-        /*
-        mLruProcesses.add(addIndex, app);
-        if (inActivity) {
-            mLruProcessActivityStart++;
-        }
-        if (inService) {
-            mLruProcessActivityStart++;
-        }
-        */
-
-        int nextIndex;
-        if (hasActivity) {
-            final int N = mLruProcesses.size();
-            if ((!app.hasActivities() || app.hasRecentTasks())
-                    && mLruProcessActivityStart < (N - 1)) {
-                // Process doesn't have activities, but has clients with
-                // activities...  move it up, but one below the top (the top
-                // should always have a real activity).
-                if (DEBUG_LRU) Slog.d(TAG_LRU,
-                        "Adding to second-top of LRU activity list: " + app);
-                mLruProcesses.add(N - 1, app);
-                // To keep it from spamming the LRU list (by making a bunch of clients),
-                // we will push down any other entries owned by the app.
-                final int uid = app.info.uid;
-                for (int i = N - 2; i > mLruProcessActivityStart; i--) {
-                    ProcessRecord subProc = mLruProcesses.get(i);
-                    if (subProc.info.uid == uid) {
-                        // We want to push this one down the list.  If the process after
-                        // it is for the same uid, however, don't do so, because we don't
-                        // want them internally to be re-ordered.
-                        if (mLruProcesses.get(i - 1).info.uid != uid) {
-                            if (DEBUG_LRU) Slog.d(TAG_LRU,
-                                    "Pushing uid " + uid + " swapping at " + i + ": "
-                                    + mLruProcesses.get(i) + " : " + mLruProcesses.get(i - 1));
-                            ProcessRecord tmp = mLruProcesses.get(i);
-                            mLruProcesses.set(i, mLruProcesses.get(i - 1));
-                            mLruProcesses.set(i - 1, tmp);
-                            i--;
-                        }
-                    } else {
-                        // A gap, we can stop here.
-                        break;
-                    }
-                }
-            } else {
-                // Process has activities, put it at the very tipsy-top.
-                if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
-                mLruProcesses.add(app);
-            }
-            nextIndex = mLruProcessServiceStart;
-        } else if (hasService) {
-            // Process has services, put it at the top of the service list.
-            if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
-            mLruProcesses.add(mLruProcessActivityStart, app);
-            nextIndex = mLruProcessServiceStart;
-            mLruProcessActivityStart++;
-        } else  {
-            // Process not otherwise of interest, it goes to the top of the non-service area.
-            int index = mLruProcessServiceStart;
-            if (client != null) {
-                // If there is a client, don't allow the process to be moved up higher
-                // in the list than that client.
-                int clientIndex = mLruProcesses.lastIndexOf(client);
-                if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client
-                        + " when updating " + app);
-                if (clientIndex <= lrui) {
-                    // Don't allow the client index restriction to push it down farther in the
-                    // list than it already is.
-                    clientIndex = lrui;
-                }
-                if (clientIndex >= 0 && index > clientIndex) {
-                    index = clientIndex;
-                }
-            }
-            if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
-            mLruProcesses.add(index, app);
-            nextIndex = index-1;
-            mLruProcessActivityStart++;
-            mLruProcessServiceStart++;
-        }
-
-        // If the app is currently using a content provider or service,
-        // bump those processes as well.
-        for (int j=app.connections.size()-1; j>=0; j--) {
-            ConnectionRecord cr = app.connections.valueAt(j);
-            if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
-                    && cr.binding.service.app != null
-                    && cr.binding.service.app.lruSeq != mLruSeq
-                    && !cr.binding.service.app.isPersistent()) {
-                nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
-                        "service connection", cr, app);
-            }
-        }
-        for (int j=app.conProviders.size()-1; j>=0; j--) {
-            ContentProviderRecord cpr = app.conProviders.get(j).provider;
-            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
-                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
-                        "provider reference", cpr, app);
-            }
-        }
+        mProcessList.removeLruProcessLocked(app);
     }
 
     final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
-        if (uid == SYSTEM_UID) {
-            // The system gets to run in any process.  If there are multiple
-            // processes with the same uid, just pick the first (this
-            // should never happen).
-            SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName);
-            if (procs == null) return null;
-            final int procCount = procs.size();
-            for (int i = 0; i < procCount; i++) {
-                final int procUid = procs.keyAt(i);
-                if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
-                    // Don't use an app process or different user process for system component.
-                    continue;
-                }
-                return procs.valueAt(i);
-            }
-        }
-        ProcessRecord proc = mProcessNames.get(processName, uid);
-        if (false && proc != null && !keepIfLarge
-                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
-                && proc.lastCachedPss >= 4000) {
-            // Turn this condition on to cause killing to happen regularly, for testing.
-            if (proc.baseProcessTracker != null) {
-                proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
-                for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                    ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
-                    StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
-                            proc.info.uid,
-                            holder.state.getName(),
-                            holder.state.getPackage(),
-                            proc.lastCachedPss, holder.appVersion);
-                }
-            }
-            proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
-        } else if (proc != null && !keepIfLarge
-                && mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
-                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
-            if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
-            if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
-                if (proc.baseProcessTracker != null) {
-                    proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
-                    for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                        ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
-                        StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
-                                proc.info.uid,
-                                holder.state.getName(),
-                                holder.state.getPackage(),
-                                proc.lastCachedPss, holder.appVersion);
-                    }
-                }
-                proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
-            }
-        }
-        return proc;
+        return mProcessList.getProcessRecordLocked(processName, uid, keepIfLarge);
+    }
+
+    final ProcessMap<ProcessRecord> getProcessNames() {
+        return mProcessList.mProcessNames;
     }
 
     void notifyPackageUse(String packageName, int reason) {
@@ -3072,7 +2617,7 @@
             info.packageName = "android";
             info.seInfoUser = SELinuxUtil.COMPLETE_STR;
             info.targetSdkVersion = Build.VERSION.SDK_INT;
-            ProcessRecord proc = startProcessLocked(processName, info /* info */,
+            ProcessRecord proc = mProcessList.startProcessLocked(processName, info /* info */,
                     false /* knownToBeDead */, 0 /* intentFlags */, ""  /* hostingType */,
                     null /* hostingName */, true /* allowWhileBooting */, true /* isolated */,
                     uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
@@ -3086,548 +2631,17 @@
             ApplicationInfo info, boolean knownToBeDead, int intentFlags,
             String hostingType, ComponentName hostingName, boolean allowWhileBooting,
             boolean isolated, boolean keepIfLarge) {
-        return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
+        return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
+                hostingType,
                 hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
                 null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
                 null /* crashHandler */);
     }
 
-    @GuardedBy("this")
-    final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
-            boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
-            boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
-            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
-        long startTime = SystemClock.elapsedRealtime();
-        ProcessRecord app;
-        if (!isolated) {
-            app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
-            checkTime(startTime, "startProcess: after getProcessRecord");
-
-            if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
-                // If we are in the background, then check to see if this process
-                // is bad.  If so, we will just silently fail.
-                if (mAppErrors.isBadProcessLocked(info)) {
-                    if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
-                            + "/" + info.processName);
-                    return null;
-                }
-            } else {
-                // When the user is explicitly starting a process, then clear its
-                // crash count so that we won't make it bad until they see at
-                // least one crash dialog again, and make the process good again
-                // if it had been bad.
-                if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
-                        + "/" + info.processName);
-                mAppErrors.resetProcessCrashTimeLocked(info);
-                if (mAppErrors.isBadProcessLocked(info)) {
-                    EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
-                            UserHandle.getUserId(info.uid), info.uid,
-                            info.processName);
-                    mAppErrors.clearBadProcessLocked(info);
-                    if (app != null) {
-                        app.bad = false;
-                    }
-                }
-            }
-        } else {
-            // If this is an isolated process, it can't re-use an existing process.
-            app = null;
-        }
-
-        // We don't have to do anything more if:
-        // (1) There is an existing application record; and
-        // (2) The caller doesn't think it is dead, OR there is no thread
-        //     object attached to it so we know it couldn't have crashed; and
-        // (3) There is a pid assigned to it, so it is either starting or
-        //     already running.
-        if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
-                + " app=" + app + " knownToBeDead=" + knownToBeDead
-                + " thread=" + (app != null ? app.thread : null)
-                + " pid=" + (app != null ? app.pid : -1));
-        if (app != null && app.pid > 0) {
-            if ((!knownToBeDead && !app.killed) || app.thread == null) {
-                // We already have the app running, or are waiting for it to
-                // come up (we have a pid but not yet its thread), so keep it.
-                if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
-                // If this is a new package in the process, add the package to the list
-                app.addPackage(info.packageName, info.versionCode, mProcessStats);
-                checkTime(startTime, "startProcess: done, added package to proc");
-                return app;
-            }
-
-            // An application record is attached to a previous process,
-            // clean it up now.
-            if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_PROCESSES, "App died: " + app);
-            checkTime(startTime, "startProcess: bad proc running, killing");
-            killProcessGroup(app.uid, app.pid);
-            handleAppDiedLocked(app, true, true);
-            checkTime(startTime, "startProcess: done killing old proc");
-        }
-
-        String hostingNameStr = hostingName != null
-                ? hostingName.flattenToShortString() : null;
-
-        if (app == null) {
-            checkTime(startTime, "startProcess: creating new process record");
-            app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
-            if (app == null) {
-                Slog.w(TAG, "Failed making new process record for "
-                        + processName + "/" + info.uid + " isolated=" + isolated);
-                return null;
-            }
-            app.crashHandler = crashHandler;
-            app.isolatedEntryPoint = entryPoint;
-            app.isolatedEntryPointArgs = entryPointArgs;
-            checkTime(startTime, "startProcess: done creating new process record");
-        } else {
-            // If this is a new package in the process, add the package to the list
-            app.addPackage(info.packageName, info.versionCode, mProcessStats);
-            checkTime(startTime, "startProcess: added package to existing proc");
-        }
-
-        // If the system is not ready yet, then hold off on starting this
-        // process until it is.
-        if (!mProcessesReady
-                && !isAllowedWhileBooting(info)
-                && !allowWhileBooting) {
-            if (!mProcessesOnHold.contains(app)) {
-                mProcessesOnHold.add(app);
-            }
-            if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
-                    "System not ready, putting on hold: " + app);
-            checkTime(startTime, "startProcess: returning with proc on hold");
-            return app;
-        }
-
-        checkTime(startTime, "startProcess: stepping in to startProcess");
-        final boolean success = startProcessLocked(app, hostingType, hostingNameStr, abiOverride);
-        checkTime(startTime, "startProcess: done starting proc!");
-        return success ? app : null;
-    }
-
     boolean isAllowedWhileBooting(ApplicationInfo ai) {
         return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
     }
 
-    @GuardedBy("this")
-    private final void startProcessLocked(ProcessRecord app,
-            String hostingType, String hostingNameStr) {
-        startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
-    }
-
-    @GuardedBy("this")
-    private final boolean startProcessLocked(ProcessRecord app,
-            String hostingType, String hostingNameStr, String abiOverride) {
-        return startProcessLocked(app, hostingType, hostingNameStr,
-                false /* disableHiddenApiChecks */, abiOverride);
-    }
-
-    /**
-     * @return {@code true} if process start is successful, false otherwise.
-     */
-    @GuardedBy("this")
-    private final boolean startProcessLocked(ProcessRecord app, String hostingType,
-            String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
-        if (app.pendingStart) {
-            return true;
-        }
-        long startTime = SystemClock.elapsedRealtime();
-        if (app.pid > 0 && app.pid != MY_PID) {
-            checkTime(startTime, "startProcess: removing from pids map");
-            synchronized (mPidsSelfLocked) {
-                mPidsSelfLocked.remove(app.pid);
-                mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
-            }
-            checkTime(startTime, "startProcess: done removing from pids map");
-            app.setPid(0);
-        }
-
-        if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
-                "startProcessLocked removing on hold: " + app);
-        mProcessesOnHold.remove(app);
-
-        checkTime(startTime, "startProcess: starting to update cpu stats");
-        updateCpuStats();
-        checkTime(startTime, "startProcess: done updating cpu stats");
-
-        try {
-            try {
-                final int userId = UserHandle.getUserId(app.uid);
-                AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
-            } catch (RemoteException e) {
-                throw e.rethrowAsRuntimeException();
-            }
-
-            int uid = app.uid;
-            int[] gids = null;
-            int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
-            if (!app.isolated) {
-                int[] permGids = null;
-                try {
-                    checkTime(startTime, "startProcess: getting gids from package manager");
-                    final IPackageManager pm = AppGlobals.getPackageManager();
-                    permGids = pm.getPackageGids(app.info.packageName,
-                            MATCH_DEBUG_TRIAGED_MISSING, app.userId);
-                    StorageManagerInternal storageManagerInternal = LocalServices.getService(
-                            StorageManagerInternal.class);
-                    mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
-                            app.info.packageName);
-                } catch (RemoteException e) {
-                    throw e.rethrowAsRuntimeException();
-                }
-
-                /*
-                 * Add shared application and profile GIDs so applications can share some
-                 * resources like shared libraries and access user-wide resources
-                 */
-                if (ArrayUtils.isEmpty(permGids)) {
-                    gids = new int[3];
-                } else {
-                    gids = new int[permGids.length + 3];
-                    System.arraycopy(permGids, 0, gids, 3, permGids.length);
-                }
-                gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
-                gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
-                gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
-
-                // Replace any invalid GIDs
-                if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
-                if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
-            }
-            checkTime(startTime, "startProcess: building args");
-            if (mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
-                uid = 0;
-            }
-            int runtimeFlags = 0;
-            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
-                runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
-                runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
-                // Also turn on CheckJNI for debuggable apps. It's quite
-                // awkward to turn on otherwise.
-                runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
-            }
-            // Run the app in safe mode if its manifest requests so or the
-            // system is booted in safe mode.
-            if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
-                mSafeMode == true) {
-                runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
-            }
-            if ("1".equals(SystemProperties.get("debug.checkjni"))) {
-                runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
-            }
-            String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
-            if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
-                runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
-            }
-            String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
-            if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
-                runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
-            }
-            if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
-                runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
-            }
-            if ("1".equals(SystemProperties.get("debug.assert"))) {
-                runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;
-            }
-            if (mNativeDebuggingApp != null && mNativeDebuggingApp.equals(app.processName)) {
-                // Enable all debug flags required by the native debugger.
-                runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT;          // Don't interpret anything
-                runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
-                runtimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;   // Disbale optimizations
-                mNativeDebuggingApp = null;
-            }
-
-            if (app.info.isPrivilegedApp() &&
-                    DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
-                runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
-            }
-
-            if (!disableHiddenApiChecks && !mHiddenApiBlacklist.isDisabled()) {
-                app.info.maybeUpdateHiddenApiEnforcementPolicy(
-                        mHiddenApiBlacklist.getPolicyForPrePApps(),
-                        mHiddenApiBlacklist.getPolicyForPApps());
-                @HiddenApiEnforcementPolicy int policy =
-                        app.info.getHiddenApiEnforcementPolicy();
-                int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
-                if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
-                    throw new IllegalStateException("Invalid API policy: " + policy);
-                }
-                runtimeFlags |= policyBits;
-            }
-
-            String invokeWith = null;
-            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
-                // Debuggable apps may include a wrapper script with their library directory.
-                String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
-                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-                try {
-                    if (new File(wrapperFileName).exists()) {
-                        invokeWith = "/system/bin/logwrapper " + wrapperFileName;
-                    }
-                } finally {
-                    StrictMode.setThreadPolicy(oldPolicy);
-                }
-            }
-
-            String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
-            if (requiredAbi == null) {
-                requiredAbi = Build.SUPPORTED_ABIS[0];
-            }
-
-            String instructionSet = null;
-            if (app.info.primaryCpuAbi != null) {
-                instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
-            }
-
-            app.gids = gids;
-            app.setRequiredAbi(requiredAbi);
-            app.instructionSet = instructionSet;
-
-            // the per-user SELinux context must be set
-            if (TextUtils.isEmpty(app.info.seInfoUser)) {
-                Slog.wtf(TAG, "SELinux tag not defined",
-                        new IllegalStateException("SELinux tag not defined for "
-                        + app.info.packageName + " (uid " + app.uid + ")"));
-            }
-            final String seInfo = app.info.seInfo
-                    + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
-            // Start the process.  It will either succeed and return a result containing
-            // the PID of the new process, or else throw a RuntimeException.
-            final String entryPoint = "android.app.ActivityThread";
-
-            return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
-                    runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
-                    startTime);
-        } catch (RuntimeException e) {
-            Slog.e(TAG, "Failure starting process " + app.processName, e);
-
-            // Something went very wrong while trying to start this process; one
-            // common case is when the package is frozen due to an active
-            // upgrade. To recover, clean up any active bookkeeping related to
-            // starting this process. (We already invoked this method once when
-            // the package was initially frozen through KILL_APPLICATION_MSG, so
-            // it doesn't hurt to use it again.)
-            forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid), false,
-                    false, true, false, false, app.userId, "start failure");
-            return false;
-        }
-    }
-
-    @GuardedBy("this")
-    private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
-            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
-            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
-            long startTime) {
-        app.pendingStart = true;
-        app.killedByAm = false;
-        app.removed = false;
-        app.killed = false;
-        final long startSeq = app.startSeq = ++mProcStartSeqCounter;
-        app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
-        if (mConstants.FLAG_PROCESS_START_ASYNC) {
-            if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
-                    "Posting procStart msg for " + app.toShortString());
-            mProcStartHandler.post(() -> {
-                try {
-                    synchronized (ActivityManagerService.this) {
-                        final String reason = isProcStartValidLocked(app, startSeq);
-                        if (reason != null) {
-                            Slog.w(TAG_PROCESSES, app + " not valid anymore,"
-                                    + " don't start process, " + reason);
-                            app.pendingStart = false;
-                            return;
-                        }
-                        app.setUsingWrapper(invokeWith != null
-                                || SystemProperties.get("wrap." + app.processName) != null);
-                        mPendingStarts.put(startSeq, app);
-                    }
-                    final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,
-                            app, app.startUid, gids, runtimeFlags, mountExternal, app.seInfo,
-                            requiredAbi, instructionSet, invokeWith, app.startTime);
-                    synchronized (ActivityManagerService.this) {
-                        handleProcessStartedLocked(app, startResult, startSeq);
-                    }
-                } catch (RuntimeException e) {
-                    synchronized (ActivityManagerService.this) {
-                        Slog.e(TAG, "Failure starting process " + app.processName, e);
-                        mPendingStarts.remove(startSeq);
-                        app.pendingStart = false;
-                        forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
-                                false, false, true, false, false, app.userId, "start failure");
-                    }
-                }
-            });
-            return true;
-        } else {
-            try {
-                final ProcessStartResult startResult = startProcess(hostingType, entryPoint, app,
-                        uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
-                        invokeWith, startTime);
-                handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
-                        startSeq, false);
-            } catch (RuntimeException e) {
-                Slog.e(TAG, "Failure starting process " + app.processName, e);
-                app.pendingStart = false;
-                forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
-                        false, false, true, false, false, app.userId, "start failure");
-            }
-            return app.pid > 0;
-        }
-    }
-
-    private ProcessStartResult startProcess(String hostingType, String entryPoint,
-            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
-            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
-            long startTime) {
-        try {
-            final String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
-            final String[] visibleVolIds = LocalServices.getService(StorageManagerInternal.class)
-                    .getVisibleVolumesForUser(UserHandle.getUserId(uid));
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
-                    app.processName);
-            checkTime(startTime, "startProcess: asking zygote to start proc");
-            final ProcessStartResult startResult;
-            if (hostingType.equals("webview_service")) {
-                startResult = startWebView(entryPoint,
-                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
-                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
-                        app.info.dataDir, null, app.info.packageName,
-                        packageNames, visibleVolIds,
-                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
-            } else {
-                startResult = Process.start(entryPoint,
-                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
-                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
-                        app.info.dataDir, invokeWith, app.info.packageName,
-                        packageNames, visibleVolIds,
-                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
-            }
-            checkTime(startTime, "startProcess: returned from zygote!");
-            return startResult;
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    @GuardedBy("this")
-    private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
-        StringBuilder sb = null;
-        if (app.killedByAm) {
-            if (sb == null) sb = new StringBuilder();
-            sb.append("killedByAm=true;");
-        }
-        if (mProcessNames.get(app.processName, app.uid) != app) {
-            if (sb == null) sb = new StringBuilder();
-            sb.append("No entry in mProcessNames;");
-        }
-        if (!app.pendingStart) {
-            if (sb == null) sb = new StringBuilder();
-            sb.append("pendingStart=false;");
-        }
-        if (app.startSeq > expectedStartSeq) {
-            if (sb == null) sb = new StringBuilder();
-            sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
-        }
-        return sb == null ? null : sb.toString();
-    }
-
-    @GuardedBy("this")
-    private boolean handleProcessStartedLocked(ProcessRecord pending,
-            ProcessStartResult startResult, long expectedStartSeq) {
-        // Indicates that this process start has been taken care of.
-        if (mPendingStarts.get(expectedStartSeq) == null) {
-            if (pending.pid == startResult.pid) {
-                pending.setUsingWrapper(startResult.usingWrapper);
-                // TODO: Update already existing clients of usingWrapper
-            }
-            return false;
-        }
-        return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper,
-                expectedStartSeq, false);
-    }
-
-    @GuardedBy("this")
-    private boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
-            long expectedStartSeq, boolean procAttached) {
-        mPendingStarts.remove(expectedStartSeq);
-        final String reason = isProcStartValidLocked(app, expectedStartSeq);
-        if (reason != null) {
-            Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" + pid
-                    + ", " + reason);
-            app.pendingStart = false;
-            Process.killProcessQuiet(pid);
-            Process.killProcessGroup(app.uid, app.pid);
-            return false;
-        }
-        mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
-        checkTime(app.startTime, "startProcess: done updating battery stats");
-
-        EventLog.writeEvent(EventLogTags.AM_PROC_START,
-                UserHandle.getUserId(app.startUid), pid, app.startUid,
-                app.processName, app.hostingType,
-                app.hostingNameStr != null ? app.hostingNameStr : "");
-
-        try {
-            AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
-                    app.seInfo, app.info.sourceDir, pid);
-        } catch (RemoteException ex) {
-            // Ignore
-        }
-
-        if (app.isPersistent()) {
-            Watchdog.getInstance().processStarted(app.processName, pid);
-        }
-
-        checkTime(app.startTime, "startProcess: building log message");
-        StringBuilder buf = mStringBuilder;
-        buf.setLength(0);
-        buf.append("Start proc ");
-        buf.append(pid);
-        buf.append(':');
-        buf.append(app.processName);
-        buf.append('/');
-        UserHandle.formatUid(buf, app.startUid);
-        if (app.isolatedEntryPoint != null) {
-            buf.append(" [");
-            buf.append(app.isolatedEntryPoint);
-            buf.append("]");
-        }
-        buf.append(" for ");
-        buf.append(app.hostingType);
-        if (app.hostingNameStr != null) {
-            buf.append(" ");
-            buf.append(app.hostingNameStr);
-        }
-        reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
-        app.setPid(pid);
-        app.setUsingWrapper(usingWrapper);
-        app.pendingStart = false;
-        checkTime(app.startTime, "startProcess: starting to update pids map");
-        ProcessRecord oldApp;
-        synchronized (mPidsSelfLocked) {
-            oldApp = mPidsSelfLocked.get(pid);
-        }
-        // If there is already an app occupying that pid that hasn't been cleaned up
-        if (oldApp != null && !app.isolated) {
-            // Clean up anything relating to this pid first
-            Slog.w(TAG, "Reusing pid " + pid
-                    + " while app is still mapped to it");
-            cleanUpApplicationRecordLocked(oldApp, false, false, -1,
-                    true /*replacingPid*/);
-        }
-        synchronized (mPidsSelfLocked) {
-            this.mPidsSelfLocked.put(pid, app);
-            if (!procAttached) {
-                Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
-                msg.obj = app;
-                mHandler.sendMessageDelayed(msg, usingWrapper
-                        ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
-            }
-        }
-        checkTime(app.startTime, "startProcess: done updating pids map");
-        return true;
-    }
-
     void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
         if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
                 "updateUsageStats: comp=" + activity + "res=" + resumed);
@@ -3691,8 +2705,8 @@
 
         int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
         synchronized (this) {
-            for (int i=mLruProcesses.size()-1; i>=0; i--) {
-                final ProcessRecord proc = mLruProcesses.get(i);
+            for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
+                final ProcessRecord proc = mProcessList.mLruProcesses.get(i);
                 if (procState > proc.setProcState) {
                     if (proc.pkgList.containsKey(packageName) ||
                             (proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) {
@@ -3986,9 +3000,35 @@
     public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
-        return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
-                resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
-                userId);
+        synchronized (this) {
+            /**
+             * Flags like {@link android.app.ActivityManager#START_FLAG_DEBUG} maybe be set on this
+             * call when called/invoked from the shell command. To avoid deadlock, we go ahead and
+             * acquire the AMS lock now since ATMS will need to synchronously call back into AMS
+             * later to modify process settings due to the flags.
+             * TODO(b/80414790): Investigate a better way of untangling this.
+             */
+            return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
+                    resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
+                    bOptions, userId);
+        }
+    }
+
+    WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        synchronized (this) {
+            /**
+             * Flags like {@link android.app.ActivityManager#START_FLAG_DEBUG} maybe be set on this
+             * call when called/invoked from the shell command. To avoid deadlock, we go ahead and
+             * acquire the AMS lock now since ATMS will need to synchronously call back into AMS
+             * later to modify process settings due to the flags.
+             * TODO(b/80414790): Investigate a better way of untangling this.
+             */
+            return mActivityTaskManager.startActivityAndWait(caller, callingPackage, intent,
+                    resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
+                    bOptions, userId);
+        }
     }
 
     @Override
@@ -4067,7 +3107,7 @@
      * to the process.
      */
     @GuardedBy("this")
-    private final void handleAppDiedLocked(ProcessRecord app,
+    final void handleAppDiedLocked(ProcessRecord app,
             boolean restarting, boolean allowRestart) {
         int pid = app.pid;
         boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
@@ -4092,32 +3132,19 @@
         });
     }
 
-    private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
-        final IBinder threadBinder = thread.asBinder();
-        // Find the application record.
-        for (int i=mLruProcesses.size()-1; i>=0; i--) {
-            final ProcessRecord rec = mLruProcesses.get(i);
-            if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
     ProcessRecord getRecordForAppLocked(IApplicationThread thread) {
         if (thread == null) {
             return null;
         }
 
-        int appIndex = getLRURecordIndexForAppLocked(thread);
-        if (appIndex >= 0) {
-            return mLruProcesses.get(appIndex);
-        }
+        ProcessRecord record = mProcessList.getLRURecordForAppLocked(thread);
+        if (record != null) return record;
 
         // Validation: if it isn't in the LRU list, it shouldn't exist, but let's
         // double-check that.
         final IBinder threadBinder = thread.asBinder();
-        final ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
+        final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
+                mProcessList.mProcessNames.getMap();
         for (int i = pmap.size()-1; i >= 0; i--) {
             final SparseArray<ProcessRecord> procs = pmap.valueAt(i);
             for (int j = procs.size()-1; j >= 0; j--) {
@@ -4137,17 +3164,7 @@
         // If there are no longer any background processes running,
         // and the app that died was not running instrumentation,
         // then tell everyone we are now low on memory.
-        boolean haveBg = false;
-        for (int i=mLruProcesses.size()-1; i>=0; i--) {
-            ProcessRecord rec = mLruProcesses.get(i);
-            if (rec.thread != null
-                    && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
-                haveBg = true;
-                break;
-            }
-        }
-
-        if (!haveBg) {
+        if (!mProcessList.haveBackgroundProcessLocked()) {
             boolean doReport = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
             if (doReport) {
                 long now = SystemClock.uptimeMillis();
@@ -4158,11 +3175,12 @@
                 }
             }
             final ArrayList<ProcessMemInfo> memInfos
-                    = doReport ? new ArrayList<ProcessMemInfo>(mLruProcesses.size()) : null;
-            EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size());
+                    = doReport ? new ArrayList<ProcessMemInfo>(mProcessList.getLruSizeLocked())
+                    : null;
+            EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mProcessList.getLruSizeLocked());
             long now = SystemClock.uptimeMillis();
-            for (int i=mLruProcesses.size()-1; i>=0; i--) {
-                ProcessRecord rec = mLruProcesses.get(i);
+            for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+                ProcessRecord rec = mProcessList.mLruProcesses.get(i);
                 if (rec == dyingProc || rec.thread == null) {
                     continue;
                 }
@@ -4219,7 +3237,7 @@
             if (!fromBinderDied) {
                 killProcessQuiet(pid);
             }
-            killProcessGroup(app.uid, pid);
+            ProcessList.killProcessGroup(app.uid, pid);
             app.killed = true;
         }
 
@@ -4638,7 +3656,7 @@
                     return;
                 }
                 synchronized (this) {
-                    killPackageProcessesLocked(packageName, appId, targetUserId,
+                    mProcessList.killPackageProcessesLocked(packageName, appId, targetUserId,
                             ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
                 }
             }
@@ -4661,30 +3679,7 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                final ArrayList<ProcessRecord> procs = new ArrayList<>();
-                final int NP = mProcessNames.getMap().size();
-                for (int ip = 0; ip < NP; ip++) {
-                    final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
-                    final int NA = apps.size();
-                    for (int ia = 0; ia < NA; ia++) {
-                        final ProcessRecord app = apps.valueAt(ia);
-                        if (app.isPersistent()) {
-                            // We don't kill persistent processes.
-                            continue;
-                        }
-                        if (app.removed) {
-                            procs.add(app);
-                        } else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
-                            app.removed = true;
-                            procs.add(app);
-                        }
-                    }
-                }
-
-                final int N = procs.size();
-                for (int i = 0; i < N; i++) {
-                    removeProcessLocked(procs.get(i), false, true, "kill all background");
-                }
+                mProcessList.killAllBackgroundProcessesLocked();
 
                 mAllowLowerMemLevel = true;
 
@@ -4718,27 +3713,7 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                final ArrayList<ProcessRecord> procs = new ArrayList<>();
-                final int NP = mProcessNames.getMap().size();
-                for (int ip = 0; ip < NP; ip++) {
-                    final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
-                    final int NA = apps.size();
-                    for (int ia = 0; ia < NA; ia++) {
-                        final ProcessRecord app = apps.valueAt(ia);
-                        if (app.removed) {
-                            procs.add(app);
-                        } else if ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
-                                && (maxProcState < 0 || app.setProcState > maxProcState)) {
-                            app.removed = true;
-                            procs.add(app);
-                        }
-                    }
-                }
-
-                final int N = procs.size();
-                for (int i = 0; i < N; i++) {
-                    removeProcessLocked(procs.get(i), false, true, "kill all background except");
-                }
+                mProcessList.killAllBackgroundProcessesExceptLocked(minTargetSdk, maxProcState);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -5036,81 +4011,6 @@
                 null, false, false, MY_PID, SYSTEM_UID, UserHandle.getUserId(uid));
     }
 
-
-    @GuardedBy("this")
-    private final boolean killPackageProcessesLocked(String packageName, int appId,
-            int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
-            boolean doit, boolean evenPersistent, String reason) {
-        ArrayList<ProcessRecord> procs = new ArrayList<>();
-
-        // Remove all processes this package may have touched: all with the
-        // same UID (except for the system or root user), and all whose name
-        // matches the package name.
-        final int NP = mProcessNames.getMap().size();
-        for (int ip=0; ip<NP; ip++) {
-            SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
-            final int NA = apps.size();
-            for (int ia=0; ia<NA; ia++) {
-                ProcessRecord app = apps.valueAt(ia);
-                if (app.isPersistent() && !evenPersistent) {
-                    // we don't kill persistent processes
-                    continue;
-                }
-                if (app.removed) {
-                    if (doit) {
-                        procs.add(app);
-                    }
-                    continue;
-                }
-
-                // Skip process if it doesn't meet our oom adj requirement.
-                if (app.setAdj < minOomAdj) {
-                    continue;
-                }
-
-                // If no package is specified, we call all processes under the
-                // give user id.
-                if (packageName == null) {
-                    if (userId != UserHandle.USER_ALL && app.userId != userId) {
-                        continue;
-                    }
-                    if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) {
-                        continue;
-                    }
-                // Package has been specified, we want to hit all processes
-                // that match it.  We need to qualify this by the processes
-                // that are running under the specified app and user ID.
-                } else {
-                    final boolean isDep = app.pkgDeps != null
-                            && app.pkgDeps.contains(packageName);
-                    if (!isDep && UserHandle.getAppId(app.uid) != appId) {
-                        continue;
-                    }
-                    if (userId != UserHandle.USER_ALL && app.userId != userId) {
-                        continue;
-                    }
-                    if (!app.pkgList.containsKey(packageName) && !isDep) {
-                        continue;
-                    }
-                }
-
-                // Process has passed all conditions, kill it!
-                if (!doit) {
-                    return true;
-                }
-                app.removed = true;
-                procs.add(app);
-            }
-        }
-
-        int N = procs.size();
-        for (int i=0; i<N; i++) {
-            removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
-        }
-        updateOomAdjLocked();
-        return N > 0;
-    }
-
     private void cleanupDisabledPackageComponentsLocked(
             String packageName, int userId, boolean killProcess, String[] changedClasses) {
 
@@ -5231,7 +4131,7 @@
             mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);
         }
 
-        boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
+        boolean didSomething = mProcessList.killPackageProcessesLocked(packageName, appId, userId,
                 ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
                 packageName == null ? ("stop user " + userId) : ("stop " + packageName));
 
@@ -5293,128 +4193,10 @@
         return didSomething;
     }
 
-    private final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
-        return removeProcessNameLocked(name, uid, null);
-    }
-
-    private final ProcessRecord removeProcessNameLocked(final String name, final int uid,
-            final ProcessRecord expecting) {
-        ProcessRecord old = mProcessNames.get(name, uid);
-        // Only actually remove when the currently recorded value matches the
-        // record that we expected; if it doesn't match then we raced with a
-        // newly created process and we don't want to destroy the new one.
-        if ((expecting == null) || (old == expecting)) {
-            mProcessNames.remove(name, uid);
-        }
-        if (old != null && old.uidRecord != null) {
-            old.uidRecord.numProcs--;
-            if (old.uidRecord.numProcs == 0) {
-                // No more processes using this uid, tell clients it is gone.
-                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
-                        "No more processes in " + old.uidRecord);
-                enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
-                EventLogTags.writeAmUidStopped(uid);
-                mActiveUids.remove(uid);
-                noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
-            }
-            old.uidRecord = null;
-        }
-        mIsolatedProcesses.remove(uid);
-        return old;
-    }
-
-    private final void addProcessNameLocked(ProcessRecord proc) {
-        // We shouldn't already have a process under this name, but just in case we
-        // need to clean up whatever may be there now.
-        ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
-        if (old == proc && proc.isPersistent()) {
-            // We are re-adding a persistent process.  Whatevs!  Just leave it there.
-            Slog.w(TAG, "Re-adding persistent process " + proc);
-        } else if (old != null) {
-            Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
-        }
-        UidRecord uidRec = mActiveUids.get(proc.uid);
-        if (uidRec == null) {
-            uidRec = new UidRecord(proc.uid);
-            // This is the first appearance of the uid, report it now!
-            if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
-                    "Creating new process uid: " + uidRec);
-            if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0
-                    || mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
-                uidRec.setWhitelist = uidRec.curWhitelist = true;
-            }
-            uidRec.updateHasInternetPermission();
-            mActiveUids.put(proc.uid, uidRec);
-            EventLogTags.writeAmUidRunning(uidRec.uid);
-            noteUidProcessState(uidRec.uid, uidRec.curProcState);
-        }
-        proc.uidRecord = uidRec;
-
-        // Reset render thread tid if it was already set, so new process can set it again.
-        proc.renderThreadTid = 0;
-        uidRec.numProcs++;
-        mProcessNames.put(proc.processName, proc.uid, proc);
-        if (proc.isolated) {
-            mIsolatedProcesses.put(proc.uid, proc);
-        }
-    }
-
-    @GuardedBy("this")
-    boolean removeProcessLocked(ProcessRecord app,
-            boolean callerWillRestart, boolean allowRestart, String reason) {
-        final String name = app.processName;
-        final int uid = app.uid;
-        if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
-            "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
-
-        ProcessRecord old = mProcessNames.get(name, uid);
-        if (old != app) {
-            // This process is no longer active, so nothing to do.
-            Slog.w(TAG, "Ignoring remove of inactive process: " + app);
-            return false;
-        }
-        removeProcessNameLocked(name, uid);
-        mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
-
-        boolean needRestart = false;
-        if ((app.pid > 0 && app.pid != MY_PID) || (app.pid == 0 && app.pendingStart)) {
-            int pid = app.pid;
-            if (pid > 0) {
-                synchronized (mPidsSelfLocked) {
-                    mPidsSelfLocked.remove(pid);
-                    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
-                }
-                mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
-                if (app.isolated) {
-                    mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
-                    getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
-                }
-            }
-            boolean willRestart = false;
-            if (app.isPersistent() && !app.isolated) {
-                if (!callerWillRestart) {
-                    willRestart = true;
-                } else {
-                    needRestart = true;
-                }
-            }
-            app.kill(reason, true);
-            handleAppDiedLocked(app, willRestart, allowRestart);
-            if (willRestart) {
-                removeLruProcessLocked(app);
-                addAppLocked(app.info, null, false, null /* ABI override */);
-            }
-        } else {
-            mRemovedProcesses.add(app);
-        }
-
-        return needRestart;
-    }
-
     @GuardedBy("this")
     private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
         cleanupAppInLaunchingProvidersLocked(app, true);
-        removeProcessLocked(app, false, true, "timeout publishing content providers");
+        mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers");
     }
 
     private final void processStartTimedOutLocked(ProcessRecord app) {
@@ -5432,7 +4214,7 @@
             Slog.w(TAG, "Process " + app + " failed to attach");
             EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
                     pid, app.uid, app.processName);
-            removeProcessNameLocked(app.processName, app.uid);
+            mProcessList.removeProcessNameLocked(app.processName, app.uid);
             mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
             mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
             // Take care of any launching providers waiting for this process.
@@ -5488,9 +4270,10 @@
         // It's possible that process called attachApplication before we got a chance to
         // update the internal state.
         if (app == null && startSeq > 0) {
-            final ProcessRecord pending = mPendingStarts.get(startSeq);
+            final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
             if (pending != null && pending.startUid == callingUid
-                    && handleProcessStartedLocked(pending, pid, pending.isUsingWrapper(),
+                    && mProcessList.handleProcessStartedLocked(pending, pid, pending
+                            .isUsingWrapper(),
                             startSeq, true)) {
                 app = pending;
             }
@@ -5532,7 +4315,7 @@
             app.deathRecipient = adr;
         } catch (RemoteException e) {
             app.resetPackageList(mProcessStats);
-            startProcessLocked(app, "link fail", processName);
+            mProcessList.startProcessLocked(app, "link fail", processName);
             return false;
         }
 
@@ -5743,7 +4526,7 @@
                 profilerInfo = null;
             }
             checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
-            updateLruProcessLocked(app, false, null);
+            mProcessList.updateLruProcessLocked(app, false, null);
             checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
             app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
         } catch (Exception e) {
@@ -5754,7 +4537,7 @@
 
             app.resetPackageList(mProcessStats);
             app.unlinkDeathRecipient();
-            startProcessLocked(app, "bind fail", processName);
+            mProcessList.startProcessLocked(app, "bind fail", processName);
             return false;
         }
 
@@ -5927,7 +4710,7 @@
                 for (int ip=0; ip<NP; ip++) {
                     if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "Starting process on hold: "
                             + procs.get(ip));
-                    startProcessLocked(procs.get(ip), "on-hold", null);
+                    mProcessList.startProcessLocked(procs.get(ip), "on-hold", null);
                 }
             }
             if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
@@ -6884,19 +5667,7 @@
 
     @Override
     public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
-        final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
-        final long cachedAppMem = mProcessList.getMemLevel(ProcessList.CACHED_APP_MIN_ADJ);
-        outInfo.availMem = getFreeMemory();
-        outInfo.totalMem = getTotalMemory();
-        outInfo.threshold = homeAppMem;
-        outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
-        outInfo.hiddenAppThreshold = cachedAppMem;
-        outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
-                ProcessList.SERVICE_ADJ);
-        outInfo.visibleAppThreshold = mProcessList.getMemLevel(
-                ProcessList.VISIBLE_APP_ADJ);
-        outInfo.foregroundAppThreshold = mProcessList.getMemLevel(
-                ProcessList.FOREGROUND_APP_ADJ);
+        mProcessList.getMemoryInfo(outInfo);
     }
 
     // =========================================================
@@ -7332,7 +6103,7 @@
         return false;
     }
 
-    private void checkTime(long startTime, String where) {
+    void checkTime(long startTime, String where) {
         long now = SystemClock.uptimeMillis();
         if ((now-startTime) > 50) {
             // If we are taking more than 50ms, log about it.
@@ -7374,6 +6145,7 @@
         ContentProviderRecord cpr;
         ContentProviderConnection conn = null;
         ProviderInfo cpi = null;
+        boolean providerRunning = false;
 
         synchronized(this) {
             long startTime = SystemClock.uptimeMillis();
@@ -7413,8 +6185,6 @@
                 }
             }
 
-            boolean providerRunning = false;
-
             if (cpr != null && cpr.proc != null) {
                 providerRunning = !cpr.proc.killed;
 
@@ -7492,7 +6262,7 @@
                         // back up on the LRU list.  This is good because
                         // content providers are often expensive to start.
                         checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
-                        updateLruProcessLocked(cpr.proc, false, null);
+                        mProcessList.updateLruProcessLocked(cpr.proc, false, null);
                         checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
                     }
                 }
@@ -7739,6 +6509,7 @@
         }
 
         // Wait for the provider to be published...
+        final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
         synchronized (cpr) {
             while (cpr.provider == null) {
                 if (cpr.launchingApp == null) {
@@ -7753,13 +6524,22 @@
                     return null;
                 }
                 try {
+                    final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
                     if (DEBUG_MU) Slog.v(TAG_MU,
                             "Waiting to start provider " + cpr
-                            + " launchingApp=" + cpr.launchingApp);
+                            + " launchingApp=" + cpr.launchingApp + " for " + wait + " ms");
                     if (conn != null) {
                         conn.waiting = true;
                     }
-                    cpr.wait();
+                    cpr.wait(wait);
+                    if (cpr.provider == null) {
+                        Slog.wtf(TAG, "Timeout waiting for provider "
+                                + cpi.applicationInfo.packageName + "/"
+                                + cpi.applicationInfo.uid + " for provider "
+                                + name
+                                + " providerRunning=" + providerRunning);
+                        return null;
+                    }
                 } catch (InterruptedException ex) {
                 } finally {
                     if (conn != null) {
@@ -8128,7 +6908,7 @@
     public final void installSystemProviders() {
         List<ProviderInfo> providers;
         synchronized (this) {
-            ProcessRecord app = mProcessNames.get("system", SYSTEM_UID);
+            ProcessRecord app = mProcessList.mProcessNames.get("system", SYSTEM_UID);
             providers = generateApplicationProvidersLocked(app);
             if (providers != null) {
                 for (int i=providers.size()-1; i>=0; i--) {
@@ -8190,9 +6970,10 @@
         final int matchFlags = GET_PROVIDERS | MATCH_DIRECT_BOOT_UNAWARE;
 
         synchronized (this) {
-            final int NP = mProcessNames.getMap().size();
+            final int NP = mProcessList.mProcessNames.getMap().size();
             for (int ip = 0; ip < NP; ip++) {
-                final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+                final SparseArray<ProcessRecord> apps = mProcessList.mProcessNames.getMap().valueAt
+                        (ip);
                 final int NA = apps.size();
                 for (int ia = 0; ia < NA; ia++) {
                     final ProcessRecord app = apps.valueAt(ia);
@@ -8298,67 +7079,6 @@
     // GLOBAL MANAGEMENT
     // =========================================================
 
-    @GuardedBy("this")
-    final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
-            boolean isolated, int isolatedUid) {
-        String proc = customProcess != null ? customProcess : info.processName;
-        final int userId = UserHandle.getUserId(info.uid);
-        int uid = info.uid;
-        if (isolated) {
-            if (isolatedUid == 0) {
-                int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1;
-                while (true) {
-                    if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID
-                            || mNextIsolatedProcessUid > LAST_ISOLATED_UID) {
-                        mNextIsolatedProcessUid = FIRST_ISOLATED_UID;
-                    }
-                    uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
-                    mNextIsolatedProcessUid++;
-                    if (mIsolatedProcesses.indexOfKey(uid) < 0) {
-                        // No process for this uid, use it.
-                        break;
-                    }
-                    stepsLeft--;
-                    if (stepsLeft <= 0) {
-                        return null;
-                    }
-                }
-            } else {
-                // Special case for startIsolatedProcess (internal only), where
-                // the uid of the isolated process is specified by the caller.
-                uid = isolatedUid;
-            }
-            getPackageManagerInternalLocked().addIsolatedUid(uid, info.uid);
-
-            // Register the isolated UID with this application so BatteryStats knows to
-            // attribute resource usage to the application.
-            //
-            // NOTE: This is done here before addProcessNameLocked, which will tell BatteryStats
-            // about the process state of the isolated UID *before* it is registered with the
-            // owning application.
-            mBatteryStatsService.addIsolatedUid(uid, info.uid);
-            StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
-                    StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
-        }
-        final ProcessRecord r = new ProcessRecord(this, info, proc, uid, getGlobalConfiguration());
-        if (!mBooted && !mBooting
-                && userId == UserHandle.USER_SYSTEM
-                && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
-            // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
-            r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
-            r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            r.setPersistent(true);
-            r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
-        }
-        if (isolated && isolatedUid != 0) {
-            // Special case for startIsolatedProcess (internal only) - assume the process
-            // is required by the system server to prevent it being killed.
-            r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
-        }
-        addProcessNameLocked(r);
-        return r;
-    }
-
     private boolean uidOnBackgroundWhitelist(final int uid) {
         final int appId = UserHandle.getAppId(uid);
         final int[] whitelist = mBackgroundAppIdWhitelist;
@@ -8419,6 +7139,7 @@
                 abiOverride);
     }
 
+    // TODO: Move to ProcessList?
     @GuardedBy("this")
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
             boolean disableHiddenApiChecks, String abiOverride) {
@@ -8431,8 +7152,8 @@
         }
 
         if (app == null) {
-            app = newProcessRecordLocked(info, customProcess, isolated, 0);
-            updateLruProcessLocked(app, false, null);
+            app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0);
+            mProcessList.updateLruProcessLocked(app, false, null);
             updateOomAdjLocked();
         }
 
@@ -8452,7 +7173,7 @@
         }
         if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
             mPersistentStartingProcesses.add(app);
-            startProcessLocked(app, "added application",
+            mProcessList.startProcessLocked(app, "added application",
                     customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
                     abiOverride);
         }
@@ -9316,7 +8037,7 @@
         synchronized (this) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                killPackageProcessesLocked(null, appId, userId,
+                mProcessList.killPackageProcessesLocked(null, appId, userId,
                         ProcessList.PERSISTENT_PROC_ADJ, false, true, true, true,
                         reason != null ? reason : "kill uid");
             } finally {
@@ -9466,8 +8187,8 @@
             final long memoryGrowthThreshold =
                     Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD);
 
-            for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                ProcessRecord proc = mLruProcesses.get(i);
+            for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+                ProcessRecord proc = mProcessList.mLruProcesses.get(i);
                 if (proc.notCachedSinceIdle) {
                     if (proc.setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
                             && proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
@@ -9597,7 +8318,7 @@
                 for (int i=procsToKill.size()-1; i>=0; i--) {
                     ProcessRecord proc = procsToKill.get(i);
                     Slog.i(TAG, "Removing system update proc: " + proc);
-                    removeProcessLocked(proc, true, false, "system update done");
+                    mProcessList.removeProcessLocked(proc, true, false, "system update done");
                 }
             }
 
@@ -9984,22 +8705,7 @@
         }
 
         synchronized (this) {
-            final int NP = mProcessNames.getMap().size();
-            for (int ip=0; ip<NP; ip++) {
-                SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
-                final int NA = apps.size();
-                for (int ia=0; ia<NA; ia++) {
-                    ProcessRecord p = apps.valueAt(ia);
-                    if (p.thread != null && p.thread.asBinder() == app) {
-                        return p;
-                    }
-                }
-            }
-
-            Slog.w(TAG, "Can't find mystery application for " + reason
-                    + " from pid=" + Binder.getCallingPid()
-                    + " uid=" + Binder.getCallingUid() + ": " + app);
-            return null;
+            return mProcessList.findAppProcessLocked(app, reason);
         }
     }
 
@@ -10215,8 +8921,8 @@
         synchronized (this) {
 
             // iterate across all processes
-            for (int i=mLruProcesses.size()-1; i>=0; i--) {
-                ProcessRecord app = mLruProcesses.get(i);
+            for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
+                ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (!allUsers && app.userId != userId) {
                     continue;
                 }
@@ -10249,44 +8955,6 @@
         return errList;
     }
 
-    static int procStateToImportance(int procState, int memAdj,
-            ActivityManager.RunningAppProcessInfo currApp,
-            int clientTargetSdk) {
-        int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk(
-                procState, clientTargetSdk);
-        if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
-            currApp.lru = memAdj;
-        } else {
-            currApp.lru = 0;
-        }
-        return imp;
-    }
-
-    @GuardedBy("this")
-    private void fillInProcMemInfoLocked(ProcessRecord app,
-            ActivityManager.RunningAppProcessInfo outInfo,
-            int clientTargetSdk) {
-        outInfo.pid = app.pid;
-        outInfo.uid = app.info.uid;
-        if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
-            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
-        }
-        if (app.isPersistent()) {
-            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
-        }
-        if (app.hasActivities()) {
-            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
-        }
-        outInfo.lastTrimLevel = app.trimMemoryLevel;
-        int adj = app.curAdj;
-        int procState = app.getCurProcState();
-        outInfo.importance = procStateToImportance(procState, adj, outInfo, clientTargetSdk);
-        outInfo.importanceReasonCode = app.adjTypeCode;
-        outInfo.processState = app.getCurProcState();
-        outInfo.isFocused = (app == getTopAppLocked());
-        outInfo.lastActivityTime = app.lastActivityTime;
-    }
-
     @Override
     public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
         enforceNotIsolatedCaller("getRunningAppProcesses");
@@ -10294,8 +8962,6 @@
         final int callingUid = Binder.getCallingUid();
         final int clientTargetSdk = mPackageManagerInt.getUidTargetSdkVersion(callingUid);
 
-        // Lazy instantiation of list
-        List<ActivityManager.RunningAppProcessInfo> runList = null;
         final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
                 callingUid) == PackageManager.PERMISSION_GRANTED;
         final int userId = UserHandle.getUserId(callingUid);
@@ -10304,44 +8970,9 @@
 
         synchronized (this) {
             // Iterate across all processes
-            for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-                ProcessRecord app = mLruProcesses.get(i);
-                if ((!allUsers && app.userId != userId)
-                        || (!allUids && app.uid != callingUid)) {
-                    continue;
-                }
-                if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
-                    // Generate process state info for running application
-                    ActivityManager.RunningAppProcessInfo currApp =
-                        new ActivityManager.RunningAppProcessInfo(app.processName,
-                                app.pid, app.getPackageList());
-                    fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
-                    if (app.adjSource instanceof ProcessRecord) {
-                        currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
-                        currApp.importanceReasonImportance =
-                                ActivityManager.RunningAppProcessInfo.procStateToImportance(
-                                        app.adjSourceProcState);
-                    } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) {
-                        ActivityServiceConnectionsHolder r =
-                                (ActivityServiceConnectionsHolder) app.adjSource;
-                        final int pid = r.getActivityPid();
-                        if (pid != -1) {
-                            currApp.importanceReasonPid = pid;
-                        }
-                    }
-                    if (app.adjTarget instanceof ComponentName) {
-                        currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
-                    }
-                    //Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
-                    //        + " lru=" + currApp.lru);
-                    if (runList == null) {
-                        runList = new ArrayList<>();
-                    }
-                    runList.add(currApp);
-                }
-            }
+            return mProcessList.getRunningAppProcessesLocked(allUsers, userId, allUids,
+                    callingUid, clientTargetSdk);
         }
-        return runList;
     }
 
     @Override
@@ -10388,7 +9019,7 @@
                 proc = mPidsSelfLocked.get(Binder.getCallingPid());
             }
             if (proc != null) {
-                fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
+                mProcessList.fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
             }
         }
     }
@@ -11005,6 +9636,7 @@
                 "Counts of Binder Proxies held by SYSTEM");
     }
 
+    // TODO: Move to ProcessList?
     @GuardedBy("this")
     void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
@@ -11014,9 +9646,9 @@
         pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
 
         if (dumpAll) {
-            final int NP = mProcessNames.getMap().size();
+            final int NP = mProcessList.mProcessNames.getMap().size();
             for (int ip=0; ip<NP; ip++) {
-                SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
+                SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip);
                 final int NA = procs.size();
                 for (int ia=0; ia<NA; ia++) {
                     ProcessRecord r = procs.valueAt(ia);
@@ -11038,10 +9670,10 @@
             }
         }
 
-        if (mIsolatedProcesses.size() > 0) {
+        if (mProcessList.mIsolatedProcesses.size() > 0) {
             boolean printed = false;
-            for (int i=0; i<mIsolatedProcesses.size(); i++) {
-                ProcessRecord r = mIsolatedProcesses.valueAt(i);
+            for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) {
+                ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i);
                 if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
                     continue;
                 }
@@ -11094,17 +9726,13 @@
             }
         }
 
-        if (mLruProcesses.size() > 0) {
+        if (mProcessList.getLruSizeLocked() > 0) {
             if (needSep) {
                 pw.println();
             }
-            pw.print("  Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
-                    pw.print(" total, non-act at ");
-                    pw.print(mLruProcesses.size()-mLruProcessActivityStart);
-                    pw.print(", non-svc at ");
-                    pw.print(mLruProcesses.size()-mLruProcessServiceStart);
-                    pw.println("):");
-            dumpProcessOomList(pw, this, mLruProcesses, "    ", "Proc", "PERS", false, dumpPackage);
+            mProcessList.dumpLruListHeaderLocked(pw);
+            dumpProcessOomList(pw, this, mProcessList.mLruProcesses, "    ", "Proc", "PERS", false,
+                    dumpPackage);
             needSep = true;
         }
 
@@ -11158,11 +9786,11 @@
                     "Starting Norm", "Restarting PERS", dumpPackage);
         }
 
-        if (mRemovedProcesses.size() > 0) {
+        if (mProcessList.mRemovedProcesses.size() > 0) {
             if (needSep) pw.println();
             needSep = true;
             pw.println("  Processes that are being removed:");
-            dumpProcessList(pw, this, mRemovedProcesses, "    ",
+            dumpProcessList(pw, this, mProcessList.mRemovedProcesses, "    ",
                     "Removed Norm", "Removed PERS", dumpPackage);
         }
 
@@ -11187,12 +9815,13 @@
         needSep = mAtmInternal.dumpForProcesses(fd, pw, dumpAll, dumpPackage, dumpAppId, needSep,
                 mTestPssMode, mWakefulness);
 
-        if (dumpAll && mPendingStarts.size() > 0) {
+        if (dumpAll && mProcessList.mPendingStarts.size() > 0) {
             if (needSep) pw.println();
             needSep = true;
             pw.println("  mPendingStarts: ");
-            for (int i = 0, len = mPendingStarts.size(); i < len; ++i ) {
-                pw.println("    " + mPendingStarts.keyAt(i) + ": " + mPendingStarts.valueAt(i));
+            for (int i = 0, len = mProcessList.mPendingStarts.size(); i < len; ++i ) {
+                pw.println("    " + mProcessList.mPendingStarts.keyAt(i) + ": "
+                        + mProcessList.mPendingStarts.valueAt(i));
             }
         }
         if (dumpAll) {
@@ -11345,9 +9974,9 @@
                 pw.print("  mLastPowerCheckUptime=");
                         TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
                         pw.println("");
-                pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
+                pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.mLruSeq);
                 pw.println("  mNumNonCachedProcs=" + mNumNonCachedProcs
-                        + " (" + mLruProcesses.size() + " total)"
+                        + " (" + mProcessList.getLruSizeLocked() + " total)"
                         + " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
                         + " mNumServiceProcs=" + mNumServiceProcs
                         + " mNewNumServiceProcs=" + mNewNumServiceProcs);
@@ -11393,9 +10022,9 @@
     void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
         int numPers = 0;
 
-        final int NP = mProcessNames.getMap().size();
+        final int NP = mProcessList.mProcessNames.getMap().size();
         for (int ip=0; ip<NP; ip++) {
-            SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
+            SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip);
             final int NA = procs.size();
             for (int ia = 0; ia<NA; ia++) {
                 ProcessRecord r = procs.valueAt(ia);
@@ -11409,8 +10038,8 @@
             }
         }
 
-        for (int i=0; i<mIsolatedProcesses.size(); i++) {
-            ProcessRecord r = mIsolatedProcesses.valueAt(i);
+        for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) {
+            ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i);
             if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
                 continue;
             }
@@ -11443,14 +10072,17 @@
             uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
         }
 
-        if (mLruProcesses.size() > 0) {
+        if (mProcessList.getLruSizeLocked() > 0) {
             long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
-            int total = mLruProcesses.size();
+            int total = mProcessList.getLruSizeLocked();
             proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total);
-            proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT, total-mLruProcessActivityStart);
-            proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT, total-mLruProcessServiceStart);
-            writeProcessOomListToProto(proto, ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this,
-                    mLruProcesses,false, dumpPackage);
+            proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT,
+                    total - mProcessList.mLruProcessActivityStart);
+            proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT,
+                    total - mProcessList.mLruProcessServiceStart);
+            writeProcessOomListToProto(proto,
+                    ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this,
+                    mProcessList.mLruProcesses,false, dumpPackage);
             proto.end(lruToken);
         }
 
@@ -11461,7 +10093,8 @@
                     if (!r.pkgList.containsKey(dumpPackage)) {
                         continue;
                     }
-                    r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED);
+                    r.writeToProto(proto,
+                            ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED);
                 }
             }
         }
@@ -11475,7 +10108,8 @@
                             || !r.pkgList.containsKey(dumpPackage))) {
                         continue;
                     }
-                    it.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS);
+                    it.writeToProto(proto,
+                            ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS);
                 }
             }
         }
@@ -11485,11 +10119,12 @@
             if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
                 continue;
             }
-            r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS);
+            r.writeToProto(proto,
+                    ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS);
         }
 
-        for (int i=0; i<mRemovedProcesses.size(); i++) {
-            ProcessRecord r = mRemovedProcesses.get(i);
+        for (int i = 0; i < mProcessList.mRemovedProcesses.size(); i++) {
+            ProcessRecord r = mProcessList.mRemovedProcesses.get(i);
             if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
                 continue;
             }
@@ -11623,7 +10258,7 @@
             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, mLruSeq);
+            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);
@@ -11704,7 +10339,7 @@
             int opti, boolean dumpAll) {
         boolean needSep = false;
 
-        if (mLruProcesses.size() > 0) {
+        if (mProcessList.getLruSizeLocked() > 0) {
             if (needSep) pw.println();
             needSep = true;
             pw.println("  OOM levels:");
@@ -11724,13 +10359,16 @@
             printOomLevel(pw, "CACHED_APP_MAX_ADJ", ProcessList.CACHED_APP_MAX_ADJ);
 
             if (needSep) pw.println();
-            pw.print("  Process OOM control ("); pw.print(mLruProcesses.size());
+            pw.print("  Process OOM control ("); pw.print(mProcessList.getLruSizeLocked());
                     pw.print(" total, non-act at ");
-                    pw.print(mLruProcesses.size()-mLruProcessActivityStart);
+                    pw.print(mProcessList.getLruSizeLocked()
+                            - mProcessList.mLruProcessActivityStart);
                     pw.print(", non-svc at ");
-                    pw.print(mLruProcesses.size()-mLruProcessServiceStart);
+                    pw.print(mProcessList.getLruSizeLocked()
+                            - mProcessList.mLruProcessServiceStart);
                     pw.println("):");
-            dumpProcessOomList(pw, this, mLruProcesses, "    ", "Proc", "PERS", true, null);
+            dumpProcessOomList(pw, this, mProcessList.mLruProcesses, "    ", "Proc", "PERS", true,
+                    null);
             needSep = true;
         }
 
@@ -12365,35 +11003,9 @@
 
     ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, boolean allPkgs,
             String[] args) {
-        ArrayList<ProcessRecord> procs;
         synchronized (this) {
-            if (args != null && args.length > start
-                    && args[start].charAt(0) != '-') {
-                procs = new ArrayList<ProcessRecord>();
-                int pid = -1;
-                try {
-                    pid = Integer.parseInt(args[start]);
-                } catch (NumberFormatException e) {
-                }
-                for (int i=mLruProcesses.size()-1; i>=0; i--) {
-                    ProcessRecord proc = mLruProcesses.get(i);
-                    if (proc.pid > 0 && proc.pid == pid) {
-                        procs.add(proc);
-                    } else if (allPkgs && proc.pkgList != null
-                            && proc.pkgList.containsKey(args[start])) {
-                        procs.add(proc);
-                    } else if (proc.processName.equals(args[start])) {
-                        procs.add(proc);
-                    }
-                }
-                if (procs.size() <= 0) {
-                    return null;
-                }
-            } else {
-                procs = new ArrayList<ProcessRecord>(mLruProcesses);
-            }
+            return mProcessList.collectProcessesLocked(start, allPkgs, args);
         }
-        return procs;
     }
 
     final void dumpGraphicsHardwareUsage(FileDescriptor fd,
@@ -14088,7 +12700,7 @@
      * app that was passed in must remain on the process lists.
      */
     @GuardedBy("this")
-    private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
+    final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
             boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
         if (index >= 0) {
             removeLruProcessLocked(app);
@@ -14228,7 +12840,7 @@
             if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                     "Removing non-persistent process during cleanup: " + app);
             if (!replacingPid) {
-                removeProcessNameLocked(app.processName, app.uid, app);
+                mProcessList.removeProcessNameLocked(app.processName, app.uid, app);
             }
             mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
         } else if (!app.removed) {
@@ -14252,9 +12864,9 @@
             if (index < 0) {
                 ProcessList.remove(app.pid);
             }
-            addProcessNameLocked(app);
+            mProcessList.addProcessNameLocked(app);
             app.pendingStart = false;
-            startProcessLocked(app, "restart", app.processName);
+            mProcessList.startProcessLocked(app, "restart", app.processName);
             return true;
         } else if (app.pid > 0 && app.pid != MY_PID) {
             // Goodbye!
@@ -14991,15 +13603,7 @@
     }
 
     private final void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
-        for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-            ProcessRecord r = mLruProcesses.get(i);
-            if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
-                try {
-                    r.thread.dispatchPackageBroadcast(cmd, packages);
-                } catch (RemoteException ex) {
-                }
-            }
-        }
+        mProcessList.sendPackageBroadcastLocked(cmd, packages, userId);
     }
 
     private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
@@ -15408,8 +14012,10 @@
                                     }
                                 } else {
                                     if (killProcess) {
-                                        killPackageProcessesLocked(ssp, UserHandle.getAppId(
-                                                intent.getIntExtra(Intent.EXTRA_UID, -1)),
+                                        final int extraUid = intent.getIntExtra(Intent.EXTRA_UID,
+                                                -1);
+                                        mProcessList.killPackageProcessesLocked(ssp,
+                                                UserHandle.getAppId(extraUid),
                                                 userId, ProcessList.INVALID_ADJ,
                                                 false, true, true, false, "change " + ssp);
                                     }
@@ -16398,7 +15004,7 @@
         }
     }
 
-    private void noteUidProcessState(final int uid, final int state) {
+    void noteUidProcessState(final int uid, final int state) {
         mBatteryStatsService.noteUidProcessState(uid, state);
         mAppOpsService.updateUidProcState(uid, state);
         if (mTrackingAssociations) {
@@ -16430,6 +15036,7 @@
     private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
             new ComputeOomAdjWindowCallback();
 
+    /** These methods are called inline during computeOomAdjLocked(), on the same thread */
     private final class ComputeOomAdjWindowCallback
             implements WindowProcessController.ComputeOomAdjCallback {
 
@@ -17611,10 +16218,10 @@
         for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
             ProcessList.abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);;
         }
-        mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
+        mPendingPssProcesses.ensureCapacity(mProcessList.getLruSizeLocked());
         mPendingPssProcesses.clear();
-        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-            ProcessRecord app = mLruProcesses.get(i);
+        for (int i = mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
+            ProcessRecord app = mProcessList.mLruProcesses.get(i);
             if (app.thread == null
                     || app.getCurProcState() == ActivityManager.PROCESS_STATE_NONEXISTENT) {
                 continue;
@@ -17790,10 +16397,10 @@
         final long curUptime = SystemClock.uptimeMillis();
         final long uptimeSince = curUptime - mLastPowerCheckUptime;
         mLastPowerCheckUptime = curUptime;
-        int i = mLruProcesses.size();
+        int i = mProcessList.mLruProcesses.size();
         while (i > 0) {
             i--;
-            ProcessRecord app = mLruProcesses.get(i);
+            ProcessRecord app = mProcessList.mLruProcesses.get(i);
             if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
                 if (app.lastCpuTime <= 0) {
                     continue;
@@ -18343,7 +16950,7 @@
 
     // TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update
     // the logic there and in mBatteryStatsService to make them aware of multiple resumed activities
-    private ProcessRecord getTopAppLocked() {
+    ProcessRecord getTopAppLocked() {
         final WindowProcessController wpc = mAtmInternal != null ? mAtmInternal.getTopApp() : null;
         final ProcessRecord r = wpc != null ? (ProcessRecord) wpc.mOwner : null;
         String pkg;
@@ -18417,7 +17024,7 @@
         final long now = SystemClock.uptimeMillis();
         final long nowElapsed = SystemClock.elapsedRealtime();
         final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
-        final int N = mLruProcesses.size();
+        final int N = mProcessList.getLruSizeLocked();
 
         // Reset state in all uid records.
         for (int i=mActiveUids.size()-1; i>=0; i--) {
@@ -18478,11 +17085,11 @@
 
         // need to reset cycle state before calling computeOomAdjLocked because of service connections
         for (int i=N-1; i>=0; i--) {
-            ProcessRecord app = mLruProcesses.get(i);
+            ProcessRecord app = mProcessList.mLruProcesses.get(i);
             app.containsCycle = false;
         }
         for (int i=N-1; i>=0; i--) {
-            ProcessRecord app = mLruProcesses.get(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);
@@ -18557,7 +17164,7 @@
             retryCycles = false;
 
             for (int i=0; i<N; i++) {
-                ProcessRecord app = mLruProcesses.get(i);
+                ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
                     app.adjSeq--;
                     app.completedAdjSeq--;
@@ -18565,7 +17172,7 @@
             }
 
             for (int i=0; i<N; i++) {
-                ProcessRecord app = mLruProcesses.get(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)) {
@@ -18576,7 +17183,7 @@
         }
 
         for (int i=N-1; i>=0; i--) {
-            ProcessRecord app = mLruProcesses.get(i);
+            ProcessRecord app = mProcessList.mLruProcesses.get(i);
             if (!app.killedByAm && app.thread != null) {
                 applyOomAdjLocked(app, true, now, nowElapsed);
 
@@ -18669,9 +17276,9 @@
         // has gone down since last time.
         if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
                 + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
-                + " numProcs=" + mLruProcesses.size() + " last=" + mLastNumProcesses);
+                + " numProcs=" + mProcessList.getLruSizeLocked() + " last=" + mLastNumProcesses);
         if (memFactor > mLastMemoryLevel) {
-            if (!mAllowLowerMemLevel || mLruProcesses.size() >= mLastNumProcesses) {
+            if (!mAllowLowerMemLevel || mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
                 memFactor = mLastMemoryLevel;
                 if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
             }
@@ -18681,7 +17288,7 @@
             StatsLog.write(StatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
         }
         mLastMemoryLevel = memFactor;
-        mLastNumProcesses = mLruProcesses.size();
+        mLastNumProcesses = mProcessList.getLruSizeLocked();
         boolean allChanged = mProcessStats.setMemFactorLocked(
                 memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
         final int trackerMemFactor = mProcessStats.getMemFactorLocked();
@@ -18709,7 +17316,7 @@
             if (factor < minFactor) factor = minFactor;
             int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
             for (int i=N-1; i>=0; i--) {
-                ProcessRecord app = mLruProcesses.get(i);
+                ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (allChanged || app.procStateChanged) {
                     setProcessTrackerStateLocked(app, trackerMemFactor, now);
                     app.procStateChanged = false;
@@ -18787,7 +17394,7 @@
                 mLowRamStartTime = 0;
             }
             for (int i=N-1; i>=0; i--) {
-                ProcessRecord app = mLruProcesses.get(i);
+                ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (allChanged || app.procStateChanged) {
                     setProcessTrackerStateLocked(app, trackerMemFactor, now);
                     app.procStateChanged = false;
@@ -19051,7 +17658,7 @@
                 continue;
             }
             synchronized (uidRec.networkStateLock) {
-                uidRec.curProcStateSeq = ++mProcStateSeqCounter;
+                uidRec.curProcStateSeq = ++mProcessList.mProcStateSeqCounter; // TODO: use method
                 if (blockState == NETWORK_STATE_BLOCK) {
                     if (blockingUids == null) {
                         blockingUids = new ArrayList<>();
@@ -19074,8 +17681,8 @@
             return;
         }
 
-        for (int i = mLruProcesses.size() - 1; i >= 0; --i) {
-            final ProcessRecord app = mLruProcesses.get(i);
+        for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; --i) {
+            final ProcessRecord app = mProcessList.mLruProcesses.get(i);
             if (!blockingUids.contains(app.uid)) {
                 continue;
             }
@@ -19273,8 +17880,8 @@
     final void trimApplicationsLocked() {
         // First remove any unused application processes whose package
         // has been removed.
-        for (int i=mRemovedProcesses.size()-1; i>=0; i--) {
-            final ProcessRecord app = mRemovedProcesses.get(i);
+        for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) {
+            final ProcessRecord app = mProcessList.mRemovedProcesses.get(i);
             if (!app.hasActivitiesOrRecentTasks()
                     && app.curReceivers.isEmpty() && app.services.size() == 0) {
                 Slog.i(
@@ -19292,7 +17899,7 @@
                     }
                 }
                 cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
-                mRemovedProcesses.remove(i);
+                mProcessList.mRemovedProcesses.remove(i);
 
                 if (app.isPersistent()) {
                     addAppLocked(app.info, null, false, null /* ABI override */);
@@ -19318,8 +17925,8 @@
                         + android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES);
             }
 
-            for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                ProcessRecord r = mLruProcesses.get(i);
+            for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+                ProcessRecord r = mProcessList.mLruProcesses.get(i);
                 if (r.thread != null && r.isPersistent()) {
                     sendSignal(r.pid, sig);
                 }
@@ -19446,7 +18053,7 @@
 
         if (proc == null) {
             ArrayMap<String, SparseArray<ProcessRecord>> all
-                    = mProcessNames.getMap();
+                    = mProcessList.mProcessNames.getMap();
             SparseArray<ProcessRecord> procs = all.get(process);
             if (procs != null && procs.size() > 0) {
                 proc = procs.valueAt(0);
@@ -19576,15 +18183,8 @@
     }
 
     void onCoreSettingsChange(Bundle settings) {
-        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-            ProcessRecord processRecord = mLruProcesses.get(i);
-            try {
-                if (processRecord.thread != null) {
-                    processRecord.thread.setCoreSettings(settings);
-                }
-            } catch (RemoteException re) {
-                /* ignore */
-            }
+        synchronized (this) {
+            mProcessList.updateCoreSettingsLocked(settings);
         }
     }
 
@@ -19716,8 +18316,8 @@
                         + android.Manifest.permission.SET_ACTIVITY_WATCHER);
             }
 
-            for (int i = 0; i < mLruProcesses.size(); i++) {
-                ProcessRecord process = mLruProcesses.get(i);
+            for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
+                ProcessRecord process = mProcessList.mLruProcesses.get(i);
                 if (!processSanityChecksLocked(process)) {
                     continue;
                 }
@@ -19749,7 +18349,7 @@
 
                 PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
                 pw.println("Binder transaction traces for all processes.\n");
-                for (ProcessRecord process : mLruProcesses) {
+                for (ProcessRecord process : mProcessList.mLruProcesses) {
                     if (!processSanityChecksLocked(process)) {
                         continue;
                     }
@@ -19810,9 +18410,10 @@
         public void killForegroundAppsForUser(int userHandle) {
             synchronized (ActivityManagerService.this) {
                 final ArrayList<ProcessRecord> procs = new ArrayList<>();
-                final int NP = mProcessNames.getMap().size();
+                final int NP = mProcessList.mProcessNames.getMap().size();
                 for (int ip = 0; ip < NP; ip++) {
-                    final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+                    final SparseArray<ProcessRecord> apps =
+                            mProcessList.mProcessNames.getMap().valueAt(ip);
                     final int NA = apps.size();
                     for (int ia = 0; ia < NA; ia++) {
                         final ProcessRecord app = apps.valueAt(ia);
@@ -19831,7 +18432,7 @@
 
                 final int N = procs.size();
                 for (int i = 0; i < N; i++) {
-                    removeProcessLocked(procs.get(i), false, true, "kill all fg");
+                    mProcessList.removeProcessLocked(procs.get(i), false, true, "kill all fg");
                 }
             }
         }
@@ -20089,8 +18690,8 @@
             if (packageName == null) return false;
 
             synchronized (ActivityManagerService.this) {
-                for (int i = 0; i < mLruProcesses.size(); i++) {
-                    final ProcessRecord pr = mLruProcesses.get(i);
+                for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
+                    final ProcessRecord pr = mProcessList.mLruProcesses.get(i);
                     if (pr.uid != uid) {
                         continue;
                     }
@@ -20323,6 +18924,110 @@
                     (WindowProcessController) parentProc, aboveSystem, reason);
 
         }
+
+        @Override
+        public void broadcastGlobalConfigurationChanged(int changes, boolean initLocale) {
+            synchronized (ActivityManagerService.this) {
+                Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                        | Intent.FLAG_RECEIVER_REPLACE_PENDING
+                        | Intent.FLAG_RECEIVER_FOREGROUND
+                        | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+                broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                        OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+                        UserHandle.USER_ALL);
+                if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
+                    intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
+                    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+                            | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+                            | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+                    if (initLocale || !mProcessesReady) {
+                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    }
+                    broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                            OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+                            UserHandle.USER_ALL);
+                }
+
+                // Send a broadcast to PackageInstallers if the configuration change is interesting
+                // for the purposes of installing additional splits.
+                if (!initLocale && isSplitConfigurationChange(changes)) {
+                    intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                            | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+
+                    // Typically only app stores will have this permission.
+                    String[] permissions =
+                            new String[] { android.Manifest.permission.INSTALL_PACKAGES };
+                    broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
+                            permissions, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+                            UserHandle.USER_ALL);
+                }
+            }
+        }
+
+        /**
+         * Returns true if this configuration change is interesting enough to send an
+         * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
+         */
+        private boolean isSplitConfigurationChange(int configDiff) {
+            return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
+        }
+
+        @Override
+        public void broadcastCloseSystemDialogs(String reason) {
+            synchronized (ActivityManagerService.this) {
+                final Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                        | Intent.FLAG_RECEIVER_FOREGROUND);
+                if (reason != null) {
+                    intent.putExtra("reason", reason);
+                }
+
+                broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                        OP_NONE, null, false, false, -1, SYSTEM_UID, UserHandle.USER_ALL);
+            }
+        }
+
+        @Override
+        public void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
+            synchronized (ActivityManagerService.this) {
+                ActivityManagerService.this.killAllBackgroundProcessesExcept(
+                        minTargetSdk, maxProcState);
+            }
+        }
+
+        @Override
+        public void startProcess(String processName, ApplicationInfo info,
+                boolean knownToBeDead, String hostingType, ComponentName hostingName) {
+            synchronized (ActivityManagerService.this) {
+                startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
+                        hostingType, hostingName, false /* allowWhileBooting */,
+                        false /* isolated */, true /* keepIfLarge */);
+            }
+        }
+
+        @Override
+        public void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
+                ProfilerInfo profilerInfo) {
+            synchronized (ActivityManagerService.this) {
+                if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
+                    setDebugApp(aInfo.processName, true, false);
+                }
+
+                if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
+                    setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
+                }
+
+                if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
+                    setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
+                }
+
+                if (profilerInfo != null) {
+                    setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
+                }
+            }
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
@@ -20504,8 +19209,8 @@
         }
         try {
             synchronized(this) {
-                killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid), userId,
-                        ProcessList.FOREGROUND_APP_ADJ, false, true, true, false,
+                mProcessList.killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid),
+                        userId, ProcessList.FOREGROUND_APP_ADJ, false, true, true, false,
                         "dep: " + packageName);
             }
         } finally {
@@ -20535,33 +19240,9 @@
 
     void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) {
         final boolean updateFrameworkRes = packagesToUpdate.contains("android");
-        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-            final ProcessRecord app = mLruProcesses.get(i);
-            if (app.thread == null) {
-                continue;
-            }
 
-            if (userId != UserHandle.USER_ALL && app.userId != userId) {
-                continue;
-            }
+        mProcessList.updateApplicationInfoLocked(packagesToUpdate, userId, updateFrameworkRes);
 
-            final int packageCount = app.pkgList.size();
-            for (int j = 0; j < packageCount; j++) {
-                final String packageName = app.pkgList.keyAt(j);
-                if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
-                    try {
-                        final ApplicationInfo ai = AppGlobals.getPackageManager()
-                                .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
-                        if (ai != null) {
-                            app.thread.scheduleApplicationInfoChanged(ai);
-                        }
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
-                                    packageName, app));
-                    }
-                }
-            }
-        }
         if (updateFrameworkRes) {
             // Update system server components that need to know about changed overlays. Because the
             // overlay is applied in ActivityThread, we need to serialize through its thread too.
@@ -20583,7 +19264,8 @@
     public void attachAgent(String process, String path) {
         try {
             synchronized (this) {
-                ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM, "attachAgent");
+                ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM,
+                        "attachAgent");
                 if (proc == null || proc.thread == null) {
                     throw new IllegalArgumentException("Unknown process: " + process);
                 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 9f768a8..692b2d4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -481,12 +481,12 @@
                 options.setLockTaskEnabled(true);
             }
             if (mWaitOption) {
-                result = mTaskInterface.startActivityAndWait(null, null, intent, mimeType,
+                result = mInternal.startActivityAndWait(null, null, intent, mimeType,
                         null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
                 res = result.result;
             } else {
-                res = mTaskInterface.startActivityAsUser(null, null, intent, mimeType,
+                res = mInternal.startActivityAsUser(null, null, intent, mimeType,
                         null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
             }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 12ed726..026c5cc 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3877,8 +3877,8 @@
         // The activity that we are finishing may be over the lock screen. In this case, we do not
         // want to consider activities that cannot be shown on the lock screen as running and should
         // proceed with finishing the activity if there is no valid next top running activity.
-        final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */);
+        final ActivityDisplay display = getDisplay();
+        final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */);
 
         if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
                 && next != null && !next.nowVisible) {
@@ -3902,23 +3902,25 @@
         if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r);
 
         r.setState(FINISHING, "finishCurrentActivityLocked");
-        final boolean finishingActivityInNonFocusedStack
-                = r.getStack() != mStackSupervisor.getTopDisplayFocusedStack()
-                && prevState == PAUSED && mode == FINISH_AFTER_VISIBLE;
+        final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
+                && prevState == PAUSED && (r.getStack() != display.getFocusedStack()
+                        || (next == null && display.topRunningActivity() == null));
 
         if (mode == FINISH_IMMEDIATELY
                 || (prevState == PAUSED
                     && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
-                || finishingActivityInNonFocusedStack
+                || finishingInNonFocusedStackOrNoRunning
                 || prevState == STOPPING
                 || prevState == STOPPED
                 || prevState == ActivityState.INITIALIZING) {
             r.makeFinishingLocked();
             boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
 
-            if (finishingActivityInNonFocusedStack) {
+            if (finishingInNonFocusedStackOrNoRunning) {
                 // Finishing activity that was in paused state and it was in not currently focused
-                // stack, need to make something visible in its place.
+                // stack, need to make something visible in its place. Also if the display does not
+                // have running activity, the configuration may need to be updated for restoring
+                // original orientation of the display.
                 mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 12c6dec..257a004 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -21,8 +21,12 @@
 import static android.Manifest.permission.START_ANY_ACTIVITY;
 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
 import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.ActivityManager.START_FLAG_DEBUG;
+import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
+import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
 import static android.app.WaitResult.INVALID_DELAY;
@@ -54,6 +58,21 @@
 import static android.view.Display.TYPE_VIRTUAL;
 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
+import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
+import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
+import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
+import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
+import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
+import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
@@ -76,22 +95,6 @@
 import static com.android.server.am.ActivityTaskManagerService.ANIMATE;
 import static com.android.server.am.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
 import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
-import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
-import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
-import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
-import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
-import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
-import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
@@ -113,6 +116,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
+import android.app.IApplicationThread;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
 import android.app.WaitResult;
@@ -789,8 +793,9 @@
     }
 
     boolean canStartHomeOnDisplay(ActivityInfo homeActivity, int displayId) {
-        if (displayId == DEFAULT_DISPLAY) {
-            // No restrictions to default display.
+        if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
+                && displayId == mService.mVr2dDisplayId)) {
+            // No restrictions to default display or vr 2d display.
             return true;
         }
 
@@ -1012,7 +1017,7 @@
                     if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
                             && processName.equals(activity.processName)) {
                         try {
-                            if (realStartActivityLocked(activity, (ProcessRecord) app.mOwner,
+                            if (realStartActivityLocked(activity, app,
                                     top == activity /* andResume */, true /* checkConfig */)) {
                                 didSomething = true;
                             }
@@ -1209,75 +1214,15 @@
     }
 
     ActivityRecord topRunningActivityLocked() {
-        return topRunningActivityLocked(false /* considerKeyguardState */);
-    }
-
-    /**
-     * Returns the top running activity in the focused stack. In the case the focused stack has no
-     * such activity, the next focusable stack on top of a display is returned.
-     * @param considerKeyguardState Indicates whether the locked state should be considered. if
-     *                            {@code true} and the keyguard is locked, only activities that
-     *                            can be shown on top of the keyguard will be considered.
-     * @return The top running activity. {@code null} if none is available.
-     */
-    ActivityRecord topRunningActivityLocked(boolean considerKeyguardState) {
-        final ActivityStack focusedStack = getTopDisplayFocusedStack();
-        ActivityRecord r = focusedStack.topRunningActivityLocked();
-        if (r != null && isValidTopRunningActivity(r, considerKeyguardState)) {
-            return r;
-        }
-
-        // Look in other non-focused and non-home stacks.
         for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-
-            // TODO: We probably want to consider the top fullscreen stack as we could have a pinned
-            // stack on top.
-            final ActivityStack topStack = display.getTopStack();
-
-            // Only consider focusable top stacks other than the current focused one.
-            if (topStack == null || !topStack.isFocusable() || topStack == focusedStack) {
-                continue;
-            }
-
-            final ActivityRecord topActivity = topStack.topRunningActivityLocked();
-
-            // Skip if no top activity.
-            if (topActivity == null) {
-                continue;
-            }
-
-
-            // This activity can be considered the top running activity if we are not
-            // considering the locked state, the keyguard isn't locked, or we can show when
-            // locked.
-            if (isValidTopRunningActivity(topActivity, considerKeyguardState)) {
+            final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
+            if (topActivity != null) {
                 return topActivity;
             }
         }
-
         return null;
     }
 
-    /**
-     * Verifies an {@link ActivityRecord} can be the top activity based on keyguard state and
-     * whether we are considering it.
-     */
-    private boolean isValidTopRunningActivity(ActivityRecord record,
-            boolean considerKeyguardState) {
-        if (!considerKeyguardState) {
-            return true;
-        }
-
-        final boolean keyguardLocked = getKeyguardController().isKeyguardLocked();
-
-        if (!keyguardLocked) {
-            return true;
-        }
-
-        return record.canShowWhenLocked();
-    }
-
     @VisibleForTesting
     void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
             @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
@@ -1299,20 +1244,17 @@
 
             // Don't debug things in the system process
             if (!aInfo.processName.equals("system")) {
-                if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
-                    mService.mAm.setDebugApp(aInfo.processName, true, false);
-                }
-
-                if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
-                    mService.mAm.setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
-                }
-
-                if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
-                    mService.mAm.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
-                }
-
-                if (profilerInfo != null) {
-                    mService.mAm.setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
+                if ((startFlags & (START_FLAG_DEBUG | START_FLAG_NATIVE_DEBUGGING
+                        | START_FLAG_TRACK_ALLOCATION)) != 0 || profilerInfo != null) {
+                    /**
+                     * Assume safe to call into AMS synchronously because the call that set these
+                     * flags should have originated from AMS which will already have its lock held.
+                     * @see ActivityManagerService#startActivityAndWait(IApplicationThread, String,
+                     * Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle, int)
+                     * TODO(b/80414790): Investigate a better way of untangling this.
+                     */
+                    mService.mAmInternal.setDebugFlagsForStartingActivity(
+                            aInfo, startFlags, profilerInfo);
                 }
             }
             final String intentLaunchToken = intent.getLaunchToken();
@@ -1359,7 +1301,7 @@
         return resolveActivity(intent, rInfo, startFlags, profilerInfo);
     }
 
-    final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
+    private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
             boolean andResume, boolean checkConfig) throws RemoteException {
 
         if (!allPausedActivitiesComplete()) {
@@ -1378,7 +1320,6 @@
         beginDeferResume();
 
         try {
-            final WindowProcessController proc = app.getWindowProcessController();
             r.startFreezingScreenLocked(proc, 0);
 
             // schedule launch ticks to collect information about slow apps.
@@ -1414,15 +1355,15 @@
 
             final int applicationInfoUid =
                     (r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
-            if ((r.userId != app.userId) || (r.appInfo.uid != applicationInfoUid)) {
+            if ((r.userId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
                 Slog.wtf(TAG,
                         "User ID for activity changing for " + r
                                 + " appInfo.uid=" + r.appInfo.uid
                                 + " info.ai.uid=" + applicationInfoUid
-                                + " old=" + r.app + " new=" + app);
+                                + " old=" + r.app + " new=" + proc);
             }
 
-            app.waitingToKill = null;
+            proc.clearWaitingToKill();
             r.launchCount++;
             r.lastLaunchTime = SystemClock.uptimeMillis();
 
@@ -1441,7 +1382,7 @@
             }
 
             try {
-                if (app.thread == null) {
+                if (!proc.hasThread()) {
                     throw new RemoteException();
                 }
                 List<ResultInfo> results = null;
@@ -1467,50 +1408,29 @@
                 r.forceNewConfig = false;
                 mService.getAppWarningsLocked().onStartActivity(r);
                 r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
-                ProfilerInfo profilerInfo = null;
-                if (mService.mAm.mProfileApp != null && mService.mAm.mProfileApp.equals(app.processName)) {
-                    if (mService.mAm.mProfileProc == null || mService.mAm.mProfileProc == app) {
-                        mService.mAm.mProfileProc = app;
-                        ProfilerInfo profilerInfoSvc = mService.mAm.mProfilerInfo;
-                        if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
-                            if (profilerInfoSvc.profileFd != null) {
-                                try {
-                                    profilerInfoSvc.profileFd = profilerInfoSvc.profileFd.dup();
-                                } catch (IOException e) {
-                                    profilerInfoSvc.closeFd();
-                                }
-                            }
+                ProfilerInfo profilerInfo = proc.onStartActivity(mService.mTopProcessState);
 
-                            profilerInfo = new ProfilerInfo(profilerInfoSvc);
-                        }
-                    }
-                }
-
-                app.hasShownUi = true;
-                app.setPendingUiClean(true);
-                app.forceProcessStateUpTo(mService.mTopProcessState);
                 // Because we could be starting an Activity in the system process this may not go
                 // across a Binder interface which would create a new Configuration. Consequently
                 // we have to always create a new Configuration here.
 
                 final MergedConfiguration mergedConfiguration = new MergedConfiguration(
-                        app.getWindowProcessController().getConfiguration(),
-                        r.getMergedOverrideConfiguration());
+                        proc.getConfiguration(), r.getMergedOverrideConfiguration());
                 r.setLastReportedConfiguration(mergedConfiguration);
 
                 logIfTransactionTooLarge(r.intent, r.icicle);
 
 
                 // Create activity launch transaction.
-                final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
-                        r.appToken);
+                final ClientTransaction clientTransaction = ClientTransaction.obtain(
+                        proc.getThread(), r.appToken);
                 clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                         System.identityHashCode(r), r.info,
                         // TODO: Have this take the merged configuration instead of separate global
                         // and override configs.
                         mergedConfiguration.getGlobalConfiguration(),
                         mergedConfiguration.getOverrideConfiguration(), r.compat,
-                        r.launchedFromPackage, task.voiceInteractor, app.getReportedProcState(),
+                        r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                         r.icicle, r.persistentState, results, newIntents,
                         mService.isNextTransitionForward(), profilerInfo));
 
@@ -1526,12 +1446,12 @@
                 // Schedule transaction.
                 mService.getLifecycleManager().scheduleTransaction(clientTransaction);
 
-                if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
+                if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
                         && mService.mHasHeavyWeightFeature) {
                     // This may be a heavy-weight process! Note that the package manager will ensure
                     // that only activity can run in the main process of the .apk, which is the only
                     // thing that will be considered heavy-weight.
-                    if (app.processName.equals(app.info.packageName)) {
+                    if (proc.mName.equals(proc.mInfo.packageName)) {
                         if (mService.mHeavyWeightProcess != null
                                 && mService.mHeavyWeightProcess != proc) {
                             Slog.w(TAG, "Starting new heavy weight process " + proc
@@ -1544,12 +1464,10 @@
 
             } catch (RemoteException e) {
                 if (r.launchFailed) {
-                    // This is the second time we failed -- finish activity
-                    // and give up.
+                    // This is the second time we failed -- finish activity and give up.
                     Slog.e(TAG, "Second failure launching "
-                            + r.intent.getComponent().flattenToShortString()
-                            + ", giving up", e);
-                    mService.mAm.appDiedLocked(app);
+                            + r.intent.getComponent().flattenToShortString() + ", giving up", e);
+                    proc.appDied();
                     stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
                             "2nd-crash", false);
                     return false;
@@ -1658,24 +1576,21 @@
         }
     }
 
-    void startSpecificActivityLocked(ActivityRecord r,
-            boolean andResume, boolean checkConfig) {
+    void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
         // Is this activity's application already running?
-        ProcessRecord app = mService.mAm.getProcessRecordLocked(r.processName,
-                r.info.applicationInfo.uid, true);
+        final WindowProcessController wpc =
+                mService.getProcessController(r.processName, r.info.applicationInfo.uid);
 
-        if (app != null && app.thread != null) {
+        if (wpc != null && wpc.hasThread()) {
             try {
-                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
+                if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
                         || !"android".equals(r.info.packageName)) {
-                    // Don't add this if it is a platform component that is marked
-                    // to run in multiple processes, because this is actually
-                    // part of the framework so doesn't make sense to track as a
-                    // separate apk in the process.
-                    app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode,
-                            mService.mAm.mProcessStats);
+                    // Don't add this if it is a platform component that is marked to run in
+                    // multiple processes, because this is actually part of the framework so doesn't
+                    // make sense to track as a separate apk in the process.
+                    wpc.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode);
                 }
-                realStartActivityLocked(r, app, andResume, checkConfig);
+                realStartActivityLocked(r, wpc, andResume, checkConfig);
                 return;
             } catch (RemoteException e) {
                 Slog.w(TAG, "Exception when starting activity "
@@ -1686,8 +1601,12 @@
             // restart the application.
         }
 
-        mService.mAm.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
-                "activity", r.intent.getComponent(), false, false, true);
+        // Post message to start process to avoid possible deadlock of calling into AMS with the
+        // ATMS lock held.
+        final Message msg = PooledLambda.obtainMessage(
+                ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
+                r.info.applicationInfo, true, "activity", r.intent.getComponent());
+        mService.mH.sendMessage(msg);
     }
 
     void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index afcf9f9..45a0652 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -53,6 +53,11 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
@@ -67,11 +72,6 @@
 import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityTaskManagerService.ANIMATE;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.am.EventLogTags.AM_NEW_INTENT;
 import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
@@ -1277,6 +1277,7 @@
 
         setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                 voiceInteractor);
+        final int preferredWindowingMode = mLaunchParams.mWindowingMode;
 
         // Do not start home activity if it cannot be launched on preferred display. We are not
         // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
@@ -1295,25 +1296,6 @@
 
         ActivityRecord reusedActivity = getReusableIntentActivity();
 
-        int preferredWindowingMode = WINDOWING_MODE_UNDEFINED;
-        int preferredLaunchDisplayId = DEFAULT_DISPLAY;
-        if (mOptions != null) {
-            preferredWindowingMode = mOptions.getLaunchWindowingMode();
-            preferredLaunchDisplayId = mOptions.getLaunchDisplayId();
-        }
-
-        // windowing mode and preferred launch display values from {@link LaunchParams} take
-        // priority over those specified in {@link ActivityOptions}.
-        if (!mLaunchParams.isEmpty()) {
-            if (mLaunchParams.hasPreferredDisplay()) {
-                preferredLaunchDisplayId = mLaunchParams.mPreferredDisplayId;
-            }
-
-            if (mLaunchParams.hasWindowingMode()) {
-                preferredWindowingMode = mLaunchParams.mWindowingMode;
-            }
-        }
-
         if (reusedActivity != null) {
             // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
             // still needs to be a lock task mode violation since the task gets cleared out and
@@ -1460,7 +1442,7 @@
             // Don't use mStartActivity.task to show the toast. We're not starting a new activity
             // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
             mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
-                    preferredLaunchDisplayId, topStack);
+                    mPreferredDisplayId, topStack);
 
             return START_DELIVERED_TO_TOP;
         }
@@ -1540,7 +1522,7 @@
         mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
 
         mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
-                preferredLaunchDisplayId, mTargetStack);
+                mPreferredDisplayId, mTargetStack);
 
         return START_SUCCESS;
     }
@@ -1614,7 +1596,6 @@
         } else {
             mPreferredDisplayId = DEFAULT_DISPLAY;
         }
-        ensureValidPreferredDisplayId(r);
 
         mLaunchMode = r.launchMode;
 
@@ -1707,24 +1688,6 @@
         mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
     }
 
-    /**
-     * Ensure preferred display ID matches the starting activity.
-     */
-    private void ensureValidPreferredDisplayId(ActivityRecord startingActivity) {
-        // Check if the Activity is a VR activity. If so, the activity should be launched in
-        // main display.
-        if (startingActivity != null && startingActivity.requestedVrComponent != null) {
-            mPreferredDisplayId = DEFAULT_DISPLAY;
-        }
-
-        // Get the virtual display ID from ActivityStackManagerService. If that's set we should
-        // always use that.
-        final int displayId = mService.mVr2dDisplayId;
-        if (displayId != INVALID_DISPLAY) {
-            mPreferredDisplayId = displayId;
-        }
-    }
-
     private void sendNewTaskResultRequestIfNeeded() {
         final ActivityStack sourceStack = mStartActivity.resultTo != null
                 ? mStartActivity.resultTo.getStack() : null;
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 2d27017..f79d9aa 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -4670,14 +4670,6 @@
         return kept;
     }
 
-    /**
-     * Returns true if this configuration change is interesting enough to send an
-     * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
-     */
-    private static boolean isSplitConfigurationChange(int configDiff) {
-        return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
-    }
-
     /** Update default (global) configuration and notify listeners about changes. */
     private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
             boolean persistent, int userId, boolean deferResume) {
@@ -4777,38 +4769,10 @@
             app.onConfigurationChanged(configCopy);
         }
 
-        Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING
-                | Intent.FLAG_RECEIVER_FOREGROUND
-                | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-        mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                UserHandle.USER_ALL);
-        if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
-            intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
-            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
-                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
-                    | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-            if (initLocale || !mAm.mProcessesReady) {
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-            }
-            mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                    OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                    UserHandle.USER_ALL);
-        }
-
-        // Send a broadcast to PackageInstallers if the configuration change is interesting
-        // for the purposes of installing additional splits.
-        if (!initLocale && isSplitConfigurationChange(changes)) {
-            intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
-            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
-                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-
-            // Typically only app stores will have this permission.
-            String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES };
-            mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions,
-                    OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
-        }
+        final Message msg = PooledLambda.obtainMessage(
+                ActivityManagerInternal::broadcastGlobalConfigurationChanged,
+                mAmInternal, changes, initLocale);
+        mH.sendMessage(msg);
 
         // Override configuration of the default display duplicates global config, so we need to
         // update it also. This will also notify WindowManager about changes.
@@ -4877,8 +4841,12 @@
             if (isDensityChange && displayId == DEFAULT_DISPLAY) {
                 mAppWarnings.onDensityChanged();
 
-                mAm.killAllBackgroundProcessesExcept(N,
-                        ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+                // Post message to start process to avoid possible deadlock of calling into AMS with
+                // the ATMS lock held.
+                final Message msg = PooledLambda.obtainMessage(
+                        ActivityManagerInternal::killAllBackgroundProcessesExcept, mAmInternal,
+                        N, ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+                mH.sendMessage(msg);
             }
         }
 
@@ -5446,7 +5414,7 @@
         return newInfo;
     }
 
-    private WindowProcessController getProcessController(String processName, int uid) {
+    WindowProcessController getProcessController(String processName, int uid) {
         if (uid == SYSTEM_UID) {
             // The system gets to run in any process. If there are multiple processes with the same
             // uid, just pick the first (this should never happen).
@@ -5896,6 +5864,9 @@
         @Override
         public void finishHeavyWeightApp() {
             synchronized (mGlobalLock) {
+                if (mHeavyWeightProcess != null) {
+                    mHeavyWeightProcess.finishActivities();
+                }
                 ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(
                         mHeavyWeightProcess);
             }
@@ -6247,20 +6218,12 @@
                             return;
                         }
                     }
-                    Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                            | Intent.FLAG_RECEIVER_FOREGROUND);
-                    if (reason != null) {
-                        intent.putExtra("reason", reason);
-                    }
                     mWindowManager.closeSystemDialogs(reason);
 
                     mStackSupervisor.closeSystemDialogsLocked();
-
-                    mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                            OP_NONE, null, false, false,
-                            -1, SYSTEM_UID, UserHandle.USER_ALL);
                 }
+                // Call into AM outside the synchronized block.
+                mAmInternal.broadcastCloseSystemDialogs(reason);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 83c4ab5..e8ec057 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -121,7 +121,7 @@
                 proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname);
                 for (int i = 0; i < uidCount; i++) {
                     final int puid = uids.keyAt(i);
-                    final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+                    final ProcessRecord r = mService.getProcessNames().get(pname, puid);
                     if (dumpPackage != null && (r == null || !r.pkgList.containsKey(dumpPackage))) {
                         continue;
                     }
@@ -148,7 +148,7 @@
                 proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
                 for (int i = 0; i < uidCount; i++) {
                     final int puid = uids.keyAt(i);
-                    final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+                    final ProcessRecord r = mService.getProcessNames().get(pname, puid);
                     if (dumpPackage != null && (r == null
                             || !r.pkgList.containsKey(dumpPackage))) {
                         continue;
@@ -181,7 +181,7 @@
                 final int uidCount = uids.size();
                 for (int i = 0; i < uidCount; i++) {
                     final int puid = uids.keyAt(i);
-                    final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+                    final ProcessRecord r = mService.getProcessNames().get(pname, puid);
                     if (dumpPackage != null && (r == null
                             || !r.pkgList.containsKey(dumpPackage))) {
                         continue;
@@ -211,7 +211,7 @@
                 final int uidCount = uids.size();
                 for (int i = 0; i < uidCount; i++) {
                     final int puid = uids.keyAt(i);
-                    final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+                    final ProcessRecord r = mService.getProcessNames().get(pname, puid);
                     if (dumpPackage != null && (r == null
                             || !r.pkgList.containsKey(dumpPackage))) {
                         continue;
@@ -471,7 +471,7 @@
                 stopReportingCrashesLocked(r);
             }
             if (res == AppErrorDialog.RESTART) {
-                mService.removeProcessLocked(r, false, true, "crash");
+                mService.mProcessList.removeProcessLocked(r, false, true, "crash");
                 if (taskId != INVALID_TASK_ID) {
                     try {
                         mService.mActivityTaskManager.startActivityFromRecents(taskId,
@@ -489,7 +489,7 @@
                     // Kill it with fire!
                     mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
                     if (!r.isPersistent()) {
-                        mService.removeProcessLocked(r, false, false, "crash");
+                        mService.mProcessList.removeProcessLocked(r, false, false, "crash");
                         mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
                     }
                 } finally {
@@ -551,7 +551,7 @@
                     } else {
                         // Huh.
                         Process.killProcess(pid);
-                        ActivityManagerService.killProcessGroup(uid, pid);
+                        ProcessList.killProcessGroup(uid, pid);
                     }
                 }
                 return true;
@@ -714,7 +714,7 @@
                 // Don't let services in this process be restarted and potentially
                 // annoy the user repeatedly.  Unless it is persistent, since those
                 // processes run critical code.
-                mService.removeProcessLocked(app, false, tryAgain, "crash");
+                mService.mProcessList.removeProcessLocked(app, false, tryAgain, "crash");
                 mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
                 if (!showBackground) {
                     return false;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a13cf4d..a0977be 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -286,7 +286,7 @@
         r.curApp = app;
         app.curReceivers.add(r);
         app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
-        mService.updateLruProcessLocked(app, false, null);
+        mService.mProcessList.updateLruProcessLocked(app, false, null);
         if (!skipOomAdj) {
             mService.updateOomAdjLocked();
         }
@@ -892,7 +892,7 @@
                     isDead = proc == null || proc.isCrashing();
                 }
             } else {
-                final ProcessRecord proc = mService.mProcessNames.get(
+                final ProcessRecord proc = mService.mProcessList.mProcessNames.get(
                         mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
                 isDead = proc == null || !proc.pendingStart;
             }
diff --git a/services/core/java/com/android/server/am/LaunchParamsController.java b/services/core/java/com/android/server/am/LaunchParamsController.java
index 218d908..68e897f 100644
--- a/services/core/java/com/android/server/am/LaunchParamsController.java
+++ b/services/core/java/com/android/server/am/LaunchParamsController.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
@@ -97,6 +98,15 @@
                     break;
             }
         }
+
+        if (activity != null && activity.requestedVrComponent != null) {
+            // Check if the Activity is a VR activity. If so, it should be launched in main display.
+            result.mPreferredDisplayId = DEFAULT_DISPLAY;
+        } else if (mService.mVr2dDisplayId != INVALID_DISPLAY) {
+            // Get the virtual display ID from ActivityTaskManagerService. If that's set we
+            // should always use that.
+            result.mPreferredDisplayId = mService.mVr2dDisplayId;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 9e7ce32..3f172cc 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -16,19 +16,79 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
+import static android.os.Process.FIRST_ISOLATED_UID;
+import static android.os.Process.LAST_ISOLATED_UID;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+import static android.os.Process.getFreeMemory;
+import static android.os.Process.getTotalMemory;
+import static android.os.Process.killProcessQuiet;
+import static android.os.Process.startWebView;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
+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.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.PERSISTENT_MASK;
+import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT;
+import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_MSG;
+import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_WITH_WRAPPER;
+import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.am.ActivityManagerService.TAG_LRU;
+import static com.android.server.am.ActivityManagerService.TAG_PROCESSES;
+import static com.android.server.am.ActivityManagerService.TAG_PSS;
+import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
 
+import dalvik.system.VMRuntime;
+
+import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 import android.app.ActivityManager;
+import android.app.AppGlobals;
 import android.app.AppProtoEnums;
+import android.app.IApplicationThread;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.net.Uri;
+import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
+import android.os.FactoryTest;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.StrictMode;
 import android.os.SystemClock;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ProcessMap;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.Zygote;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.MemInfoReader;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.Watchdog;
+import com.android.server.pm.dex.DexManager;
 import com.android.server.wm.WindowManagerService;
 
 import android.content.res.Resources;
@@ -36,7 +96,15 @@
 import android.os.SystemProperties;
 import android.net.LocalSocketAddress;
 import android.net.LocalSocket;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.LongSparseArray;
 import android.util.Slog;
+import android.util.SparseArray;
+import android.util.StatsLog;
 import android.view.Display;
 
 /**
@@ -47,7 +115,7 @@
 
     // The minimum time we allow between crashes, for us to consider this
     // application to be bad and stop and its services and reject broadcasts.
-    static final int MIN_CRASH_INTERVAL = 60*1000;
+    static final int MIN_CRASH_INTERVAL = 60 * 1000;
 
     // OOM adjustments for processes in various states:
 
@@ -129,7 +197,7 @@
     static final int NATIVE_ADJ = -1000;
 
     // Memory pages are 4K.
-    static final int PAGE_SIZE = 4*1024;
+    static final int PAGE_SIZE = 4 * 1024;
 
     // Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE
     static final int SCHED_GROUP_BACKGROUND = 0;
@@ -148,7 +216,7 @@
     static final int MIN_CACHED_APPS = 2;
 
     // We allow empty processes to stick around for at most 30 minutes.
-    static final long MAX_EMPTY_TIME = 30*60*1000;
+    static final long MAX_EMPTY_TIME = 30 * 60 * 1000;
 
     // Threshold of number of cached+empty where we consider memory critical.
     static final int TRIM_CRITICAL_THRESHOLD = 3;
@@ -168,6 +236,12 @@
     static final byte LMK_PROCREMOVE = 2;
     static final byte LMK_PROCPURGE = 3;
 
+    ActivityManagerService mService = null;
+
+    // To kill process groups asynchronously
+    static KillHandler sKillHandler = null;
+    static ServiceThread sKillThread = null;
+
     // These are the various interesting memory levels that we will give to
     // the OOM killer.  Note that the OOM killer only supports 6 slots, so we
     // can't give it a different value for every possible kind of process.
@@ -199,6 +273,123 @@
     private static LocalSocket sLmkdSocket;
     private static OutputStream sLmkdOutputStream;
 
+    /**
+     * Temporary to avoid allocations.  Protected by main lock.
+     */
+    @GuardedBy("mService")
+    final StringBuilder mStringBuilder = new StringBuilder(256);
+
+    /**
+     * A global counter for generating sequence numbers.
+     * This value will be used when incrementing sequence numbers in individual uidRecords.
+     *
+     * Having a global counter ensures that seq numbers are monotonically increasing for a
+     * particular uid even when the uidRecord is re-created.
+     */
+    @GuardedBy("mService")
+    @VisibleForTesting
+    long mProcStateSeqCounter = 0;
+
+    /**
+     * A global counter for generating sequence numbers to uniquely identify pending process starts.
+     */
+    @GuardedBy("mService")
+    private long mProcStartSeqCounter = 0;
+
+    /**
+     * Contains {@link ProcessRecord} objects for pending process starts.
+     *
+     * Mapping: {@link #mProcStartSeqCounter} -> {@link ProcessRecord}
+     */
+    @GuardedBy("mService")
+    final LongSparseArray<ProcessRecord> mPendingStarts = new LongSparseArray<>();
+
+    /**
+     * List of running applications, sorted by recent usage.
+     * The first entry in the list is the least recently used.
+     */
+    final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
+
+    /**
+     * Where in mLruProcesses that the processes hosting activities start.
+     */
+    int mLruProcessActivityStart = 0;
+
+    /**
+     * Where in mLruProcesses that the processes hosting services start.
+     * This is after (lower index) than mLruProcessesActivityStart.
+     */
+    int mLruProcessServiceStart = 0;
+
+    /**
+     * Current sequence id for process LRU updating.
+     */
+    int mLruSeq = 0;
+
+    /**
+     * The currently running isolated processes.
+     */
+    final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
+
+    /**
+     * Counter for assigning isolated process uids, to avoid frequently reusing the
+     * same ones.
+     */
+    int mNextIsolatedProcessUid = 0;
+
+    /**
+     * Processes that are being forcibly torn down.
+     */
+    final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
+
+    /**
+     * All of the applications we currently have running organized by name.
+     * The keys are strings of the application package name (as
+     * returned by the package manager), and the keys are ApplicationRecord
+     * objects.
+     */
+    final MyProcessMap mProcessNames = new MyProcessMap();
+
+    final class MyProcessMap extends ProcessMap<ProcessRecord> {
+        @Override
+        public ProcessRecord put(String name, int uid, ProcessRecord value) {
+            final ProcessRecord r = super.put(name, uid, value);
+            mService.mAtmInternal.onProcessAdded(r.getWindowProcessController());
+            return r;
+        }
+
+        @Override
+        public ProcessRecord remove(String name, int uid) {
+            final ProcessRecord r = super.remove(name, uid);
+            mService.mAtmInternal.onProcessRemoved(name, uid);
+            return r;
+        }
+    }
+
+    final class KillHandler extends Handler {
+        static final int KILL_PROCESS_GROUP_MSG = 4000;
+
+        public KillHandler(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case KILL_PROCESS_GROUP_MSG:
+                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
+                    Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
+                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                    break;
+
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
+    ////////////////////  END FIELDS  ////////////////////
+
     ProcessList() {
         MemInfoReader minfo = new MemInfoReader();
         minfo.readMemInfo();
@@ -206,6 +397,16 @@
         updateOomLevels(0, 0, false);
     }
 
+    void init(ActivityManagerService service) {
+        mService = service;
+        if (sKillHandler == null) {
+            sKillThread = new ServiceThread(TAG + ":kill",
+                    THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+            sKillThread.start();
+            sKillHandler = new KillHandler(sKillThread.getLooper());
+        }
+    }
+
     void applyDisplaySize(WindowManagerService wm) {
         if (!mHaveDisplaySize) {
             Point p = new Point();
@@ -221,12 +422,12 @@
     private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
         // Scale buckets from avail memory: at 300MB we use the lowest values to
         // 700MB or more for the top values.
-        float scaleMem = ((float)(mTotalMemMb-350))/(700-350);
+        float scaleMem = ((float) (mTotalMemMb - 350)) / (700 - 350);
 
         // Scale buckets from screen size.
-        int minSize = 480*800;  //  384000
-        int maxSize = 1280*800; // 1024000  230400 870400  .264
-        float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
+        int minSize = 480 * 800;  //  384000
+        int maxSize = 1280 * 800; // 1024000  230400 870400  .264
+        float scaleDisp = ((float)(displayWidth * displayHeight) - minSize) / (maxSize - minSize);
         if (false) {
             Slog.i("XXXXXX", "scaleMem=" + scaleMem);
             Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth
@@ -246,27 +447,27 @@
 
         final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
 
-        for (int i=0; i<mOomAdj.length; i++) {
+        for (int i = 0; i < mOomAdj.length; i++) {
             int low = mOomMinFreeLow[i];
             int high = mOomMinFreeHigh[i];
             if (is64bit) {
                 // Increase the high min-free levels for cached processes for 64-bit
-                if (i == 4) high = (high*3)/2;
-                else if (i == 5) high = (high*7)/4;
+                if (i == 4) high = (high * 3) / 2;
+                else if (i == 5) high = (high * 7) / 4;
             }
-            mOomMinFree[i] = (int)(low + ((high-low)*scale));
+            mOomMinFree[i] = (int)(low + ((high - low) * scale));
         }
 
         if (minfree_abs >= 0) {
-            for (int i=0; i<mOomAdj.length; i++) {
+            for (int i = 0; i < mOomAdj.length; i++) {
                 mOomMinFree[i] = (int)((float)minfree_abs * mOomMinFree[i]
                         / mOomMinFree[mOomAdj.length - 1]);
             }
         }
 
         if (minfree_adj != 0) {
-            for (int i=0; i<mOomAdj.length; i++) {
-                mOomMinFree[i] += (int)((float)minfree_adj * mOomMinFree[i]
+            for (int i = 0; i < mOomAdj.length; i++) {
+                mOomMinFree[i] += (int)((float) minfree_adj * mOomMinFree[i]
                         / mOomMinFree[mOomAdj.length - 1]);
                 if (mOomMinFree[i] < 0) {
                     mOomMinFree[i] = 0;
@@ -277,13 +478,15 @@
         // The maximum size we will restore a process from cached to background, when under
         // memory duress, is 1/3 the size we have reserved for kernel caches and other overhead
         // before killing background processes.
-        mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024) / 3;
+        mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ) / 1024) / 3;
 
         // Ask the kernel to try to keep enough memory free to allocate 3 full
         // screen 32bpp buffers without entering direct reclaim.
         int reserve = displayWidth * displayHeight * 4 * 3 / 1024;
-        int reserve_adj = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);
-        int reserve_abs = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
+        int reserve_adj = Resources.getSystem().getInteger(
+                com.android.internal.R.integer.config_extraFreeKbytesAdjust);
+        int reserve_abs = Resources.getSystem().getInteger(
+                com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
 
         if (reserve_abs >= 0) {
             reserve = reserve_abs;
@@ -297,10 +500,10 @@
         }
 
         if (write) {
-            ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
+            ByteBuffer buf = ByteBuffer.allocate(4 * (2 * mOomAdj.length + 1));
             buf.putInt(LMK_TARGET);
-            for (int i=0; i<mOomAdj.length; i++) {
-                buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
+            for (int i = 0; i < mOomAdj.length; i++) {
+                buf.putInt((mOomMinFree[i] * 1024)/PAGE_SIZE);
                 buf.putInt(mOomAdj[i]);
             }
 
@@ -320,7 +523,7 @@
             if (space == null) return prefix;
             return prefix + "  ";
         }
-        return prefix + "+" + Integer.toString(val-base);
+        return prefix + "+" + Integer.toString(val - base);
     }
 
     public static String makeOomAdjString(int setAdj) {
@@ -477,7 +680,7 @@
     }
 
     public static void appendRamKb(StringBuilder sb, long ramKb) {
-        for (int j=0, fact=10; j<6; j++, fact*=10) {
+        for (int j = 0, fact = 10; j < 6; j++, fact *= 10) {
             if (ramKb < fact) {
                 sb.append(' ');
             }
@@ -737,12 +940,12 @@
     }
 
     long getMemLevel(int adjustment) {
-        for (int i=0; i<mOomAdj.length; i++) {
+        for (int i = 0; i < mOomAdj.length; i++) {
             if (adjustment <= mOomAdj[i]) {
                 return mOomMinFree[i] * 1024;
             }
         }
-        return mOomMinFree[mOomAdj.length-1] * 1024;
+        return mOomMinFree[mOomAdj.length - 1] * 1024;
     }
 
     /**
@@ -858,4 +1061,1515 @@
             }
         }
     }
+
+    static void killProcessGroup(int uid, int pid) {
+        /* static; one-time init here */
+        if (sKillHandler != null) {
+            sKillHandler.sendMessage(
+                    sKillHandler.obtainMessage(KillHandler.KILL_PROCESS_GROUP_MSG, uid, pid));
+        } else {
+            Slog.w(TAG, "Asked to kill process group before system bringup!");
+            Process.killProcessGroup(uid, pid);
+        }
+    }
+
+    final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean
+            keepIfLarge) {
+        if (uid == SYSTEM_UID) {
+            // The system gets to run in any process.  If there are multiple
+            // processes with the same uid, just pick the first (this
+            // should never happen).
+            SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName);
+            if (procs == null) return null;
+            final int procCount = procs.size();
+            for (int i = 0; i < procCount; i++) {
+                final int procUid = procs.keyAt(i);
+                if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
+                    // Don't use an app process or different user process for system component.
+                    continue;
+                }
+                return procs.valueAt(i);
+            }
+        }
+        ProcessRecord proc = mProcessNames.get(processName, uid);
+        if (false && proc != null && !keepIfLarge
+                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
+                && proc.lastCachedPss >= 4000) {
+            // Turn this condition on to cause killing to happen regularly, for testing.
+            if (proc.baseProcessTracker != null) {
+                proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
+                for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                    ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+                    StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+                            proc.info.uid,
+                            holder.state.getName(),
+                            holder.state.getPackage(),
+                            proc.lastCachedPss, holder.appVersion);
+                }
+            }
+            proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
+        } else if (proc != null && !keepIfLarge
+                && mService.mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
+                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+            if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc
+                    .lastCachedPss);
+            if (proc.lastCachedPss >= getCachedRestoreThresholdKb()) {
+                if (proc.baseProcessTracker != null) {
+                    proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList,
+                            proc.lastCachedPss);
+                    for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                        ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+                        StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+                                proc.info.uid,
+                                holder.state.getName(),
+                                holder.state.getPackage(),
+                                proc.lastCachedPss, holder.appVersion);
+                    }
+                }
+                proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
+            }
+        }
+        return proc;
+    }
+
+    void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
+        final long homeAppMem = getMemLevel(HOME_APP_ADJ);
+        final long cachedAppMem = getMemLevel(CACHED_APP_MIN_ADJ);
+        outInfo.availMem = getFreeMemory();
+        outInfo.totalMem = getTotalMemory();
+        outInfo.threshold = homeAppMem;
+        outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
+        outInfo.hiddenAppThreshold = cachedAppMem;
+        outInfo.secondaryServerThreshold = getMemLevel(SERVICE_ADJ);
+        outInfo.visibleAppThreshold = getMemLevel(VISIBLE_APP_ADJ);
+        outInfo.foregroundAppThreshold = getMemLevel(FOREGROUND_APP_ADJ);
+    }
+
+    ProcessRecord findAppProcessLocked(IBinder app, String reason) {
+        final int NP = mProcessNames.getMap().size();
+        for (int ip = 0; ip < NP; ip++) {
+            SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+            final int NA = apps.size();
+            for (int ia = 0; ia < NA; ia++) {
+                ProcessRecord p = apps.valueAt(ia);
+                if (p.thread != null && p.thread.asBinder() == app) {
+                    return p;
+                }
+            }
+        }
+
+        Slog.w(TAG, "Can't find mystery application for " + reason
+                + " from pid=" + Binder.getCallingPid()
+                + " uid=" + Binder.getCallingUid() + ": " + app);
+        return null;
+    }
+
+    private void checkSlow(long startTime, String where) {
+        long now = SystemClock.uptimeMillis();
+        if ((now - startTime) > 50) {
+            // If we are taking more than 50ms, log about it.
+            Slog.w(TAG, "Slow operation: " + (now - startTime) + "ms so far, now at " + where);
+        }
+    }
+
+    /**
+     * @return {@code true} if process start is successful, false otherwise.
+     * @param app
+     * @param hostingType
+     * @param hostingNameStr
+     * @param disableHiddenApiChecks
+     * @param abiOverride
+     */
+    @GuardedBy("mService")
+    boolean startProcessLocked(ProcessRecord app, String hostingType,
+            String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
+        if (app.pendingStart) {
+            return true;
+        }
+        long startTime = SystemClock.elapsedRealtime();
+        if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
+            checkSlow(startTime, "startProcess: removing from pids map");
+            synchronized (mService.mPidsSelfLocked) {
+                mService.mPidsSelfLocked.remove(app.pid);
+                mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
+            }
+            checkSlow(startTime, "startProcess: done removing from pids map");
+            app.setPid(0);
+        }
+
+        if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v(
+                TAG_PROCESSES,
+                "startProcessLocked removing on hold: " + app);
+        mService.mProcessesOnHold.remove(app);
+
+        checkSlow(startTime, "startProcess: starting to update cpu stats");
+        mService.updateCpuStats();
+        checkSlow(startTime, "startProcess: done updating cpu stats");
+
+        try {
+            try {
+                final int userId = UserHandle.getUserId(app.uid);
+                AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+
+            int uid = app.uid;
+            int[] gids = null;
+            int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
+            if (!app.isolated) {
+                int[] permGids = null;
+                try {
+                    checkSlow(startTime, "startProcess: getting gids from package manager");
+                    final IPackageManager pm = AppGlobals.getPackageManager();
+                    permGids = pm.getPackageGids(app.info.packageName,
+                            MATCH_DIRECT_BOOT_AUTO, app.userId);
+                    StorageManagerInternal storageManagerInternal = LocalServices.getService(
+                            StorageManagerInternal.class);
+                    mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
+                            app.info.packageName);
+                } catch (RemoteException e) {
+                    throw e.rethrowAsRuntimeException();
+                }
+
+                /*
+                 * Add shared application and profile GIDs so applications can share some
+                 * resources like shared libraries and access user-wide resources
+                 */
+                if (ArrayUtils.isEmpty(permGids)) {
+                    gids = new int[3];
+                } else {
+                    gids = new int[permGids.length + 3];
+                    System.arraycopy(permGids, 0, gids, 3, permGids.length);
+                }
+                gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
+                gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
+                gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
+
+                // Replace any invalid GIDs
+                if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
+                if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
+            }
+            checkSlow(startTime, "startProcess: building args");
+            if (mService.mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
+                uid = 0;
+            }
+            int runtimeFlags = 0;
+            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
+                runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
+                // Also turn on CheckJNI for debuggable apps. It's quite
+                // awkward to turn on otherwise.
+                runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+            }
+            // Run the app in safe mode if its manifest requests so or the
+            // system is booted in safe mode.
+            if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
+                    mService.mSafeMode == true) {
+                runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
+            }
+            if ("1".equals(SystemProperties.get("debug.checkjni"))) {
+                runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+            }
+            String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
+            if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
+                runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
+            }
+            String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
+            if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
+                runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
+            }
+            if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
+                runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
+            }
+            if ("1".equals(SystemProperties.get("debug.assert"))) {
+                runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;
+            }
+            if (mService.mNativeDebuggingApp != null
+                    && mService.mNativeDebuggingApp.equals(app.processName)) {
+                // Enable all debug flags required by the native debugger.
+                runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT;          // Don't interpret anything
+                runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
+                runtimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;   // Disbale optimizations
+                mService.mNativeDebuggingApp = null;
+            }
+
+            if (app.info.isPrivilegedApp() &&
+                    DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
+                runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
+            }
+
+            if (!disableHiddenApiChecks && !mService.mHiddenApiBlacklist.isDisabled()) {
+                app.info.maybeUpdateHiddenApiEnforcementPolicy(
+                        mService.mHiddenApiBlacklist.getPolicyForPrePApps(),
+                        mService.mHiddenApiBlacklist.getPolicyForPApps());
+                @ApplicationInfo.HiddenApiEnforcementPolicy int policy =
+                        app.info.getHiddenApiEnforcementPolicy();
+                int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
+                if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
+                    throw new IllegalStateException("Invalid API policy: " + policy);
+                }
+                runtimeFlags |= policyBits;
+            }
+
+            String invokeWith = null;
+            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                // Debuggable apps may include a wrapper script with their library directory.
+                String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
+                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+                try {
+                    if (new File(wrapperFileName).exists()) {
+                        invokeWith = "/system/bin/logwrapper " + wrapperFileName;
+                    }
+                } finally {
+                    StrictMode.setThreadPolicy(oldPolicy);
+                }
+            }
+
+            String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
+            if (requiredAbi == null) {
+                requiredAbi = Build.SUPPORTED_ABIS[0];
+            }
+
+            String instructionSet = null;
+            if (app.info.primaryCpuAbi != null) {
+                instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
+            }
+
+            app.gids = gids;
+            app.setRequiredAbi(requiredAbi);
+            app.instructionSet = instructionSet;
+
+            // the per-user SELinux context must be set
+            if (TextUtils.isEmpty(app.info.seInfoUser)) {
+                Slog.wtf(ActivityManagerService.TAG, "SELinux tag not defined",
+                        new IllegalStateException("SELinux tag not defined for "
+                                + app.info.packageName + " (uid " + app.uid + ")"));
+            }
+            final String seInfo = app.info.seInfo
+                    + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
+            // Start the process.  It will either succeed and return a result containing
+            // the PID of the new process, or else throw a RuntimeException.
+            final String entryPoint = "android.app.ActivityThread";
+
+            return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
+                    runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
+                    startTime);
+        } catch (RuntimeException e) {
+            Slog.e(ActivityManagerService.TAG, "Failure starting process " + app.processName, e);
+
+            // Something went very wrong while trying to start this process; one
+            // common case is when the package is frozen due to an active
+            // upgrade. To recover, clean up any active bookkeeping related to
+            // starting this process. (We already invoked this method once when
+            // the package was initially frozen through KILL_APPLICATION_MSG, so
+            // it doesn't hurt to use it again.)
+            mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
+                    false, false, true, false, false, app.userId, "start failure");
+            return false;
+        }
+    }
+
+    @GuardedBy("mService")
+    boolean startProcessLocked(String hostingType, String hostingNameStr,
+            String entryPoint,
+            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
+            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
+            long startTime) {
+        app.pendingStart = true;
+        app.killedByAm = false;
+        app.removed = false;
+        app.killed = false;
+        final long startSeq = app.startSeq = ++mProcStartSeqCounter;
+        app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
+        if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
+            if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
+                    "Posting procStart msg for " + app.toShortString());
+            mService.mProcStartHandler.post(() -> {
+                try {
+                    synchronized (mService) {
+                        final String reason = isProcStartValidLocked(app, startSeq);
+                        if (reason != null) {
+                            Slog.w(TAG_PROCESSES, app + " not valid anymore,"
+                                    + " don't start process, " + reason);
+                            app.pendingStart = false;
+                            return;
+                        }
+                        app.setUsingWrapper(invokeWith != null
+                                || SystemProperties.get("wrap." + app.processName) != null);
+                        mPendingStarts.put(startSeq, app);
+                    }
+                    final Process.ProcessStartResult startResult = startProcess(app.hostingType,
+                            entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
+                            app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
+                    synchronized (mService) {
+                        handleProcessStartedLocked(app, startResult, startSeq);
+                    }
+                } catch (RuntimeException e) {
+                    synchronized (mService) {
+                        Slog.e(ActivityManagerService.TAG, "Failure starting process "
+                                + app.processName, e);
+                        mPendingStarts.remove(startSeq);
+                        app.pendingStart = false;
+                        mService.forceStopPackageLocked(app.info.packageName,
+                                UserHandle.getAppId(app.uid),
+                                false, false, true, false, false, app.userId, "start failure");
+                    }
+                }
+            });
+            return true;
+        } else {
+            try {
+                final Process.ProcessStartResult startResult = startProcess(hostingType,
+                        entryPoint, app,
+                        uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
+                        invokeWith, startTime);
+                handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
+                        startSeq, false);
+            } catch (RuntimeException e) {
+                Slog.e(ActivityManagerService.TAG, "Failure starting process "
+                        + app.processName, e);
+                app.pendingStart = false;
+                mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
+                        false, false, true, false, false, app.userId, "start failure");
+            }
+            return app.pid > 0;
+        }
+    }
+
+    private Process.ProcessStartResult startProcess(String hostingType, String entryPoint,
+            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
+            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
+            long startTime) {
+        try {
+            final String[] packageNames = mService.mContext.getPackageManager()
+                    .getPackagesForUid(uid);
+            final String[] visibleVolIds = LocalServices.getService(StorageManagerInternal.class)
+                    .getVisibleVolumesForUser(UserHandle.getUserId(uid));
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
+                    app.processName);
+            checkSlow(startTime, "startProcess: asking zygote to start proc");
+            final Process.ProcessStartResult startResult;
+            if (hostingType.equals("webview_service")) {
+                startResult = startWebView(entryPoint,
+                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
+                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
+                        app.info.dataDir, null, app.info.packageName,
+                        packageNames, visibleVolIds,
+                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+            } else {
+                startResult = Process.start(entryPoint,
+                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
+                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
+                        app.info.dataDir, invokeWith, app.info.packageName,
+                        packageNames, visibleVolIds,
+                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+            }
+            checkSlow(startTime, "startProcess: returned from zygote!");
+            return startResult;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    @GuardedBy("mService")
+    final void startProcessLocked(ProcessRecord app,
+            String hostingType, String hostingNameStr) {
+        startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
+    }
+
+    @GuardedBy("mService")
+    final boolean startProcessLocked(ProcessRecord app,
+            String hostingType, String hostingNameStr, String abiOverride) {
+        return startProcessLocked(app, hostingType, hostingNameStr,
+                false /* disableHiddenApiChecks */, abiOverride);
+    }
+
+    @GuardedBy("mService")
+    final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
+            boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
+            boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
+            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
+        long startTime = SystemClock.elapsedRealtime();
+        ProcessRecord app;
+        if (!isolated) {
+            app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
+            checkSlow(startTime, "startProcess: after getProcessRecord");
+
+            if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
+                // If we are in the background, then check to see if this process
+                // is bad.  If so, we will just silently fail.
+                if (mService.mAppErrors.isBadProcessLocked(info)) {
+                    if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+                            + "/" + info.processName);
+                    return null;
+                }
+            } else {
+                // When the user is explicitly starting a process, then clear its
+                // crash count so that we won't make it bad until they see at
+                // least one crash dialog again, and make the process good again
+                // if it had been bad.
+                if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+                        + "/" + info.processName);
+                mService.mAppErrors.resetProcessCrashTimeLocked(info);
+                if (mService.mAppErrors.isBadProcessLocked(info)) {
+                    EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
+                            UserHandle.getUserId(info.uid), info.uid,
+                            info.processName);
+                    mService.mAppErrors.clearBadProcessLocked(info);
+                    if (app != null) {
+                        app.bad = false;
+                    }
+                }
+            }
+        } else {
+            // If this is an isolated process, it can't re-use an existing process.
+            app = null;
+        }
+
+        // We don't have to do anything more if:
+        // (1) There is an existing application record; and
+        // (2) The caller doesn't think it is dead, OR there is no thread
+        //     object attached to it so we know it couldn't have crashed; and
+        // (3) There is a pid assigned to it, so it is either starting or
+        //     already running.
+        if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
+                + " app=" + app + " knownToBeDead=" + knownToBeDead
+                + " thread=" + (app != null ? app.thread : null)
+                + " pid=" + (app != null ? app.pid : -1));
+        if (app != null && app.pid > 0) {
+            if ((!knownToBeDead && !app.killed) || app.thread == null) {
+                // We already have the app running, or are waiting for it to
+                // come up (we have a pid but not yet its thread), so keep it.
+                if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
+                // If this is a new package in the process, add the package to the list
+                app.addPackage(info.packageName, info.versionCode, mService.mProcessStats);
+                checkSlow(startTime, "startProcess: done, added package to proc");
+                return app;
+            }
+
+            // An application record is attached to a previous process,
+            // clean it up now.
+            if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app);
+            checkSlow(startTime, "startProcess: bad proc running, killing");
+            ProcessList.killProcessGroup(app.uid, app.pid);
+            mService.handleAppDiedLocked(app, true, true);
+            checkSlow(startTime, "startProcess: done killing old proc");
+        }
+
+        String hostingNameStr = hostingName != null
+                ? hostingName.flattenToShortString() : null;
+
+        if (app == null) {
+            checkSlow(startTime, "startProcess: creating new process record");
+            app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
+            if (app == null) {
+                Slog.w(TAG, "Failed making new process record for "
+                        + processName + "/" + info.uid + " isolated=" + isolated);
+                return null;
+            }
+            app.crashHandler = crashHandler;
+            app.isolatedEntryPoint = entryPoint;
+            app.isolatedEntryPointArgs = entryPointArgs;
+            checkSlow(startTime, "startProcess: done creating new process record");
+        } else {
+            // If this is a new package in the process, add the package to the list
+            app.addPackage(info.packageName, info.versionCode, mService.mProcessStats);
+            checkSlow(startTime, "startProcess: added package to existing proc");
+        }
+
+        // If the system is not ready yet, then hold off on starting this
+        // process until it is.
+        if (!mService.mProcessesReady
+                && !mService.isAllowedWhileBooting(info)
+                && !allowWhileBooting) {
+            if (!mService.mProcessesOnHold.contains(app)) {
+                mService.mProcessesOnHold.add(app);
+            }
+            if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
+                    "System not ready, putting on hold: " + app);
+            checkSlow(startTime, "startProcess: returning with proc on hold");
+            return app;
+        }
+
+        checkSlow(startTime, "startProcess: stepping in to startProcess");
+        final boolean success = startProcessLocked(app, hostingType, hostingNameStr,
+                abiOverride);
+        checkSlow(startTime, "startProcess: done starting proc!");
+        return success ? app : null;
+    }
+
+    @GuardedBy("mService")
+    private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
+        StringBuilder sb = null;
+        if (app.killedByAm) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("killedByAm=true;");
+        }
+        if (mProcessNames.get(app.processName, app.uid) != app) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("No entry in mProcessNames;");
+        }
+        if (!app.pendingStart) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("pendingStart=false;");
+        }
+        if (app.startSeq > expectedStartSeq) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
+        }
+        return sb == null ? null : sb.toString();
+    }
+
+    @GuardedBy("mService")
+    private boolean handleProcessStartedLocked(ProcessRecord pending,
+            Process.ProcessStartResult startResult, long expectedStartSeq) {
+        // Indicates that this process start has been taken care of.
+        if (mPendingStarts.get(expectedStartSeq) == null) {
+            if (pending.pid == startResult.pid) {
+                pending.setUsingWrapper(startResult.usingWrapper);
+                // TODO: Update already existing clients of usingWrapper
+            }
+            return false;
+        }
+        return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper,
+                expectedStartSeq, false);
+    }
+
+    @GuardedBy("mService")
+    boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
+            long expectedStartSeq, boolean procAttached) {
+        mPendingStarts.remove(expectedStartSeq);
+        final String reason = isProcStartValidLocked(app, expectedStartSeq);
+        if (reason != null) {
+            Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" +
+                    pid
+                    + ", " + reason);
+            app.pendingStart = false;
+            killProcessQuiet(pid);
+            Process.killProcessGroup(app.uid, app.pid);
+            return false;
+        }
+        mService.mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
+        checkSlow(app.startTime, "startProcess: done updating battery stats");
+
+        EventLog.writeEvent(EventLogTags.AM_PROC_START,
+                UserHandle.getUserId(app.startUid), pid, app.startUid,
+                app.processName, app.hostingType,
+                app.hostingNameStr != null ? app.hostingNameStr : "");
+
+        try {
+            AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
+                    app.seInfo, app.info.sourceDir, pid);
+        } catch (RemoteException ex) {
+            // Ignore
+        }
+
+        if (app.isPersistent()) {
+            Watchdog.getInstance().processStarted(app.processName, pid);
+        }
+
+        checkSlow(app.startTime, "startProcess: building log message");
+        StringBuilder buf = mStringBuilder;
+        buf.setLength(0);
+        buf.append("Start proc ");
+        buf.append(pid);
+        buf.append(':');
+        buf.append(app.processName);
+        buf.append('/');
+        UserHandle.formatUid(buf, app.startUid);
+        if (app.isolatedEntryPoint != null) {
+            buf.append(" [");
+            buf.append(app.isolatedEntryPoint);
+            buf.append("]");
+        }
+        buf.append(" for ");
+        buf.append(app.hostingType);
+        if (app.hostingNameStr != null) {
+            buf.append(" ");
+            buf.append(app.hostingNameStr);
+        }
+        mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
+        app.setPid(pid);
+        app.setUsingWrapper(usingWrapper);
+        app.pendingStart = false;
+        checkSlow(app.startTime, "startProcess: starting to update pids map");
+        ProcessRecord oldApp;
+        synchronized (mService.mPidsSelfLocked) {
+            oldApp = mService.mPidsSelfLocked.get(pid);
+        }
+        // If there is already an app occupying that pid that hasn't been cleaned up
+        if (oldApp != null && !app.isolated) {
+            // Clean up anything relating to this pid first
+            Slog.w(TAG, "Reusing pid " + pid
+                    + " while app is still mapped to it");
+            mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
+                    true /*replacingPid*/);
+        }
+        synchronized (mService.mPidsSelfLocked) {
+            mService.mPidsSelfLocked.put(pid, app);
+            if (!procAttached) {
+                Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
+                msg.obj = app;
+                mService.mHandler.sendMessageDelayed(msg, usingWrapper
+                        ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
+            }
+        }
+        checkSlow(app.startTime, "startProcess: done updating pids map");
+        return true;
+    }
+
+    final void removeLruProcessLocked(ProcessRecord app) {
+        int lrui = mLruProcesses.lastIndexOf(app);
+        if (lrui >= 0) {
+            if (!app.killed) {
+                if (app.isPersistent()) {
+                    Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
+                } else {
+                    Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
+                    if (app.pid > 0) {
+                        killProcessQuiet(app.pid);
+                        ProcessList.killProcessGroup(app.uid, app.pid);
+                    } else {
+                        app.pendingStart = false;
+                    }
+                }
+            }
+            if (lrui <= mLruProcessActivityStart) {
+                mLruProcessActivityStart--;
+            }
+            if (lrui <= mLruProcessServiceStart) {
+                mLruProcessServiceStart--;
+            }
+            mLruProcesses.remove(lrui);
+        }
+    }
+
+    void killAllBackgroundProcessesLocked() {
+        final ArrayList<ProcessRecord> procs = new ArrayList<>();
+        final int NP = mProcessNames.getMap().size();
+        for (int ip = 0; ip < NP; ip++) {
+            final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+            final int NA = apps.size();
+            for (int ia = 0; ia < NA; ia++) {
+                final ProcessRecord app = apps.valueAt(ia);
+                if (app.isPersistent()) {
+                    // We don't kill persistent processes.
+                    continue;
+                }
+                if (app.removed) {
+                    procs.add(app);
+                } else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+                    app.removed = true;
+                    procs.add(app);
+                }
+            }
+        }
+
+        final int N = procs.size();
+        for (int i = 0; i < N; i++) {
+            removeProcessLocked(procs.get(i), false, true, "kill all background");
+        }
+    }
+
+    @GuardedBy("mService")
+    final boolean killPackageProcessesLocked(String packageName, int appId,
+            int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
+            boolean doit, boolean evenPersistent, String reason) {
+        ArrayList<ProcessRecord> procs = new ArrayList<>();
+
+        // Remove all processes this package may have touched: all with the
+        // same UID (except for the system or root user), and all whose name
+        // matches the package name.
+        final int NP = mProcessNames.getMap().size();
+        for (int ip = 0; ip < NP; ip++) {
+            SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+            final int NA = apps.size();
+            for (int ia = 0; ia < NA; ia++) {
+                ProcessRecord app = apps.valueAt(ia);
+                if (app.isPersistent() && !evenPersistent) {
+                    // we don't kill persistent processes
+                    continue;
+                }
+                if (app.removed) {
+                    if (doit) {
+                        procs.add(app);
+                    }
+                    continue;
+                }
+
+                // Skip process if it doesn't meet our oom adj requirement.
+                if (app.setAdj < minOomAdj) {
+                    continue;
+                }
+
+                // If no package is specified, we call all processes under the
+                // give user id.
+                if (packageName == null) {
+                    if (userId != UserHandle.USER_ALL && app.userId != userId) {
+                        continue;
+                    }
+                    if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) {
+                        continue;
+                    }
+                    // Package has been specified, we want to hit all processes
+                    // that match it.  We need to qualify this by the processes
+                    // that are running under the specified app and user ID.
+                } else {
+                    final boolean isDep = app.pkgDeps != null
+                            && app.pkgDeps.contains(packageName);
+                    if (!isDep && UserHandle.getAppId(app.uid) != appId) {
+                        continue;
+                    }
+                    if (userId != UserHandle.USER_ALL && app.userId != userId) {
+                        continue;
+                    }
+                    if (!app.pkgList.containsKey(packageName) && !isDep) {
+                        continue;
+                    }
+                }
+
+                // Process has passed all conditions, kill it!
+                if (!doit) {
+                    return true;
+                }
+                app.removed = true;
+                procs.add(app);
+            }
+        }
+
+        int N = procs.size();
+        for (int i=0; i<N; i++) {
+            removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
+        }
+        mService.updateOomAdjLocked();
+        return N > 0;
+    }
+    @GuardedBy("mService")
+    boolean removeProcessLocked(ProcessRecord app,
+            boolean callerWillRestart, boolean allowRestart, String reason) {
+        final String name = app.processName;
+        final int uid = app.uid;
+        if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
+                "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
+
+        ProcessRecord old = mProcessNames.get(name, uid);
+        if (old != app) {
+            // This process is no longer active, so nothing to do.
+            Slog.w(TAG, "Ignoring remove of inactive process: " + app);
+            return false;
+        }
+        removeProcessNameLocked(name, uid);
+        mService.mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
+
+        boolean needRestart = false;
+        if ((app.pid > 0 && app.pid != ActivityManagerService.MY_PID) || (app.pid == 0 && app
+                .pendingStart)) {
+            int pid = app.pid;
+            if (pid > 0) {
+                synchronized (mService.mPidsSelfLocked) {
+                    mService.mPidsSelfLocked.remove(pid);
+                    mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
+                }
+                mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
+                if (app.isolated) {
+                    mService.mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+                    mService.getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
+                }
+            }
+            boolean willRestart = false;
+            if (app.isPersistent() && !app.isolated) {
+                if (!callerWillRestart) {
+                    willRestart = true;
+                } else {
+                    needRestart = true;
+                }
+            }
+            app.kill(reason, true);
+            mService.handleAppDiedLocked(app, willRestart, allowRestart);
+            if (willRestart) {
+                removeLruProcessLocked(app);
+                mService.addAppLocked(app.info, null, false, null /* ABI override */);
+            }
+        } else {
+            mRemovedProcesses.add(app);
+        }
+
+        return needRestart;
+    }
+
+    @GuardedBy("mService")
+    final void addProcessNameLocked(ProcessRecord proc) {
+        // We shouldn't already have a process under this name, but just in case we
+        // need to clean up whatever may be there now.
+        ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
+        if (old == proc && proc.isPersistent()) {
+            // We are re-adding a persistent process.  Whatevs!  Just leave it there.
+            Slog.w(TAG, "Re-adding persistent process " + proc);
+        } else if (old != null) {
+            Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+        }
+        UidRecord uidRec = mService.mActiveUids.get(proc.uid);
+        if (uidRec == null) {
+            uidRec = new UidRecord(proc.uid);
+            // This is the first appearance of the uid, report it now!
+            if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+                    "Creating new process uid: " + uidRec);
+            if (Arrays.binarySearch(mService.mDeviceIdleTempWhitelist,
+                    UserHandle.getAppId(proc.uid)) >= 0
+                    || mService.mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
+                uidRec.setWhitelist = uidRec.curWhitelist = true;
+            }
+            uidRec.updateHasInternetPermission();
+            mService.mActiveUids.put(proc.uid, uidRec);
+            EventLogTags.writeAmUidRunning(uidRec.uid);
+            mService.noteUidProcessState(uidRec.uid, uidRec.curProcState);
+        }
+        proc.uidRecord = uidRec;
+
+        // Reset render thread tid if it was already set, so new process can set it again.
+        proc.renderThreadTid = 0;
+        uidRec.numProcs++;
+        mProcessNames.put(proc.processName, proc.uid, proc);
+        if (proc.isolated) {
+            mIsolatedProcesses.put(proc.uid, proc);
+        }
+    }
+
+    @GuardedBy("mService")
+    final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
+            boolean isolated, int isolatedUid) {
+        String proc = customProcess != null ? customProcess : info.processName;
+        final int userId = UserHandle.getUserId(info.uid);
+        int uid = info.uid;
+        if (isolated) {
+            if (isolatedUid == 0) {
+                int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1;
+                while (true) {
+                    if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID
+                            || mNextIsolatedProcessUid > LAST_ISOLATED_UID) {
+                        mNextIsolatedProcessUid = FIRST_ISOLATED_UID;
+                    }
+                    uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
+                    mNextIsolatedProcessUid++;
+                    if (mIsolatedProcesses.indexOfKey(uid) < 0) {
+                        // No process for this uid, use it.
+                        break;
+                    }
+                    stepsLeft--;
+                    if (stepsLeft <= 0) {
+                        return null;
+                    }
+                }
+            } else {
+                // Special case for startIsolatedProcess (internal only), where
+                // the uid of the isolated process is specified by the caller.
+                uid = isolatedUid;
+            }
+            mService.getPackageManagerInternalLocked().addIsolatedUid(uid, info.uid);
+
+            // Register the isolated UID with this application so BatteryStats knows to
+            // attribute resource usage to the application.
+            //
+            // NOTE: This is done here before addProcessNameLocked, which will tell BatteryStats
+            // about the process state of the isolated UID *before* it is registered with the
+            // owning application.
+            mService.mBatteryStatsService.addIsolatedUid(uid, info.uid);
+            StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
+                    StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
+        }
+        final ProcessRecord r = new ProcessRecord(mService, info, proc, uid,
+                mService.getGlobalConfiguration());
+
+        if (!mService.mBooted && !mService.mBooting
+                && userId == UserHandle.USER_SYSTEM
+                && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
+            // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
+            r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+            r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            r.setPersistent(true);
+            r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
+        }
+        if (isolated && isolatedUid != 0) {
+            // Special case for startIsolatedProcess (internal only) - assume the process
+            // is required by the system server to prevent it being killed.
+            r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
+        }
+        addProcessNameLocked(r);
+        return r;
+    }
+
+    @GuardedBy("mService")
+    final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
+        return removeProcessNameLocked(name, uid, null);
+    }
+
+    @GuardedBy("mService")
+    final ProcessRecord removeProcessNameLocked(final String name, final int uid,
+            final ProcessRecord expecting) {
+        ProcessRecord old = mProcessNames.get(name, uid);
+        // Only actually remove when the currently recorded value matches the
+        // record that we expected; if it doesn't match then we raced with a
+        // newly created process and we don't want to destroy the new one.
+        if ((expecting == null) || (old == expecting)) {
+            mProcessNames.remove(name, uid);
+        }
+        if (old != null && old.uidRecord != null) {
+            old.uidRecord.numProcs--;
+            if (old.uidRecord.numProcs == 0) {
+                // No more processes using this uid, tell clients it is gone.
+                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+                        "No more processes in " + old.uidRecord);
+                mService.enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
+                EventLogTags.writeAmUidStopped(uid);
+                mService.mActiveUids.remove(uid);
+                mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
+            }
+            old.uidRecord = null;
+        }
+        mIsolatedProcesses.remove(uid);
+        return old;
+    }
+
+    /** Call setCoreSettings on all LRU processes, with the new settings. */
+    @GuardedBy("mService")
+    void updateCoreSettingsLocked(Bundle settings) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord processRecord = mLruProcesses.get(i);
+            try {
+                if (processRecord.thread != null) {
+                    processRecord.thread.setCoreSettings(settings);
+                }
+            } catch (RemoteException re) {
+                /* ignore */
+            }
+        }
+    }
+
+    /**
+     * Kill all background processes except for ones with targetSdk lower than minTargetSdk and
+     * procstate lower than maxProcState.
+     * @param minTargetSdk
+     * @param maxProcState
+     */
+    @GuardedBy("mService")
+    void killAllBackgroundProcessesExceptLocked(int minTargetSdk, int maxProcState) {
+        final ArrayList<ProcessRecord> procs = new ArrayList<>();
+        final int NP = mProcessNames.getMap().size();
+        for (int ip = 0; ip < NP; ip++) {
+            final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+            final int NA = apps.size();
+            for (int ia = 0; ia < NA; ia++) {
+                final ProcessRecord app = apps.valueAt(ia);
+                if (app.removed) {
+                    procs.add(app);
+                } else if ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
+                        && (maxProcState < 0 || app.setProcState > maxProcState)) {
+                    app.removed = true;
+                    procs.add(app);
+                }
+            }
+        }
+
+        final int N = procs.size();
+        for (int i = 0; i < N; i++) {
+            removeProcessLocked(procs.get(i), false, true, "kill all background except");
+        }
+    }
+
+    /**
+     * Call updateTimePrefs on all LRU processes
+     * @param timePref The time pref to pass to each process
+     */
+    @GuardedBy("mService")
+    void updateAllTimePrefsLocked(int timePref) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord r = mLruProcesses.get(i);
+            if (r.thread != null) {
+                try {
+                    r.thread.updateTimePrefs(timePref);
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to update preferences for: "
+                            + r.info.processName);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void setAllHttpProxyLocked(String host, String port, String exclList, Uri pacFileUrl) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord r = mLruProcesses.get(i);
+            // Don't dispatch to isolated processes as they can't access
+            // ConnectivityManager and don't have network privileges anyway.
+            if (r.thread != null && !r.isolated) {
+                try {
+                    r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to update http proxy for: " +
+                            r.info.processName);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void clearAllDnsCacheLocked() {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord r = mLruProcesses.get(i);
+            if (r.thread != null) {
+                try {
+                    r.thread.clearDnsCache();
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void handleAllTrustStorageUpdateLocked() {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord r = mLruProcesses.get(i);
+            if (r.thread != null) {
+                try {
+                    r.thread.handleTrustStorageUpdate();
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to handle trust storage update for: " +
+                            r.info.processName);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
+            String what, Object obj, ProcessRecord srcApp) {
+        app.lastActivityTime = now;
+
+        if (app.hasActivitiesOrRecentTasks()) {
+            // Don't want to touch dependent processes that are hosting activities.
+            return index;
+        }
+
+        int lrui = mLruProcesses.lastIndexOf(app);
+        if (lrui < 0) {
+            Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
+                    + what + " " + obj + " from " + srcApp);
+            return index;
+        }
+
+        if (lrui >= index) {
+            // Don't want to cause this to move dependent processes *back* in the
+            // list as if they were less frequently used.
+            return index;
+        }
+
+        if (lrui >= mLruProcessActivityStart) {
+            // Don't want to touch dependent processes that are hosting activities.
+            return index;
+        }
+
+        mLruProcesses.remove(lrui);
+        if (index > 0) {
+            index--;
+        }
+        if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
+                + " in LRU list: " + app);
+        mLruProcesses.add(index, app);
+        return index;
+    }
+
+    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
+            ProcessRecord client) {
+        final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities()
+                || app.treatLikeActivity;
+        final boolean hasService = false; // not impl yet. app.services.size() > 0;
+        if (!activityChange && hasActivity) {
+            // The process has activities, so we are only allowing activity-based adjustments
+            // to move it.  It should be kept in the front of the list with other
+            // processes that have activities, and we don't want those to change their
+            // order except due to activity operations.
+            return;
+        }
+
+        mLruSeq++;
+        final long now = SystemClock.uptimeMillis();
+        app.lastActivityTime = now;
+
+        // First a quick reject: if the app is already at the position we will
+        // put it, then there is nothing to do.
+        if (hasActivity) {
+            final int N = mLruProcesses.size();
+            if (N > 0 && mLruProcesses.get(N - 1) == app) {
+                if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
+                return;
+            }
+        } else {
+            if (mLruProcessServiceStart > 0
+                    && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
+                if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
+                return;
+            }
+        }
+
+        int lrui = mLruProcesses.lastIndexOf(app);
+
+        if (app.isPersistent() && lrui >= 0) {
+            // We don't care about the position of persistent processes, as long as
+            // they are in the list.
+            if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
+            return;
+        }
+
+        /* In progress: compute new position first, so we can avoid doing work
+           if the process is not actually going to move.  Not yet working.
+        int addIndex;
+        int nextIndex;
+        boolean inActivity = false, inService = false;
+        if (hasActivity) {
+            // Process has activities, put it at the very tipsy-top.
+            addIndex = mLruProcesses.size();
+            nextIndex = mLruProcessServiceStart;
+            inActivity = true;
+        } else if (hasService) {
+            // Process has services, put it at the top of the service list.
+            addIndex = mLruProcessActivityStart;
+            nextIndex = mLruProcessServiceStart;
+            inActivity = true;
+            inService = true;
+        } else  {
+            // Process not otherwise of interest, it goes to the top of the non-service area.
+            addIndex = mLruProcessServiceStart;
+            if (client != null) {
+                int clientIndex = mLruProcesses.lastIndexOf(client);
+                if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating "
+                        + app);
+                if (clientIndex >= 0 && addIndex > clientIndex) {
+                    addIndex = clientIndex;
+                }
+            }
+            nextIndex = addIndex > 0 ? addIndex-1 : addIndex;
+        }
+
+        Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act="
+                + mLruProcessActivityStart + "): " + app);
+        */
+
+        if (lrui >= 0) {
+            if (lrui < mLruProcessActivityStart) {
+                mLruProcessActivityStart--;
+            }
+            if (lrui < mLruProcessServiceStart) {
+                mLruProcessServiceStart--;
+            }
+            /*
+            if (addIndex > lrui) {
+                addIndex--;
+            }
+            if (nextIndex > lrui) {
+                nextIndex--;
+            }
+            */
+            mLruProcesses.remove(lrui);
+        }
+
+        /*
+        mLruProcesses.add(addIndex, app);
+        if (inActivity) {
+            mLruProcessActivityStart++;
+        }
+        if (inService) {
+            mLruProcessActivityStart++;
+        }
+        */
+
+        int nextIndex;
+        if (hasActivity) {
+            final int N = mLruProcesses.size();
+            if ((!app.hasActivities() || app.hasRecentTasks())
+                    && mLruProcessActivityStart < (N - 1)) {
+                // Process doesn't have activities, but has clients with
+                // activities...  move it up, but one below the top (the top
+                // should always have a real activity).
+                if (DEBUG_LRU) Slog.d(TAG_LRU,
+                        "Adding to second-top of LRU activity list: " + app);
+                mLruProcesses.add(N - 1, app);
+                // To keep it from spamming the LRU list (by making a bunch of clients),
+                // we will push down any other entries owned by the app.
+                final int uid = app.info.uid;
+                for (int i = N - 2; i > mLruProcessActivityStart; i--) {
+                    ProcessRecord subProc = mLruProcesses.get(i);
+                    if (subProc.info.uid == uid) {
+                        // We want to push this one down the list.  If the process after
+                        // it is for the same uid, however, don't do so, because we don't
+                        // want them internally to be re-ordered.
+                        if (mLruProcesses.get(i - 1).info.uid != uid) {
+                            if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                    "Pushing uid " + uid + " swapping at " + i + ": "
+                                            + mLruProcesses.get(i) + " : "
+                                            + mLruProcesses.get(i - 1));
+                            ProcessRecord tmp = mLruProcesses.get(i);
+                            mLruProcesses.set(i, mLruProcesses.get(i - 1));
+                            mLruProcesses.set(i - 1, tmp);
+                            i--;
+                        }
+                    } else {
+                        // A gap, we can stop here.
+                        break;
+                    }
+                }
+            } else {
+                // Process has activities, put it at the very tipsy-top.
+                if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
+                mLruProcesses.add(app);
+            }
+            nextIndex = mLruProcessServiceStart;
+        } else if (hasService) {
+            // Process has services, put it at the top of the service list.
+            if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
+            mLruProcesses.add(mLruProcessActivityStart, app);
+            nextIndex = mLruProcessServiceStart;
+            mLruProcessActivityStart++;
+        } else  {
+            // Process not otherwise of interest, it goes to the top of the non-service area.
+            int index = mLruProcessServiceStart;
+            if (client != null) {
+                // If there is a client, don't allow the process to be moved up higher
+                // in the list than that client.
+                int clientIndex = mLruProcesses.lastIndexOf(client);
+                if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client
+                        + " when updating " + app);
+                if (clientIndex <= lrui) {
+                    // Don't allow the client index restriction to push it down farther in the
+                    // list than it already is.
+                    clientIndex = lrui;
+                }
+                if (clientIndex >= 0 && index > clientIndex) {
+                    index = clientIndex;
+                }
+            }
+            if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
+            mLruProcesses.add(index, app);
+            nextIndex = index - 1;
+            mLruProcessActivityStart++;
+            mLruProcessServiceStart++;
+        }
+
+        // If the app is currently using a content provider or service,
+        // bump those processes as well.
+        for (int j = app.connections.size() - 1; j >= 0; j--) {
+            ConnectionRecord cr = app.connections.valueAt(j);
+            if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
+                    && cr.binding.service.app != null
+                    && cr.binding.service.app.lruSeq != mLruSeq
+                    && !cr.binding.service.app.isPersistent()) {
+                nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+                        now,
+                        nextIndex,
+                        "service connection", cr, app);
+            }
+        }
+        for (int j = app.conProviders.size() - 1; j >= 0; j--) {
+            ContentProviderRecord cpr = app.conProviders.get(j).provider;
+            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
+                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
+                        "provider reference", cpr, app);
+            }
+        }
+    }
+
+    final ProcessRecord getLRURecordForAppLocked(IApplicationThread thread) {
+        final IBinder threadBinder = thread.asBinder();
+        // Find the application record.
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            final ProcessRecord rec = mLruProcesses.get(i);
+            if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
+                return rec;
+            }
+        }
+        return null;
+    }
+
+    boolean haveBackgroundProcessLocked() {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            final ProcessRecord rec = mLruProcesses.get(i);
+            if (rec.thread != null
+                    && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static int procStateToImportance(int procState, int memAdj,
+            ActivityManager.RunningAppProcessInfo currApp,
+            int clientTargetSdk) {
+        int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk(
+                procState, clientTargetSdk);
+        if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
+            currApp.lru = memAdj;
+        } else {
+            currApp.lru = 0;
+        }
+        return imp;
+    }
+
+    @GuardedBy("mService")
+    void fillInProcMemInfoLocked(ProcessRecord app,
+            ActivityManager.RunningAppProcessInfo outInfo,
+            int clientTargetSdk) {
+        outInfo.pid = app.pid;
+        outInfo.uid = app.info.uid;
+        if (mService.mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
+            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
+        }
+        if (app.isPersistent()) {
+            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
+        }
+        if (app.hasActivities()) {
+            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
+        }
+        outInfo.lastTrimLevel = app.trimMemoryLevel;
+        int adj = app.curAdj;
+        int procState = app.getCurProcState();
+        outInfo.importance = procStateToImportance(procState, adj, outInfo,
+                clientTargetSdk);
+        outInfo.importanceReasonCode = app.adjTypeCode;
+        outInfo.processState = app.getCurProcState();
+        outInfo.isFocused = (app == mService.getTopAppLocked());
+        outInfo.lastActivityTime = app.lastActivityTime;
+    }
+
+    @GuardedBy("mService")
+    List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesLocked(boolean allUsers,
+            int userId, boolean allUids, int callingUid, int clientTargetSdk) {
+        // Lazy instantiation of list
+        List<ActivityManager.RunningAppProcessInfo> runList = null;
+
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord app = mLruProcesses.get(i);
+            if ((!allUsers && app.userId != userId)
+                    || (!allUids && app.uid != callingUid)) {
+                continue;
+            }
+            if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
+                // Generate process state info for running application
+                ActivityManager.RunningAppProcessInfo currApp =
+                        new ActivityManager.RunningAppProcessInfo(app.processName,
+                                app.pid, app.getPackageList());
+                fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
+                if (app.adjSource instanceof ProcessRecord) {
+                    currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
+                    currApp.importanceReasonImportance =
+                            ActivityManager.RunningAppProcessInfo.procStateToImportance(
+                                    app.adjSourceProcState);
+                } else if (app.adjSource instanceof ActivityRecord) {
+                    ActivityRecord r = (ActivityRecord)app.adjSource;
+                    if (r.app != null) currApp.importanceReasonPid = r.app.getPid();
+                }
+                if (app.adjTarget instanceof ComponentName) {
+                    currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
+                }
+                //Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
+                //        + " lru=" + currApp.lru);
+                if (runList == null) {
+                    runList = new ArrayList<>();
+                }
+                runList.add(currApp);
+            }
+        }
+        return runList;
+    }
+
+    @GuardedBy("mService")
+    int getLruSizeLocked() {
+        return mLruProcesses.size();
+    }
+
+    @GuardedBy("mService")
+    void dumpLruListHeaderLocked(PrintWriter pw) {
+        pw.print("  Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
+        pw.print(" total, non-act at ");
+        pw.print(mLruProcesses.size() - mLruProcessActivityStart);
+        pw.print(", non-svc at ");
+        pw.print(mLruProcesses.size() - mLruProcessServiceStart);
+        pw.println("):");
+    }
+
+    @GuardedBy("mService")
+    ArrayList<ProcessRecord> collectProcessesLocked(int start, boolean allPkgs, String[] args) {
+        ArrayList<ProcessRecord> procs;
+        if (args != null && args.length > start
+                && args[start].charAt(0) != '-') {
+            procs = new ArrayList<ProcessRecord>();
+            int pid = -1;
+            try {
+                pid = Integer.parseInt(args[start]);
+            } catch (NumberFormatException e) {
+            }
+            for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+                ProcessRecord proc = mLruProcesses.get(i);
+                if (proc.pid > 0 && proc.pid == pid) {
+                    procs.add(proc);
+                } else if (allPkgs && proc.pkgList != null
+                        && proc.pkgList.containsKey(args[start])) {
+                    procs.add(proc);
+                } else if (proc.processName.equals(args[start])) {
+                    procs.add(proc);
+                }
+            }
+            if (procs.size() <= 0) {
+                return null;
+            }
+        } else {
+            procs = new ArrayList<ProcessRecord>(mLruProcesses);
+        }
+        return procs;
+    }
+
+    @GuardedBy("mService")
+    void updateApplicationInfoLocked(List<String> packagesToUpdate, int userId,
+            boolean updateFrameworkRes) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            final ProcessRecord app = mLruProcesses.get(i);
+            if (app.thread == null) {
+                continue;
+            }
+
+            if (userId != UserHandle.USER_ALL && app.userId != userId) {
+                continue;
+            }
+
+            final int packageCount = app.pkgList.size();
+            for (int j = 0; j < packageCount; j++) {
+                final String packageName = app.pkgList.keyAt(j);
+                if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
+                    try {
+                        final ApplicationInfo ai = AppGlobals.getPackageManager()
+                                .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
+                        if (ai != null) {
+                            app.thread.scheduleApplicationInfoChanged(ai);
+                        }
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
+                                packageName, app));
+                    }
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord r = mLruProcesses.get(i);
+            if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
+                try {
+                    r.thread.dispatchPackageBroadcast(cmd, packages);
+                } catch (RemoteException ex) {
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 0eb535b..fa7a08b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -28,6 +28,7 @@
 import android.app.ApplicationErrorReport;
 import android.app.Dialog;
 import android.app.IApplicationThread;
+import android.app.ProfilerInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -60,6 +61,7 @@
 import com.android.server.Watchdog;
 
 import java.io.File;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -733,7 +735,7 @@
             if (pid > 0) {
                 EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
                 Process.killProcessQuiet(pid);
-                ActivityManagerService.killProcessGroup(uid, pid);
+                ProcessList.killProcessGroup(uid, pid);
             } else {
                 pendingStart = false;
             }
@@ -836,6 +838,13 @@
         return null;
     }
 
+    @Override
+    public void addPackage(String pkg, long versionCode) {
+        synchronized (mService) {
+            addPackage(pkg, versionCode, mService.mProcessStats);
+        }
+    }
+
     /*
      *  Return true if package has been added false if not
      */
@@ -1157,7 +1166,7 @@
                 mService.mServices.updateServiceConnectionActivitiesLocked(this);
             }
             if (updateLru) {
-                mService.updateLruProcessLocked(this, activityChange, null);
+                mService.mProcessList.updateLruProcessLocked(this, activityChange, null);
             }
             if (updateOomAdj) {
                 mService.updateOomAdjLocked();
@@ -1176,10 +1185,55 @@
      * Returns the total time (in milliseconds) spent executing in both user and system code.
      * Safe to call without lock held.
      */
+    @Override
     public long getCpuTime() {
         return mService.mProcessCpuTracker.getCpuTimeForPid(pid);
     }
 
+    @Override
+    public void clearWaitingToKill() {
+        synchronized (mService) {
+            waitingToKill = null;
+        }
+    }
+
+    @Override
+    public ProfilerInfo onStartActivity(int topProcessState) {
+        synchronized (mService) {
+            ProfilerInfo profilerInfo = null;
+            if (mService.mProfileApp != null && mService.mProfileApp.equals(processName)) {
+                if (mService.mProfileProc == null || mService.mProfileProc == this) {
+                    mService.mProfileProc = this;
+                    final ProfilerInfo profilerInfoSvc = mService.mProfilerInfo;
+                    if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
+                        if (profilerInfoSvc.profileFd != null) {
+                            try {
+                                profilerInfoSvc.profileFd = profilerInfoSvc.profileFd.dup();
+                            } catch (IOException e) {
+                                profilerInfoSvc.closeFd();
+                            }
+                        }
+
+                        profilerInfo = new ProfilerInfo(profilerInfoSvc);
+                    }
+                }
+            }
+
+            hasShownUi = true;
+            setPendingUiClean(true);
+            forceProcessStateUpTo(topProcessState);
+
+            return profilerInfo;
+        }
+    }
+
+    @Override
+    public void appDied() {
+        synchronized (mService) {
+            mService.appDiedLocked(this);
+        }
+    }
+
     public long getInputDispatchingTimeout() {
         return mWindowProcessController.getInputDispatchingTimeout();
     }
@@ -1256,8 +1310,8 @@
 
                 if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
 
-                for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
-                    ProcessRecord r = mService.mLruProcesses.get(i);
+                for (int i = mService.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+                    ProcessRecord r = mService.mProcessList.mLruProcesses.get(i);
                     if (r != null && r.thread != null) {
                         int myPid = r.pid;
                         if (myPid > 0 && myPid != pid && myPid != parentPid && myPid != MY_PID) {
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
index 792b66b..1743dde 100644
--- a/services/core/java/com/android/server/am/WindowProcessController.java
+++ b/services/core/java/com/android/server/am/WindowProcessController.java
@@ -37,6 +37,7 @@
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.IApplicationThread;
+import android.app.ProfilerInfo;
 import android.app.servertransaction.ConfigurationChangeItem;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -674,6 +675,35 @@
         mAtm.mH.sendMessage(m);
     }
 
+    void clearWaitingToKill() {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Message m = PooledLambda.obtainMessage(
+                WindowProcessListener::clearWaitingToKill, mListener);
+        mAtm.mH.sendMessage(m);
+    }
+
+    void addPackage(String pkg, long versionCode) {
+        // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
+        // using WM lock. Need to figure-out if it is okay to do this asynchronously.
+        if (mListener == null) return;
+        mListener.addPackage(pkg, versionCode);
+    }
+
+    ProfilerInfo onStartActivity(int topProcessState) {
+        // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
+        // using WM lock. Need to figure-out if it is okay to do this asynchronously.
+        if (mListener == null) return null;
+        return mListener.onStartActivity(topProcessState);
+    }
+
+    public void appDied() {
+        // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
+        // using WM lock. Need to figure-out if it is okay to do this asynchronously.
+        if (mListener == null) return;
+        mListener.appDied();
+    }
+
     @Override
     public void onConfigurationChanged(Configuration newGlobalConfig) {
         super.onConfigurationChanged(newGlobalConfig);
diff --git a/services/core/java/com/android/server/am/WindowProcessListener.java b/services/core/java/com/android/server/am/WindowProcessListener.java
index 9cad6fe4..4a7e6e8 100644
--- a/services/core/java/com/android/server/am/WindowProcessListener.java
+++ b/services/core/java/com/android/server/am/WindowProcessListener.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.app.ProfilerInfo;
 import android.content.pm.ApplicationInfo;
 import android.util.proto.ProtoOutputStream;
 
@@ -51,5 +52,16 @@
     /** Returns the total time (in milliseconds) spent executing in both user and system code. */
     long getCpuTime();
 
+    /** Clears the waiting to kill reason for this process. */
+    void clearWaitingToKill();
+
+    /** Adds the package to the process. */
+    void addPackage(String pkg, long versionCode);
+
+    /** Called when we are in the process on starting an activity. */
+    ProfilerInfo onStartActivity(int topProcessState);
+
+    /** App died :(...oh well */
+    void appDied();
     void writeToProto(ProtoOutputStream proto, long fieldId);
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index fc76b46..d57214e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -109,10 +109,12 @@
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputConnectionInspector;
+import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
@@ -129,6 +131,9 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.inputmethod.UnbindReason;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.HandlerCaller;
@@ -144,7 +149,6 @@
 import com.android.internal.view.IInputMethodSession;
 import com.android.internal.view.IInputSessionCallback;
 import com.android.internal.view.InputBindResult;
-import com.android.internal.view.InputMethodClient;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -500,6 +504,7 @@
      *
      * @see #mCurFocusedWindow
      */
+    @SoftInputModeFlags
     int mCurFocusedWindowSoftInputMode;
 
     /**
@@ -517,6 +522,7 @@
      *
      * @see android.view.inputmethod.InputConnectionInspector.MissingMethodFlags
      */
+    @MissingMethodFlags
     int mCurInputContextMissingMethods;
 
     /**
@@ -690,20 +696,21 @@
         final IBinder mImeToken;
         @NonNull
         final String mImeId;
-        // @InputMethodClient.StartInputReason
+        @StartInputReason
         final int mStartInputReason;
         final boolean mRestarting;
         @Nullable
         final IBinder mTargetWindow;
         @NonNull
         final EditorInfo mEditorInfo;
+        @SoftInputModeFlags
         final int mTargetWindowSoftInputMode;
         final int mClientBindSequenceNumber;
 
         StartInputInfo(@NonNull IBinder imeToken, @NonNull String imeId,
-                /* @InputMethodClient.StartInputReason */ int startInputReason, boolean restarting,
+                @StartInputReason int startInputReason, boolean restarting,
                 @Nullable IBinder targetWindow, @NonNull EditorInfo editorInfo,
-                int targetWindowSoftInputMode, int clientBindSequenceNumber) {
+                @SoftInputModeFlags int targetWindowSoftInputMode, int clientBindSequenceNumber) {
             mSequenceNumber = sSequenceNumber.getAndIncrement();
             mTimestamp = SystemClock.uptimeMillis();
             mWallTime = System.currentTimeMillis();
@@ -771,13 +778,14 @@
             String mImeTokenString;
             @NonNull
             String mImeId;
-            /* @InputMethodClient.StartInputReason */
+            @StartInputReason
             int mStartInputReason;
             boolean mRestarting;
             @NonNull
             String mTargetWindowString;
             @NonNull
             EditorInfo mEditorInfo;
+            @SoftInputModeFlags
             int mTargetWindowSoftInputMode;
             int mClientBindSequenceNumber;
 
@@ -834,7 +842,7 @@
                 pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
                         + " (timestamp=" + entry.mTimestamp + ")"
                         + " reason="
-                        + InputMethodClient.getStartInputReason(entry.mStartInputReason)
+                        + InputMethodDebug.startInputReasonToString(entry.mStartInputReason)
                         + " restarting=" + entry.mRestarting);
 
                 pw.print(prefix);
@@ -846,7 +854,7 @@
                         + " clientBindSeq=" + entry.mClientBindSequenceNumber);
 
                 pw.print(prefix);
-                pw.println(" softInputMode=" + InputMethodClient.softInputModeToString(
+                pw.println(" softInputMode=" + InputMethodDebug.softInputModeToString(
                                 entry.mTargetWindowSoftInputMode));
 
                 pw.print(prefix);
@@ -1499,7 +1507,7 @@
         // TODO: Is it really possible that switchUserLocked() happens before system ready?
         if (mSystemReady) {
             hideCurrentInputLocked(0, null);
-            resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_USER);
+            resetCurrentMethodAndClient(UnbindReason.SWITCH_USER);
             buildInputMethodListLocked(initialUserSwitch);
             if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
                 // This is the first time of the user switch and
@@ -1729,7 +1737,7 @@
      * {@link InputMethodManagerService}.
      *
      * <p>As a general principle, IPCs from the application process that take
-     * {@link InputMethodClient} will be rejected without this step.</p>
+     * {@link IInputMethodClient} will be rejected without this step.</p>
      *
      * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
      *               of {@link android.view.inputmethod.InputMethodManager} that runs on the client
@@ -1808,8 +1816,7 @@
          }
     }
 
-    void unbindCurrentClientLocked(
-            /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
+    void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
         if (mCurClient != null) {
             if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client="
                     + mCurClient.client.asBinder());
@@ -1855,8 +1862,7 @@
 
     @GuardedBy("mMethodMap")
     @NonNull
-    InputBindResult attachNewInputLocked(
-            /* @InputMethodClient.StartInputReason */ final int startInputReason, boolean initial) {
+    InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
         if (!mBoundToMethod) {
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                     MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
@@ -1886,9 +1892,8 @@
     @GuardedBy("mMethodMap")
     @NonNull
     InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
-            /* @InputConnectionInspector.missingMethods */ final int missingMethods,
-            @NonNull EditorInfo attribute, int controlFlags,
-            /* @InputMethodClient.StartInputReason */ final int startInputReason) {
+            @MissingMethodFlags int missingMethods, @NonNull EditorInfo attribute, int controlFlags,
+            @StartInputReason int startInputReason) {
         // If no method is currently selected, do nothing.
         if (mCurMethodId == null) {
             return InputBindResult.NO_IME;
@@ -1921,7 +1926,7 @@
             mCurClientInKeyguard = isKeyguardLocked();
             // If the client is changing, we need to switch over to the new
             // one.
-            unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_CLIENT);
+            unbindCurrentClientLocked(UnbindReason.SWITCH_CLIENT);
             if (DEBUG) Slog.v(TAG, "switching to client: client="
                     + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
 
@@ -2046,7 +2051,7 @@
                     mCurClient.curSession = new SessionState(mCurClient,
                             method, session, channel);
                     InputBindResult res = attachNewInputLocked(
-                            InputMethodClient.START_INPUT_REASON_SESSION_CREATED_BY_IME, true);
+                            StartInputReason.SESSION_CREATED_BY_IME, true);
                     if (res.method != null) {
                         executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
                                 MSG_BIND_CLIENT, mCurClient.client, res));
@@ -2088,8 +2093,7 @@
         clearCurMethodLocked();
     }
 
-    void resetCurrentMethodAndClient(
-            /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
+    void resetCurrentMethodAndClient(@UnbindReason int unbindClientReason) {
         mCurMethodId = null;
         unbindCurrentMethodLocked();
         unbindCurrentClientLocked(unbindClientReason);
@@ -2166,7 +2170,7 @@
                 mLastBindTime = SystemClock.uptimeMillis();
                 mShowRequested = mInputShown;
                 mInputShown = false;
-                unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_DISCONNECT_IME);
+                unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
             }
         }
     }
@@ -2477,12 +2481,12 @@
                 setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
-                resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_IME_FAILED);
+                resetCurrentMethodAndClient(UnbindReason.SWITCH_IME_FAILED);
             }
             mShortcutInputMethodsAndSubtypes.clear();
         } else {
             // There is no longer an input method set, so stop any current one.
-            resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_NO_IME);
+            resetCurrentMethodAndClient(UnbindReason.NO_IME);
         }
         // Here is not the perfect place to reset the switching controller. Ideally
         // mSwitchingController and mSettings should be able to share the same state.
@@ -2560,7 +2564,7 @@
                 intent.putExtra("input_method_id", id);
                 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
             }
-            unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_IME);
+            unbindCurrentClientLocked(UnbindReason.SWITCH_IME);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -2734,11 +2738,10 @@
     @NonNull
     @Override
     public InputBindResult startInputOrWindowGainedFocus(
-            /* @InputMethodClient.StartInputReason */ final int startInputReason,
-            IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
-            int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
-            /* @InputConnectionInspector.missingMethods */ final int missingMethods,
-            int unverifiedTargetSdkVersion) {
+            @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
+            int controlFlags, @SoftInputModeFlags int softInputMode, int windowFlags,
+            @Nullable EditorInfo attribute, IInputContext inputContext,
+            @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
         if (windowToken == null) {
             Slog.e(TAG, "windowToken cannot be null.");
             return InputBindResult.NULL;
@@ -2749,7 +2752,7 @@
         if (result == null) {
             // This must never happen, but just in case.
             Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
-                    + InputMethodClient.getStartInputReason(startInputReason)
+                    + InputMethodDebug.startInputReasonToString(startInputReason)
                     + " windowFlags=#" + Integer.toHexString(windowFlags)
                     + " editorInfo=" + attribute);
             return InputBindResult.NULL;
@@ -2759,12 +2762,10 @@
 
     @NonNull
     private InputBindResult startInputOrWindowGainedFocusInternal(
-            /* @InputMethodClient.StartInputReason */ final int startInputReason,
-            IInputMethodClient client, @NonNull IBinder windowToken, int controlFlags,
-            /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
+            @StartInputReason int startInputReason, IInputMethodClient client,
+            @NonNull IBinder windowToken, int controlFlags, @SoftInputModeFlags int softInputMode,
             int windowFlags, EditorInfo attribute, IInputContext inputContext,
-            /* @InputConnectionInspector.missingMethods */  final int missingMethods,
-            int unverifiedTargetSdkVersion) {
+            @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
         // Needs to check the validity before clearing calling identity
         final boolean calledFromValidUser = calledFromValidUser();
         InputBindResult res = null;
@@ -2774,14 +2775,14 @@
                     mWindowManagerInternal.getDisplayIdForWindow(windowToken);
             synchronized (mMethodMap) {
                 if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason="
-                        + InputMethodClient.getStartInputReason(startInputReason)
+                        + InputMethodDebug.startInputReasonToString(startInputReason)
                         + " client=" + client.asBinder()
                         + " inputContext=" + inputContext
                         + " missingMethods="
                         + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
                         + " attribute=" + attribute
                         + " controlFlags=#" + Integer.toHexString(controlFlags)
-                        + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
+                        + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
                         + " windowFlags=#" + Integer.toHexString(windowFlags)
                         + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
 
@@ -4579,7 +4580,7 @@
             p.println("  mCurClient=" + client + " mCurSeq=" + mCurSeq);
             p.println("  mCurFocusedWindow=" + mCurFocusedWindow
                     + " softInputMode=" +
-                    InputMethodClient.softInputModeToString(mCurFocusedWindowSoftInputMode)
+                    InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)
                     + " client=" + mCurFocusedWindowClient);
             focusedWindowClient = mCurFocusedWindowClient;
             p.println("  mCurId=" + mCurId + " mHaveConnection=" + mHaveConnection
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 3f8941d..4ece538d 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -57,6 +57,8 @@
  * This isn't strictly necessary because each controller is only interested in a specific field,
  * and the receivers that are listening for global state change will all run on the main looper,
  * but we don't enforce that so this is safer.
+ *
+ * Test: atest com.android.server.job.controllers.JobStatusTest
  * @hide
  */
 public final class JobStatus {
@@ -154,7 +156,9 @@
 
     // Constraints.
     final int requiredConstraints;
+    private final int mRequiredConstraintsOfInterest;
     int satisfiedConstraints = 0;
+    private int mSatisfiedConstraintsOfInterest = 0;
 
     // Set to true if doze constraint was satisfied due to app being whitelisted.
     public boolean dozeWhitelisted;
@@ -265,6 +269,28 @@
 
     private long totalNetworkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
 
+    /////// Booleans that track if a job is ready to run. They should be updated whenever dependent
+    /////// states change.
+
+    /**
+     * The deadline for the job has passed. This is only good for non-periodic jobs. A periodic job
+     * should only run if its constraints are satisfied.
+     * Computed as: NOT periodic AND has deadline constraint AND deadline constraint satisfied.
+     */
+    private boolean mReadyDeadlineSatisfied;
+
+    /**
+     * The device isn't Dozing or this job will be in the foreground. This implicit constraint must
+     * be satisfied.
+     */
+    private boolean mReadyNotDozing;
+
+    /**
+     * The job is not restricted from running in the background (due to Battery Saver). This
+     * implicit constraint must be satisfied.
+     */
+    private boolean mReadyNotRestrictedInBg;
+
     /** Provide a handle to the service that this job will be run on. */
     public int getServiceToken() {
         return callingUid;
@@ -349,6 +375,8 @@
             requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
         }
         this.requiredConstraints = requiredConstraints;
+        mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
+        mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
 
         mLastSuccessfulRunTime = lastSuccessfulRunTime;
         mLastFailedRunTime = lastFailedRunTime;
@@ -865,7 +893,12 @@
     }
 
     boolean setDeadlineConstraintSatisfied(boolean state) {
-        return setConstraintSatisfied(CONSTRAINT_DEADLINE, state);
+        if (setConstraintSatisfied(CONSTRAINT_DEADLINE, state)) {
+            // The constraint was changed. Update the ready flag.
+            mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state;
+            return true;
+        }
+        return false;
     }
 
     boolean setIdleConstraintSatisfied(boolean state) {
@@ -882,11 +915,21 @@
 
     boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
         dozeWhitelisted = whitelisted;
-        return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
+        if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) {
+            // The constraint was changed. Update the ready flag.
+            mReadyNotDozing = state || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+            return true;
+        }
+        return false;
     }
 
     boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) {
-        return setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state);
+        if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state)) {
+            // The constraint was changed. Update the ready flag.
+            mReadyNotRestrictedInBg = state;
+            return true;
+        }
+        return false;
     }
 
     boolean setUidActive(final boolean newActiveState) {
@@ -903,6 +946,7 @@
             return false;
         }
         satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
+        mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
         return true;
     }
 
@@ -933,24 +977,15 @@
     /**
      * @return Whether or not this job is ready to run, based on its requirements. This is true if
      * the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
-     * TODO: This function is called a *lot*.  We should probably just have it check an
-     * already-computed boolean, which we updated whenever we see one of the states it depends
-     * on here change.
      */
     public boolean isReady() {
         // Deadline constraint trumps other constraints (except for periodic jobs where deadline
         // is an implementation detail. A periodic job should only run if its constraints are
         // satisfied).
-        // AppNotIdle implicit constraint must be satisfied
         // DeviceNotDozing implicit constraint must be satisfied
         // NotRestrictedInBackground implicit constraint must be satisfied
-        final boolean deadlineSatisfied = (!job.isPeriodic() && hasDeadlineConstraint()
-                && (satisfiedConstraints & CONSTRAINT_DEADLINE) != 0);
-        final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0
-                || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
-        final boolean notRestrictedInBg =
-                (satisfiedConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0;
-        return (isConstraintsSatisfied() || deadlineSatisfied) && notDozing && notRestrictedInBg;
+        return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
+                || isConstraintsSatisfied());
     }
 
     static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
@@ -971,15 +1006,13 @@
             return true;
         }
 
-        final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
-
-        int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
+        int sat = mSatisfiedConstraintsOfInterest;
         if (overrideState == OVERRIDE_SOFT) {
             // override: pretend all 'soft' requirements are satisfied
             sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
         }
 
-        return (sat & req) == req;
+        return (sat & mRequiredConstraintsOfInterest) == mRequiredConstraintsOfInterest;
     }
 
     public boolean matches(int uid, int jobId) {
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index b29b7cf..99a04d7 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -20,6 +20,7 @@
 import android.hardware.contexthub.V1_0.ContextHubMsg;
 import android.hardware.contexthub.V1_0.IContexthub;
 import android.hardware.contexthub.V1_0.Result;
+import android.hardware.location.ContextHubInfo;
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.IContextHubClient;
 import android.hardware.location.IContextHubClientCallback;
@@ -57,9 +58,9 @@
     private final ContextHubClientManager mClientManager;
 
     /*
-     * The ID of the hub that this client is attached to.
+     * The object describing the hub that this client is attached to.
      */
-    private final int mAttachedContextHubId;
+    private final ContextHubInfo mAttachedContextHubInfo;
 
     /*
      * The host end point ID of this client.
@@ -85,11 +86,12 @@
 
     /* package */ ContextHubClientBroker(
             Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
-            int contextHubId, short hostEndPointId, IContextHubClientCallback callback) {
+            ContextHubInfo contextHubInfo, short hostEndPointId,
+            IContextHubClientCallback callback) {
         mContext = context;
         mContextHubProxy = contextHubProxy;
         mClientManager = clientManager;
-        mAttachedContextHubId = contextHubId;
+        mAttachedContextHubInfo = contextHubInfo;
         mHostEndPointId = hostEndPointId;
         mCallbackInterface = callback;
     }
@@ -119,11 +121,12 @@
             ContextHubMsg messageToNanoApp = ContextHubServiceUtil.createHidlContextHubMessage(
                     mHostEndPointId, message);
 
+            int contextHubId = mAttachedContextHubInfo.getId();
             try {
-                result = mContextHubProxy.sendMessageToHub(mAttachedContextHubId, messageToNanoApp);
+                result = mContextHubProxy.sendMessageToHub(contextHubId, messageToNanoApp);
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = "
-                        + mAttachedContextHubId + ")", e);
+                        + contextHubId + ")", e);
                 result = Result.UNKNOWN_FAILURE;
             }
         } else {
@@ -156,7 +159,7 @@
      * @return the ID of the context hub this client is attached to
      */
     /* package */ int getAttachedContextHubId() {
-        return mAttachedContextHubId;
+        return mAttachedContextHubInfo.getId();
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 4243f02..eda8c6f 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -19,13 +19,13 @@
 import android.content.Context;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
 import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.location.ContextHubInfo;
 import android.hardware.location.IContextHubClient;
 import android.hardware.location.IContextHubClientCallback;
 import android.hardware.location.NanoAppMessage;
 import android.os.RemoteException;
 import android.util.Log;
 
-import java.util.NoSuchElementException;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 
@@ -80,15 +80,15 @@
      * Registers a new client with the service.
      *
      * @param clientCallback the callback interface of the client to register
-     * @param contextHubId   the ID of the hub this client is attached to
+     * @param contextHubInfo the object describing the hub this client is attached to
      *
      * @return the client interface
      *
      * @throws IllegalStateException if max number of clients have already registered
      */
     /* package */ IContextHubClient registerClient(
-            IContextHubClientCallback clientCallback, int contextHubId) {
-        ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubId);
+            IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
+        ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubInfo);
 
         try {
             broker.attachDeathRecipient();
@@ -183,14 +183,14 @@
      * manager.
      *
      * @param clientCallback the callback interface of the client to register
-     * @param contextHubId   the ID of the hub this client is attached to
+     * @param contextHubInfo the object describing the hub this client is attached to
      *
      * @return the ContextHubClientBroker object
      *
      * @throws IllegalStateException if max number of clients have already registered
      */
     private synchronized ContextHubClientBroker createNewClientBroker(
-            IContextHubClientCallback clientCallback, int contextHubId) {
+            IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
         if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
             throw new IllegalStateException("Could not register client - max limit exceeded");
         }
@@ -198,10 +198,11 @@
         ContextHubClientBroker broker = null;
         int id = mNextHostEndpointId;
         for (int i = 0; i <= MAX_CLIENT_ID; i++) {
-            if (!mHostEndPointIdToClientMap.containsKey((short)id)) {
+            if (!mHostEndPointIdToClientMap.containsKey((short) id)) {
                 broker = new ContextHubClientBroker(
-                        mContext, mContextHubProxy, this, contextHubId, (short)id, clientCallback);
-                mHostEndPointIdToClientMap.put((short)id, broker);
+                        mContext, mContextHubProxy, this, contextHubInfo, (short) id,
+                        clientCallback);
+                mHostEndPointIdToClientMap.put((short) id, broker);
                 mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
                 break;
             }
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 27509de..96e9337 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -170,8 +170,9 @@
 
         HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
         for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
+            ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
             IContextHubClient client = mClientManager.registerClient(
-                    createDefaultClientCallback(contextHubId), contextHubId);
+                    createDefaultClientCallback(contextHubId), contextHubInfo);
             defaultClientMap.put(contextHubId, client);
 
             try {
@@ -623,7 +624,8 @@
             throw new NullPointerException("Cannot register client with null callback");
         }
 
-        return mClientManager.registerClient(clientCallback, contextHubId);
+        ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
+        return mClientManager.registerClient(clientCallback, contextHubInfo);
     }
 
     /**
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 452b699..4f4b6bf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -72,6 +72,7 @@
     static final int NTWK_ALLOWED_TMP_WHITELIST = 4;
     static final int NTWK_BLOCKED_BG_RESTRICT = 5;
     static final int NTWK_ALLOWED_DEFAULT = 6;
+    static final int NTWK_ALLOWED_SYSTEM = 7;
 
     private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
     private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 61d67b7..099671d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,6 +16,8 @@
 
 package com.android.server.net;
 
+import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
+
 import android.net.Network;
 import android.net.NetworkTemplate;
 import android.telephony.SubscriptionPlan;
@@ -46,6 +48,28 @@
     public abstract boolean isUidNetworkingBlocked(int uid, String ifname);
 
     /**
+     * Figure out if networking is blocked for a given set of conditions.
+     *
+     * This is used by ConnectivityService via passing stale copies of conditions, so it must not
+     * take any locks.
+     *
+     * @param uid The target uid.
+     * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+     * @param isNetworkMetered True if the network is metered.
+     * @param isBackgroundRestricted True if data saver is enabled.
+     *
+     * @return true if networking is blocked for the UID under the specified conditions.
+     */
+    public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
+            boolean isBackgroundRestricted) {
+        // Log of invoking internal function is disabled because it will be called very
+        // frequently. And metrics are unlikely needed on this method because the callers are
+        // external and this method doesn't take any locks or perform expensive operations.
+        return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+                isBackgroundRestricted, null);
+    }
+
+    /**
      * Informs that an appId has been added or removed from the temp-powersave-whitelist so that
      * that network rules for that appId can be updated.
      *
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 48e09d7..d799642 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -99,6 +99,7 @@
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_SYSTEM;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_WHITELIST;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_WHITELIST;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT;
@@ -4837,46 +4838,75 @@
         final long startTime = mStatLogger.getTime();
 
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-        final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
-
-        mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
-
-        return ret;
-    }
-
-    private boolean isUidNetworkingBlockedInternal(int uid, boolean isNetworkMetered) {
         final int uidRules;
         final boolean isBackgroundRestricted;
         synchronized (mUidRulesFirstLock) {
             uidRules = mUidRules.get(uid, RULE_NONE);
             isBackgroundRestricted = mRestrictBackground;
         }
-        if (hasRule(uidRules, RULE_REJECT_ALL)) {
-            mLogger.networkBlocked(uid, NTWK_BLOCKED_POWER);
-            return true;
+        final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+                isBackgroundRestricted, mLogger);
+
+        mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
+
+        return ret;
+    }
+
+    private static boolean isSystem(int uid) {
+        return uid < Process.FIRST_APPLICATION_UID;
+    }
+
+    static boolean isUidNetworkingBlockedInternal(int uid, int uidRules, boolean isNetworkMetered,
+            boolean isBackgroundRestricted, @Nullable NetworkPolicyLogger logger) {
+        final int reason;
+        // Networks are never blocked for system components
+        if (isSystem(uid)) {
+            reason = NTWK_ALLOWED_SYSTEM;
         }
-        if (!isNetworkMetered) {
-            mLogger.networkBlocked(uid, NTWK_ALLOWED_NON_METERED);
-            return false;
+        else if (hasRule(uidRules, RULE_REJECT_ALL)) {
+            reason = NTWK_BLOCKED_POWER;
         }
-        if (hasRule(uidRules, RULE_REJECT_METERED)) {
-            mLogger.networkBlocked(uid, NTWK_BLOCKED_BLACKLIST);
-            return true;
+        else if (!isNetworkMetered) {
+            reason = NTWK_ALLOWED_NON_METERED;
         }
-        if (hasRule(uidRules, RULE_ALLOW_METERED)) {
-            mLogger.networkBlocked(uid, NTWK_ALLOWED_WHITELIST);
-            return false;
+        else if (hasRule(uidRules, RULE_REJECT_METERED)) {
+            reason = NTWK_BLOCKED_BLACKLIST;
         }
-        if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
-            mLogger.networkBlocked(uid, NTWK_ALLOWED_TMP_WHITELIST);
-            return false;
+        else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
+            reason = NTWK_ALLOWED_WHITELIST;
         }
-        if (isBackgroundRestricted) {
-            mLogger.networkBlocked(uid, NTWK_BLOCKED_BG_RESTRICT);
-            return true;
+        else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
+            reason = NTWK_ALLOWED_TMP_WHITELIST;
         }
-        mLogger.networkBlocked(uid, NTWK_ALLOWED_DEFAULT);
-        return false;
+        else if (isBackgroundRestricted) {
+            reason = NTWK_BLOCKED_BG_RESTRICT;
+        }
+        else {
+            reason = NTWK_ALLOWED_DEFAULT;
+        }
+
+        final boolean blocked;
+        switch(reason) {
+            case NTWK_ALLOWED_DEFAULT:
+            case NTWK_ALLOWED_NON_METERED:
+            case NTWK_ALLOWED_TMP_WHITELIST:
+            case NTWK_ALLOWED_WHITELIST:
+            case NTWK_ALLOWED_SYSTEM:
+                blocked = false;
+                break;
+            case NTWK_BLOCKED_POWER:
+            case NTWK_BLOCKED_BLACKLIST:
+            case NTWK_BLOCKED_BG_RESTRICT:
+                blocked = true;
+                break;
+            default:
+                throw new IllegalArgumentException();
+        }
+        if (logger != null) {
+            logger.networkBlocked(uid, reason);
+        }
+
+        return blocked;
     }
 
     private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal {
@@ -4918,11 +4948,18 @@
         public boolean isUidNetworkingBlocked(int uid, String ifname) {
             final long startTime = mStatLogger.getTime();
 
+            final int uidRules;
+            final boolean isBackgroundRestricted;
+            synchronized (mUidRulesFirstLock) {
+                uidRules = mUidRules.get(uid, RULE_NONE);
+                isBackgroundRestricted = mRestrictBackground;
+            }
             final boolean isNetworkMetered;
             synchronized (mNetworkPoliciesSecondLock) {
                 isNetworkMetered = mMeteredIfaces.contains(ifname);
             }
-            final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
+            final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+                    isBackgroundRestricted, mLogger);
 
             mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 506cc44..348b8e6 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -719,8 +719,7 @@
                     return;
                 }
                 final long now = System.currentTimeMillis();
-                MetricsLogger.action(r.getLogMaker(now)
-                        .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+                MetricsLogger.action(r.getItemLogMaker()
                         .setType(MetricsEvent.TYPE_ACTION)
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
@@ -865,8 +864,7 @@
                     r.stats.onExpansionChanged(userAction, expanded);
                     final long now = System.currentTimeMillis();
                     if (userAction) {
-                        MetricsLogger.action(r.getLogMaker(now)
-                                .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+                        MetricsLogger.action(r.getItemLogMaker()
                                 .setType(expanded ? MetricsEvent.TYPE_DETAIL
                                         : MetricsEvent.TYPE_COLLAPSE));
                     }
@@ -5842,8 +5840,7 @@
         mArchive.record(r.sbn);
 
         final long now = System.currentTimeMillis();
-        final LogMaker logMaker = r.getLogMaker(now)
-                .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+        final LogMaker logMaker = r.getItemLogMaker()
                 .setType(MetricsEvent.TYPE_DISMISS)
                 .setSubtype(reason);
         if (rank != -1 && count != -1) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 65ec580..19a62d9 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -611,6 +611,7 @@
     }
 
     public void applyAdjustments() {
+        long now = System.currentTimeMillis();
         synchronized (mAdjustments) {
             for (Adjustment adjustment: mAdjustments) {
                 Bundle signals = adjustment.getSignals();
@@ -618,17 +619,25 @@
                     final ArrayList<String> people =
                             adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
                     setPeopleOverride(people);
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_PEOPLE, people.size()));
                 }
                 if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
                     final ArrayList<SnoozeCriterion> snoozeCriterionList =
                             adjustment.getSignals().getParcelableArrayList(
                                     Adjustment.KEY_SNOOZE_CRITERIA);
                     setSnoozeCriteria(snoozeCriterionList);
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SNOOZE_CRITERIA,
+                                    snoozeCriterionList.size()));
                 }
                 if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
                     final String groupOverrideKey =
                             adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
                     setOverrideGroupKey(groupOverrideKey);
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_GROUP_KEY,
+                                    groupOverrideKey));
                 }
                 if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
                     // Only allow user sentiment update from assistant if user hasn't already
@@ -637,19 +646,31 @@
                             && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
                         setUserSentiment(adjustment.getSignals().getInt(
                                 Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
+                        MetricsLogger.action(getAdjustmentLogMaker()
+                                .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_USER_SENTIMENT,
+                                        getUserSentiment()));
                     }
                 }
                 if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) {
                     setSmartActions(signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_ACTIONS,
+                                    getSmartActions().size()));
                 }
                 if (signals.containsKey(Adjustment.KEY_SMART_REPLIES)) {
                     setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES));
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_REPLIES,
+                                    getSmartReplies().size()));
                 }
                 if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
                     int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
                     importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
                     importance = Math.min(IMPORTANCE_HIGH, importance);
                     setAssistantImportance(importance);
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_IMPORTANCE,
+                                    importance));
                 }
             }
         }
@@ -1203,6 +1224,16 @@
         return getLogMaker(System.currentTimeMillis());
     }
 
+    public LogMaker getItemLogMaker() {
+        return getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM);
+    }
+
+    public LogMaker getAdjustmentLogMaker() {
+        return getLogMaker()
+                .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+                .setType(MetricsEvent.NOTIFICATION_ASSISTANT_ADJUSTMENT);
+    }
+
     @VisibleForTesting
     static final class Light {
         public final int color;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 760f155..44b80c1 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1212,27 +1212,89 @@
     }
 
     private final class Metrics extends Callback {
-        private static final String COUNTER_PREFIX = "dnd_mode_";
+        private static final String COUNTER_MODE_PREFIX = "dnd_mode_";
+        private static final String COUNTER_TYPE_PREFIX = "dnd_type_";
+        private static final int DND_OFF = 0;
+        private static final int DND_ON_MANUAL = 1;
+        private static final int DND_ON_AUTOMATIC = 2;
+        private static final String COUNTER_RULE = "dnd_rule_count";
         private static final long MINIMUM_LOG_PERIOD_MS = 60 * 1000;
 
+        // Total silence, alarms only, priority only
         private int mPreviousZenMode = -1;
-        private long mBeginningMs = 0L;
+        private long mModeLogTimeMs = 0L;
+
+        private int mNumZenRules = -1;
+        private long mRuleCountLogTime = 0L;
+
+        // automatic (1) vs manual (0) vs dnd off (2)
+        private int mPreviousZenType = -1;
+        private long mTypeLogTimeMs = 0L;
 
         @Override
         void onZenModeChanged() {
             emit();
         }
 
+        @Override
+        void onConfigChanged() {
+            emit();
+        }
+
         private void emit() {
             mHandler.postMetricsTimer();
+            emitZenMode();
+            emitRules();
+            emitDndType();
+        }
+
+        private void emitZenMode() {
             final long now = SystemClock.elapsedRealtime();
-            final long since = (now - mBeginningMs);
+            final long since = (now - mModeLogTimeMs);
             if (mPreviousZenMode != mZenMode || since > MINIMUM_LOG_PERIOD_MS) {
                 if (mPreviousZenMode != -1) {
-                    MetricsLogger.count(mContext, COUNTER_PREFIX + mPreviousZenMode, (int) since);
+                    MetricsLogger.count(
+                            mContext, COUNTER_MODE_PREFIX + mPreviousZenMode, (int) since);
                 }
                 mPreviousZenMode = mZenMode;
-                mBeginningMs = now;
+                mModeLogTimeMs = now;
+            }
+        }
+
+        private void emitRules() {
+            final long now = SystemClock.elapsedRealtime();
+            final long since = (now - mRuleCountLogTime);
+            synchronized (mConfig) {
+                int numZenRules = mConfig.automaticRules.size();
+                if (mNumZenRules != numZenRules
+                        || since > MINIMUM_LOG_PERIOD_MS) {
+                    if (mNumZenRules != -1) {
+                        MetricsLogger.count(mContext, COUNTER_RULE,
+                                numZenRules - mNumZenRules);
+                    }
+                    mNumZenRules = numZenRules;
+
+                    mRuleCountLogTime = since;
+                }
+            }
+        }
+
+        private void emitDndType() {
+            final long now = SystemClock.elapsedRealtime();
+            final long since = (now - mTypeLogTimeMs);
+            synchronized (mConfig) {
+                boolean dndOn = mZenMode != Global.ZEN_MODE_OFF;
+                int zenType = !dndOn ? DND_OFF
+                        : (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC;
+                if (zenType != mPreviousZenType
+                        || since > MINIMUM_LOG_PERIOD_MS) {
+                    if (mPreviousZenType != -1) {
+                        MetricsLogger.count(
+                                mContext, COUNTER_TYPE_PREFIX + mPreviousZenType, (int) since);
+                    }
+                    mTypeLogTimeMs = now;
+                    mPreviousZenType = zenType;
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index be24c71..5569822 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -243,7 +243,7 @@
     }
 
     /**
-     * Called by {@link PowerManagerService} to update the battery saver stete.
+     * Called by {@link PowerManagerService} to update the battery saver state.
      */
     public void enableBatterySaver(boolean enable, int reason) {
         synchronized (mLock) {
@@ -290,8 +290,8 @@
      * This method is called only in the following cases:
      * - When battery saver becomes activated.
      * - When battery saver becomes deactivated.
-     * - When battery saver is on the interactive state changes.
-     * - When battery saver is on the battery saver policy changes.
+     * - When battery saver is on and the interactive state changes.
+     * - When battery saver is on and the battery saver policy changes.
      */
     void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
         final LowPowerModeListener[] listeners;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 15f6938..ed3e6c6 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -435,14 +435,14 @@
                 final RecentsAnimationController recentsAnimationController =
                         mService.getRecentsAnimationController();
                 if (recentsAnimationController != null
-                        && recentsAnimationController.hasInputConsumerForApp(w.mAppToken)) {
+                        && recentsAnimationController.shouldApplyInputConsumer(w.mAppToken)) {
                     if (recentsAnimationController.updateInputConsumerForApp(
                             recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
                         addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
                         mAddRecentsAnimationInputConsumerHandle = false;
                     }
-                    // Skip adding the window below regardless of whether there is an input consumer
-                    // to handle it
+                    // If the target app window does not yet exist, then we don't add the input
+                    // consumer window, but also don't add the app window below.
                     return;
                 }
             }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6fef1630..5c80759 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -458,10 +458,9 @@
         mRunner = null;
         mCanceled = true;
 
-        // Clear associated input consumers
+        // Update the input windows after the animation is complete
         final InputMonitor inputMonitor =
                 mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
-        inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
         inputMonitor.updateInputWindowsLw(true /*force*/);
 
         // We have deferred all notifications to the target app as a part of the recents animation,
@@ -494,6 +493,11 @@
     @Override
     public void binderDied() {
         cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
+
+        // Clear associated input consumers on runner death
+        final InputMonitor inputMonitor =
+                mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+        inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
     }
 
     void checkAnimationReady(WallpaperController wallpaperController) {
@@ -516,8 +520,14 @@
                 && isTargetOverWallpaper();
     }
 
-    boolean hasInputConsumerForApp(AppWindowToken appToken) {
-        return mInputConsumerEnabled && isAnimatingApp(appToken);
+    /**
+     * @return Whether to use the input consumer to override app input to route home/recents.
+     */
+    boolean shouldApplyInputConsumer(AppWindowToken appToken) {
+        // Only apply the input consumer if it is enabled, it is not the target (home/recents)
+        // being revealed with the transition, and we are actively animating the app as a part of
+        // the animation
+        return mInputConsumerEnabled && mTargetAppToken != appToken && isAnimatingApp(appToken);
     }
 
     boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
@@ -675,6 +685,7 @@
         final String innerPrefix = prefix + "  ";
         pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
         pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
+        pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size());
         pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
         pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
         pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 21e807e..6fd17955 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.graphics.Bitmap.CompressFormat.*;
+
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -361,6 +362,7 @@
 
             // For snapshots with reduced resolution, do not create or save full sized bitmaps
             if (mSnapshot.isReducedResolution()) {
+                swBitmap.recycle();
                 return true;
             }
 
@@ -373,6 +375,8 @@
                 Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
                 return false;
             }
+            reduced.recycle();
+            swBitmap.recycle();
             return true;
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7342ba1..056e92e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2675,8 +2675,9 @@
     public void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
         synchronized (mWindowMap) {
             if (mRecentsAnimationController != null) {
-                mRecentsAnimationController.cleanupAnimation(reorderMode);
+                final RecentsAnimationController controller = mRecentsAnimationController;
                 mRecentsAnimationController = null;
+                controller.cleanupAnimation(reorderMode);
                 mAppTransition.updateBooster();
             }
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index b06be1a..2dbbf55 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -70,7 +70,8 @@
     }
 
     @Override
-    public boolean checkDeviceIdentifierAccess(String packageName, int userHandle) {
+    public boolean checkDeviceIdentifierAccess(String packageName, int userHandle, int pid,
+            int uid) {
         return false;
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 70cdba2..b88165e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -206,6 +206,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.StatsLog;
 import android.util.Xml;
 import android.view.IWindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -7871,7 +7872,21 @@
     }
 
     @Override
-    public boolean checkDeviceIdentifierAccess(String packageName, int userHandle) {
+    public boolean checkDeviceIdentifierAccess(String packageName, int userHandle, int pid,
+            int uid) {
+        // If the caller is not a system app then it should only be able to check its own device
+        // identifier access.
+        int callingAppId = UserHandle.getAppId(mInjector.binderGetCallingUid());
+        if (callingAppId >= Process.FIRST_APPLICATION_UID
+                && callingAppId != UserHandle.getAppId(uid)) {
+            return false;
+        }
+        // A device or profile owner must also have the READ_PHONE_STATE permission to access device
+        // identifiers. If the package being checked does not have this permission then deny access.
+        if (mContext.checkPermission(android.Manifest.permission.READ_PHONE_STATE, pid, uid)
+                != PackageManager.PERMISSION_GRANTED) {
+            return false;
+        }
         // Allow access to the device owner.
         ComponentName deviceOwner = getDeviceOwnerComponent(true);
         if (deviceOwner != null && deviceOwner.getPackageName().equals(packageName)) {
@@ -9373,6 +9388,7 @@
             }
             saveUserRestrictionsLocked(userHandle);
         }
+        StatsLog.write(StatsLog.USER_RESTRICTION_CHANGED, key, enabledFromThisOwner);
         if (SecurityLog.isLoggingEnabled()) {
             final int eventTag = enabledFromThisOwner
                     ? SecurityLog.TAG_USER_RESTRICTION_ADDED
diff --git a/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
similarity index 78%
rename from services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java
rename to services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 1752479..b63138e 100644
--- a/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -16,19 +16,27 @@
 
 package com.android.server.job.controllers;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
 import static org.junit.Assert.assertEquals;
 
 import android.app.job.JobInfo;
 import android.content.ComponentName;
+import android.content.pm.PackageManagerInternal;
 import android.os.SystemClock;
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
 
 import java.time.Clock;
 import java.time.ZoneOffset;
@@ -37,8 +45,17 @@
 public class JobStatusTest {
     private static final double DELTA = 0.00001;
 
+    private MockitoSession mMockingSession;
+
     @Before
     public void setUp() throws Exception {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .mockStatic(LocalServices.class)
+                .startMocking();
+        doReturn(mock(PackageManagerInternal.class))
+                .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
         // Freeze the clocks at this moment in time
         JobSchedulerService.sSystemClock =
                 Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
@@ -48,6 +65,13 @@
                 Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
     }
 
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
     @Test
     public void testFraction() throws Exception {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 1eb88ba..113ee2d 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -26,13 +26,21 @@
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
+import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.TAG_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.os.Process.SYSTEM_UID;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
@@ -124,6 +132,7 @@
 import android.text.format.Time;
 import android.util.DataUnit;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Range;
 import android.util.RecurrenceRule;
 
@@ -171,6 +180,7 @@
 import java.time.ZoneId;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Iterator;
@@ -1644,6 +1654,76 @@
                 true);
     }
 
+    /**
+     * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+     * conditions.
+     */
+    @Test
+    public void testIsUidNetworkingBlocked() {
+        final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
+
+        // Metered network. Data saver on.
+        expectedBlockedStates.add(new Pair<>(true, RULE_NONE));
+        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
+        expectedBlockedStates.add(new Pair<>(true, RULE_ALLOW_ALL));
+        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+        verifyNetworkBlockedState(
+                true /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
+        expectedBlockedStates.clear();
+
+        // Metered network. Data saver off.
+        expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
+        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
+        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+        verifyNetworkBlockedState(
+                true /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
+        expectedBlockedStates.clear();
+
+        // Non-metered network. Data saver on.
+        expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
+        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_REJECT_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
+        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+        verifyNetworkBlockedState(
+                false /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
+
+        // Non-metered network. Data saver off. The result is the same as previous case since
+        // the network is blocked only for RULE_REJECT_ALL regardless of data saver.
+        verifyNetworkBlockedState(
+                false /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
+        expectedBlockedStates.clear();
+    }
+
+    private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
+            ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
+        final NetworkPolicyManagerInternal npmi = LocalServices
+                .getService(NetworkPolicyManagerInternal.class);
+
+        for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
+            final boolean expectedResult = pair.first;
+            final int rule = pair.second;
+            assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
+                    expectedResult,
+                    npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+            assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
+                    npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+        }
+    }
+
+    private String formatBlockedStateError(int uid, int rule, boolean metered,
+            boolean backgroundRestricted) {
+        return String.format(
+                "Unexpected BlockedState: (uid=%d, rule=%s, metered=%b, backgroundRestricted=%b)",
+                uid, uidRulesToString(rule), metered, backgroundRestricted);
+    }
+
     private SubscriptionPlan buildMonthlyDataPlan(ZonedDateTime start, long limitBytes) {
         return SubscriptionPlan.Builder
                 .createRecurringMonthly(start)
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
index ea90ffd..0da5742 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
@@ -20,11 +20,14 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
 
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
 
 import android.platform.test.annotations.Presubmit;
 
@@ -131,4 +134,57 @@
         new ActivityBuilder(mService).setTask(fullscreenTask).build();
         return fullscreenStack;
     }
+
+    /**
+     * Verifies the correct activity is returned when querying the top running activity.
+     */
+    @Test
+    public void testTopRunningActivity() {
+        // Create stack to hold focus.
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        final KeyguardController keyguard = mSupervisor.getKeyguardController();
+        final ActivityStack stack = mSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(stack).build();
+
+        // Make sure the top running activity is not affected when keyguard is not locked.
+        assertTopRunningActivity(activity, display);
+
+        // Check to make sure activity not reported when it cannot show on lock and lock is on.
+        doReturn(true).when(keyguard).isKeyguardLocked();
+        assertEquals(activity, display.topRunningActivity());
+        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+        // Change focus to stack with activity.
+        stack.moveToFront("focusChangeToTestStack");
+        assertEquals(stack, display.getFocusedStack());
+        assertEquals(activity, display.topRunningActivity());
+        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+        // Add activity that should be shown on the keyguard.
+        final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setStack(stack)
+                .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
+                .build();
+
+        // Ensure the show when locked activity is returned.
+        assertTopRunningActivity(showWhenLockedActivity, display);
+
+        // Change focus back to empty stack.
+        emptyStack.moveToFront("focusChangeToEmptyStack");
+        assertEquals(emptyStack, display.getFocusedStack());
+        // If there is no running activity in focused stack, the running activity in next focusable
+        // stack should be returned.
+        assertTopRunningActivity(showWhenLockedActivity, display);
+    }
+
+    private static void assertTopRunningActivity(ActivityRecord top, ActivityDisplay display) {
+        assertEquals(top, display.topRunningActivity());
+        assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
+    }
 }
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 c44e492..f42f5b1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -64,6 +64,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -102,6 +103,7 @@
  *     com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
  */
 @SmallTest
+@FlakyTest(bugId = 113616538)
 @RunWith(AndroidJUnit4.class)
 public class ActivityManagerServiceTest {
     private static final String TAG = ActivityManagerServiceTest.class.getSimpleName();
@@ -173,7 +175,7 @@
                 true); // expectNotify
 
         // Explicitly setting the seq counter for more verification.
-        mAms.mProcStateSeqCounter = 42;
+        mAms.mProcessList.mProcStateSeqCounter = 42;
 
         // Uid state is not moving from background to foreground or vice versa.
         verifySeqCounterAndInteractions(uidRec,
@@ -260,7 +262,7 @@
 
         final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid, null);
         appRec.thread = Mockito.mock(IApplicationThread.class);
-        mAms.mLruProcesses.add(appRec);
+        mAms.mProcessList.mLruProcesses.add(appRec);
 
         return uidRec;
     }
@@ -275,11 +277,11 @@
         uidRec.curProcState = curState;
         mAms.incrementProcStateSeqAndNotifyAppsLocked();
 
-        assertEquals(expectedGlobalCounter, mAms.mProcStateSeqCounter);
+        assertEquals(expectedGlobalCounter, mAms.mProcessList.mProcStateSeqCounter);
         assertEquals(expectedCurProcStateSeq, uidRec.curProcStateSeq);
 
-        for (int i = mAms.mLruProcesses.size() - 1; i >= 0; --i) {
-            final ProcessRecord app = mAms.mLruProcesses.get(i);
+        for (int i = mAms.mProcessList.getLruSizeLocked() - 1; i >= 0; --i) {
+            final ProcessRecord app = mAms.mProcessList.mLruProcesses.get(i);
             // AMS should notify apps only for block states other than NETWORK_STATE_NO_CHANGE.
             if (app.uid == uidRec.uid && expectedBlockState == NETWORK_STATE_BLOCK) {
                 verify(app.thread).setNetworkBlockSeq(uidRec.curProcStateSeq);
@@ -829,4 +831,4 @@
             mRestricted = restricted;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index ba25b16..2dfb375 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -16,38 +16,59 @@
 
 package com.android.server.am;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+
 import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
 import android.app.IActivityManager;
-import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.os.RemoteException;
-import android.test.AndroidTestCase;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
 
 import java.util.List;
 
-public class ActivityManagerTest extends AndroidTestCase {
+import androidx.test.filters.FlakyTest;
 
-    IActivityManager service;
-    @Override
+/**
+ * Tests for {@link ActivityManager}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:com.android.server.am.ActivityManagerTest
+ */
+@Presubmit
+@FlakyTest(detail = "Promote to presubmit if stable")
+public class ActivityManagerTest {
+
+    private IActivityManager service;
+
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         service = ActivityManager.getService();
     }
 
+    @Test
     public void testTaskIdsForRunningUsers() throws RemoteException {
-        for(int userId : service.getRunningUserIds()) {
+        int[] runningUserIds = service.getRunningUserIds();
+        assertThat(runningUserIds).isNotEmpty();
+        for (int userId : runningUserIds) {
             testTaskIdsForUser(userId);
         }
     }
 
     private void testTaskIdsForUser(int userId) throws RemoteException {
-        List<ActivityManager.RecentTaskInfo> recentTasks = service.getRecentTasks(
-                100, 0, userId).getList();
-        if(recentTasks != null) {
-            for(ActivityManager.RecentTaskInfo recentTask : recentTasks) {
-                int taskId = recentTask.persistentId;
+        List<?> recentTasks = service.getRecentTasks(100, 0, userId).getList();
+        if (recentTasks != null) {
+            for (Object elem : recentTasks) {
+                assertThat(elem).isInstanceOf(RecentTaskInfo.class);
+                RecentTaskInfo recentTask = (RecentTaskInfo) elem;
+                int taskId = recentTask.taskId;
                 assertEquals("The task id " + taskId + " should not belong to user " + userId,
-                        taskId / UserHandle.PER_USER_RANGE, userId);
+                             taskId / UserHandle.PER_USER_RANGE, userId);
             }
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index cc7a24d..2c993d3 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -25,7 +25,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
 
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
@@ -307,62 +306,6 @@
     }
 
     /**
-     * Verifies the correct activity is returned when querying the top running activity.
-     */
-    @Test
-    public void testTopRunningActivity() throws Exception {
-        // Create stack to hold focus
-        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
-        final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(stack).build();
-
-        // Make sure the top running activity is not affected when keyguard is not locked
-        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
-        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */));
-
-        // Check to make sure activity not reported when it cannot show on lock and lock is on.
-        doReturn(true).when(keyguard).isKeyguardLocked();
-        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
-        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */));
-
-        // Change focus to stack with activity.
-        stack.moveToFront("focusChangeToTestStack");
-        assertEquals(stack, display.getFocusedStack());
-        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
-        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */));
-
-        // Add activity that should be shown on the keyguard.
-        final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .setStack(stack)
-                .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
-                .build();
-
-        // Ensure the show when locked activity is returned.
-        assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked());
-        assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */));
-
-        // Change focus back to empty stack
-        emptyStack.moveToFront("focusChangeToEmptyStack");
-        assertEquals(emptyStack, display.getFocusedStack());
-        // Looking for running activity only in top and focused stack, so nothing should be returned
-        // from empty stack.
-        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked());
-        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */));
-    }
-
-    /**
      * Verify that split-screen primary stack will be chosen if activity is launched that targets
      * split-screen secondary, but a matching existing instance is found on top of split-screen
      * primary stack.
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 5fcd2aa..53f67af 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -26,6 +26,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
@@ -35,10 +38,14 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyInt;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 import android.content.pm.ActivityInfo;
 import android.os.UserHandle;
@@ -655,6 +662,39 @@
     }
 
     @Test
+    public void testFinishCurrentActivity() {
+        // Create 2 activities on a new display.
+        final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+        final ActivityStack stack1 = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityStack stack2 = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        // There is still an activity1 in stack1 so the activity2 should be added to finishing list
+        // that will be destroyed until idle.
+        final ActivityRecord activity2 = finishCurrentActivity(stack2);
+        assertEquals(FINISHING, activity2.getState());
+        assertTrue(mSupervisor.mFinishingActivities.contains(activity2));
+
+        // The display becomes empty. Since there is no next activity to be idle, the activity
+        // should be destroyed immediately with updating configuration to restore original state.
+        final ActivityRecord activity1 = finishCurrentActivity(stack1);
+        assertEquals(DESTROYING, activity1.getState());
+        verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */,
+                eq(display.mDisplayId), anyBoolean(), anyBoolean());
+    }
+
+    private ActivityRecord finishCurrentActivity(ActivityStack stack) {
+        final ActivityRecord activity = stack.topRunningActivityLocked();
+        assertNotNull(activity);
+        activity.setState(PAUSED, "finishCurrentActivity");
+        activity.makeFinishingLocked();
+        stack.finishCurrentActivityLocked(activity, ActivityStack.FINISH_AFTER_VISIBLE,
+                false /* oomAdj */, "finishCurrentActivity");
+        return activity;
+    }
+
+    @Test
     public void testShouldSleepActivities() throws Exception {
         // When focused activity and keyguard is going away, we should not sleep regardless
         // of the display state
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 58fe70d..01d51e4 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -58,8 +58,9 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
-import android.os.HandlerThread;
+import android.os.Handler;
 import android.os.Looper;
+import android.os.Process;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
 import android.testing.DexmakerShareClassLoaderRule;
@@ -69,7 +70,9 @@
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.app.IVoiceInteractor;
+import com.android.server.AppOpsService;
 import com.android.server.AttributeCache;
+import com.android.server.ServiceThread;
 import com.android.server.wm.AppWindowContainerController;
 import com.android.server.wm.PinnedStackWindowController;
 import com.android.server.wm.RootWindowContainerController;
@@ -82,6 +85,7 @@
 import org.junit.Before;
 import org.mockito.MockitoAnnotations;
 
+import java.io.File;
 import java.util.List;
 
 /**
@@ -97,7 +101,7 @@
             new DexmakerShareClassLoaderRule();
 
     private final Context mContext = InstrumentationRegistry.getContext();
-    private HandlerThread mHandlerThread;
+    final TestInjector mTestInjector = new TestInjector();
 
     ActivityTaskManagerService mService;
     ActivityStackSupervisor mSupervisor;
@@ -115,13 +119,12 @@
             MockitoAnnotations.initMocks(this);
             AttributeCache.init(mContext);
         }
-        mHandlerThread = new HandlerThread("ActivityTestsBaseThread");
-        mHandlerThread.start();
+        mTestInjector.setUp();
     }
 
     @After
     public void tearDown() {
-        mHandlerThread.quitSafely();
+        mTestInjector.tearDown();
     }
 
     protected ActivityTaskManagerService createActivityTaskManagerService() {
@@ -143,7 +146,7 @@
     }
 
     ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
-        final TestActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
+        final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
         setupActivityManagerService(am, atm);
         return am;
     }
@@ -173,6 +176,7 @@
         doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
         doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
         doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
+        am.mActivityTaskManager = atm;
         am.mWindowManager = prepareMockWindowManager();
         atm.setWindowManager(am.mWindowManager);
 
@@ -192,8 +196,6 @@
         // An id appended to the end of the component name to make it unique
         private static int sCurrentActivityId = 0;
 
-
-
         private final ActivityTaskManagerService mService;
 
         private ComponentName mComponent;
@@ -487,6 +489,40 @@
         }
     }
 
+    private static class TestInjector extends ActivityManagerService.Injector {
+        private ServiceThread mHandlerThread;
+
+        @Override
+        public Context getContext() {
+            return InstrumentationRegistry.getContext();
+        }
+
+        @Override
+        public AppOpsService getAppOpsService(File file, Handler handler) {
+            return null;
+        }
+
+        @Override
+        public Handler getUiHandler(ActivityManagerService service) {
+            return mHandlerThread.getThreadHandler();
+        }
+
+        @Override
+        public boolean isNetworkRestrictedForUid(int uid) {
+            return false;
+        }
+
+        void setUp() {
+            mHandlerThread = new ServiceThread("ActivityTestsThread",
+                    Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
+            mHandlerThread.start();
+        }
+
+        void tearDown() {
+            mHandlerThread.quitSafely();
+        }
+    }
+
     /**
      * An {@link ActivityManagerService} subclass which provides a test
      * {@link ActivityStackSupervisor}.
@@ -495,8 +531,8 @@
 
         private ActivityManagerInternal mInternal;
 
-        TestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
-            super(context, atm);
+        TestActivityManagerService(TestInjector testInjector) {
+            super(testInjector, testInjector.mHandlerThread);
             mUgmInternal = mock(UriGrantsManagerInternal.class);
         }
 
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 3819e21..06d41f1 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -21,6 +21,7 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -37,6 +38,7 @@
  */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
+@FlakyTest(bugId = 113616538)
 public class AppErrorDialogTest {
 
     private Context mContext;
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
index a030210..1b823ff 100644
--- a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -46,6 +46,7 @@
 import android.view.IWindowManager;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -67,6 +68,7 @@
  * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
  */
 @MediumTest
+@FlakyTest(bugId = 113616538)
 @RunWith(AndroidJUnit4.class)
 public class AssistDataRequesterTest extends ActivityTestsBase {
 
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index 62c5734..75f7c4c 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -45,7 +45,7 @@
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
-public class BroadcastRecordTest extends ActivityTestsBase {
+public class BroadcastRecordTest {
 
     @Test
     public void testCleanupDisabledPackageReceivers() {
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 1276f65..27e8c63 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -116,8 +116,7 @@
 
         mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
         mService = spy(new MyTestActivityTaskManagerService(mContext));
-        final TestActivityManagerService am =
-                spy(new MyTestActivityManagerService(mContext, mService));
+        final TestActivityManagerService am = spy(new MyTestActivityManagerService());
         setupActivityManagerService(am, mService);
         mRecentTasks = (TestRecentTasks) mService.getRecentTasks();
         mRecentTasks.loadParametersFromResources(mContext.getResources());
@@ -848,8 +847,8 @@
     }
 
     private class MyTestActivityManagerService extends TestActivityManagerService {
-        MyTestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
-            super(context, atm);
+        MyTestActivityManagerService() {
+            super(mTestInjector);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
index 8d54bc2..48bfe1d 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
@@ -16,34 +16,43 @@
 
 package com.android.server.am;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
 import android.content.pm.UserInfo;
-import android.os.Environment;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.test.AndroidTestCase;
-import android.util.Log;
+import android.platform.test.annotations.Presubmit;
 import android.util.SparseBooleanArray;
 
-import com.android.server.am.TaskPersister;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
-import java.io.File;
-import java.util.Random;
+import androidx.test.filters.FlakyTest;
 
 /**
- * atest FrameworksServicesTests:TaskPersisterTest
+ * Tests for {@link TaskPersister}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskPersisterTest
  */
-public class TaskPersisterTest extends AndroidTestCase {
+@Presubmit
+@FlakyTest(detail = "Promote to presubmit if stable")
+public class TaskPersisterTest {
     private static final String TEST_USER_NAME = "AM-Test-User";
 
     private TaskPersister mTaskPersister;
     private int testUserId;
     private UserManager mUserManager;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-        mUserManager = UserManager.get(getContext());
-        mTaskPersister = new TaskPersister(getContext().getFilesDir());
+        mUserManager = UserManager.get(getTargetContext());
+        mTaskPersister = new TaskPersister(getTargetContext().getFilesDir());
         // In ARC, the maximum number of supported users is one, which is different from the ones of
         // most phones (more than 4). This prevents TaskPersisterTest from creating another user for
         // test. However, since guest users can be added as much as possible, we create guest user
@@ -51,9 +60,8 @@
         testUserId = createUser(TEST_USER_NAME, UserInfo.FLAG_GUEST);
     }
 
-    @Override
+    @After
     public void tearDown() throws Exception {
-        super.tearDown();
         mTaskPersister.unloadUserDataFromMemory(testUserId);
         removeUser(testUserId);
     }
@@ -64,6 +72,7 @@
         return taskId;
     }
 
+    @Test
     public void testTaskIdsPersistence() {
         SparseBooleanArray taskIdsOnFile = new SparseBooleanArray();
         for (int i = 0; i < 100; i++) {
@@ -72,21 +81,18 @@
         mTaskPersister.writePersistedTaskIdsForUser(taskIdsOnFile, testUserId);
         SparseBooleanArray newTaskIdsOnFile = mTaskPersister
                 .loadPersistedTaskIdsForUser(testUserId);
-        assertTrue("TaskIds written differ from TaskIds read back from file",
-                taskIdsOnFile.equals(newTaskIdsOnFile));
+        assertEquals("TaskIds written differ from TaskIds read back from file",
+                taskIdsOnFile, newTaskIdsOnFile);
     }
 
     private int createUser(String name, int flags) {
         UserInfo user = mUserManager.createUser(name, flags);
-        if (user == null) {
-            fail("Error while creating the test user: " + TEST_USER_NAME);
-        }
+        assertNotNull("Error while creating the test user: " + TEST_USER_NAME, user);
         return user.id;
     }
 
     private void removeUser(int userId) {
-        if (!mUserManager.removeUser(userId)) {
-            fail("Error while removing the test user: " + TEST_USER_NAME);
-        }
+        boolean userRemoved = mUserManager.removeUser(userId);
+        assertTrue("Error while removing the test user: " + TEST_USER_NAME, userRemoved);
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
index 921cdea..27766d3 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
@@ -69,13 +69,11 @@
 
     private static final String TASK_TAG = "task";
 
-    private ActivityTaskManagerService mService;
-
     @Before
     public void setUp() throws Exception {
         super.setUp();
         TaskRecord.setTaskRecordFactory(null);
-        mService = createActivityTaskManagerService();
+        setupActivityTaskManagerService();
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index cc4f519..75e1d0d 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -16,6 +16,41 @@
 
 package com.android.server.am;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
+
+import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
+import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
+
+import static com.google.android.collect.Lists.newArrayList;
+import static com.google.android.collect.Sets.newHashSet;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.validateMockitoUsage;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
 import android.app.IUserSwitchObserver;
 import android.content.Context;
 import android.content.IIntentReceiver;
@@ -31,80 +66,62 @@
 import android.os.RemoteException;
 import android.os.UserManagerInternal;
 import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
 import com.android.server.pm.UserManagerService;
 import com.android.server.wm.WindowManagerService;
 
-import org.mockito.Mockito;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
-import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
-import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
+import androidx.test.filters.SmallTest;
 
 /**
- * Usage: bit FrameworksServicesTests:com.android.server.am.UserControllerTest
+ * Tests for {@link UserController}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:com.android.server.am.UserControllerTest
  */
 @Presubmit
-public class UserControllerTest extends AndroidTestCase {
+@SmallTest
+public class UserControllerTest {
     private static final int TEST_USER_ID = 10;
     private static final int NONEXIST_USER_ID = 2;
     private static String TAG = UserControllerTest.class.getSimpleName();
     private UserController mUserController;
     private TestInjector mInjector;
 
-    private static final List<String> START_FOREGROUND_USER_ACTIONS =
-            Arrays.asList(
-                    Intent.ACTION_USER_STARTED,
-                    Intent.ACTION_USER_SWITCHED,
-                    Intent.ACTION_USER_STARTING);
+    private static final List<String> START_FOREGROUND_USER_ACTIONS = newArrayList(
+            Intent.ACTION_USER_STARTED,
+            Intent.ACTION_USER_SWITCHED,
+            Intent.ACTION_USER_STARTING);
 
-    private static final List<String> START_BACKGROUND_USER_ACTIONS =
-            Arrays.asList(
-                    Intent.ACTION_USER_STARTED,
-                    Intent.ACTION_LOCKED_BOOT_COMPLETED,
-                    Intent.ACTION_USER_STARTING);
+    private static final List<String> START_BACKGROUND_USER_ACTIONS = newArrayList(
+            Intent.ACTION_USER_STARTED,
+            Intent.ACTION_LOCKED_BOOT_COMPLETED,
+            Intent.ACTION_USER_STARTING);
 
-    private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES =
-            new HashSet<>(Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG,
-                    SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG));
+    private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
+            REPORT_USER_SWITCH_MSG,
+            USER_SWITCH_TIMEOUT_MSG,
+            SYSTEM_USER_START_MSG,
+            SYSTEM_USER_CURRENT_MSG);
 
-    private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES =
-            new HashSet<>(Arrays.asList(SYSTEM_USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG));
+    private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
+            SYSTEM_USER_START_MSG,
+            REPORT_LOCKED_BOOT_COMPLETE_MSG);
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         runWithDexmakerShareClassLoader(() -> {
-            mInjector = Mockito.spy(new TestInjector(getContext()));
+            mInjector = spy(new TestInjector(getTargetContext()));
             doNothing().when(mInjector).clearAllLockedTasks(anyString());
             doNothing().when(mInjector).startHomeActivity(anyInt(), anyString());
             doReturn(false).when(mInjector).stackSupervisorSwitchUser(anyInt(), any());
@@ -114,58 +131,54 @@
         });
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+    @After
+    public void tearDown() throws Exception {
         mInjector.handlerThread.quit();
-        Mockito.validateMockitoUsage();
+        validateMockitoUsage();
     }
 
-    @SmallTest
-    public void testStartUser_foreground() throws RemoteException {
+    @Test
+    public void testStartUser_foreground() {
         mUserController.startUser(TEST_USER_ID, true /* foreground */);
-        Mockito.verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
-        Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
-        Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
-        Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true);
-        Mockito.verify(mInjector).clearAllLockedTasks(anyString());
+        verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
+        verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+        verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
+        verify(mInjector.getWindowManager()).setSwitchingUser(true);
+        verify(mInjector).clearAllLockedTasks(anyString());
         startForegroundUserAssertions();
     }
 
-    @SmallTest
-    public void testStartUser_background() throws RemoteException {
+    @Test
+    public void testStartUser_background() {
         mUserController.startUser(TEST_USER_ID, false /* foreground */);
-        Mockito.verify(
-                mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
-        Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
-        Mockito.verify(mInjector, never()).clearAllLockedTasks(anyString());
+        verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+        verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+        verify(mInjector, never()).clearAllLockedTasks(anyString());
         startBackgroundUserAssertions();
     }
 
-    @SmallTest
-    public void testStartUserUIDisabled() throws RemoteException {
+    @Test
+    public void testStartUserUIDisabled() {
         mUserController.mUserSwitchUiEnabled = false;
         mUserController.startUser(TEST_USER_ID, true /* foreground */);
-        Mockito.verify(mInjector.getWindowManager(), never())
-                .startFreezingScreen(anyInt(), anyInt());
-        Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
-        Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+        verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+        verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+        verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
         startForegroundUserAssertions();
     }
 
     private void startUserAssertions(
-            List<String> expectedActions, Set<Integer> expectedMessageCodes)
-            throws RemoteException {
+            List<String> expectedActions, Set<Integer> expectedMessageCodes) {
         assertEquals(expectedActions, getActions(mInjector.sentIntents));
         Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
         assertEquals("Unexpected message sent", expectedMessageCodes, actualCodes);
     }
 
-    private void startBackgroundUserAssertions() throws RemoteException {
+    private void startBackgroundUserAssertions() {
         startUserAssertions(START_BACKGROUND_USER_ACTIONS, START_BACKGROUND_USER_MESSAGE_CODES);
     }
 
-    private void startForegroundUserAssertions() throws RemoteException {
+    private void startForegroundUserAssertions() {
         startUserAssertions(START_FOREGROUND_USER_ACTIONS, START_FOREGROUND_USER_MESSAGE_CODES);
         Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
@@ -177,15 +190,15 @@
         assertEquals("Unexpected new user id", TEST_USER_ID, reportMsg.arg2);
     }
 
-    @SmallTest
-    public void testFailedStartUserInForeground() throws RemoteException {
+    @Test
+    public void testFailedStartUserInForeground() {
         mUserController.mUserSwitchUiEnabled = false;
         mUserController.startUserInForeground(NONEXIST_USER_ID);
-        Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
-        Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(false);
+        verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
+        verify(mInjector.getWindowManager()).setSwitchingUser(false);
     }
 
-    @SmallTest
+    @Test
     public void testDispatchUserSwitch() throws RemoteException {
         // Prepare mock observer and register it
         IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -206,7 +219,7 @@
         // Call dispatchUserSwitch and verify that observer was called only once
         mInjector.handler.clearAllRecordedMessages();
         mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
-        Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
+        verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
         Set<Integer> expectedCodes = Collections.singleton(CONTINUE_USER_SWITCH_MSG);
         Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
         assertEquals("Unexpected message sent", expectedCodes, actualCodes);
@@ -220,7 +233,7 @@
         assertEquals("Unexpected new user id", TEST_USER_ID, conMsg.arg2);
     }
 
-    @SmallTest
+    @Test
     public void testDispatchUserSwitchBadReceiver() throws RemoteException {
         // Prepare mock observer which doesn't notify the callback and register it
         IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -236,14 +249,14 @@
         // Call dispatchUserSwitch and verify that observer was called only once
         mInjector.handler.clearAllRecordedMessages();
         mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
-        Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
+        verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
         // Verify that CONTINUE_USER_SWITCH_MSG is not sent (triggers timeout)
         Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
-        assertTrue("No messages should be sent", actualCodes.isEmpty());
+        assertWithMessage("No messages should be sent").that(actualCodes).isEmpty();
     }
 
-    @SmallTest
-    public void testContinueUserSwitch() throws RemoteException {
+    @Test
+    public void testContinueUserSwitch() {
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, true);
         Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -254,12 +267,12 @@
         mInjector.handler.clearAllRecordedMessages();
         // Verify that continueUserSwitch worked as expected
         mUserController.continueUserSwitch(userState, oldUserId, newUserId);
-        Mockito.verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
+        verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
         continueUserSwitchAssertions();
     }
 
-    @SmallTest
-    public void testContinueUserSwitchUIDisabled() throws RemoteException {
+    @Test
+    public void testContinueUserSwitchUIDisabled() {
         mUserController.mUserSwitchUiEnabled = false;
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, true);
@@ -271,11 +284,11 @@
         mInjector.handler.clearAllRecordedMessages();
         // Verify that continueUserSwitch worked as expected
         mUserController.continueUserSwitch(userState, oldUserId, newUserId);
-        Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+        verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
         continueUserSwitchAssertions();
     }
 
-    private void continueUserSwitchAssertions() throws RemoteException {
+    private void continueUserSwitchAssertions() {
         Set<Integer> expectedCodes = Collections.singleton(REPORT_USER_SWITCH_COMPLETE_MSG);
         Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
         assertEquals("Unexpected message sent", expectedCodes, actualCodes);
@@ -284,7 +297,7 @@
         assertEquals("Unexpected userId", TEST_USER_ID, msg.arg1);
     }
 
-    @SmallTest
+    @Test
     public void testDispatchUserSwitchComplete() throws RemoteException {
         // Prepare mock observer and register it
         IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -298,12 +311,12 @@
         mInjector.handler.clearAllRecordedMessages();
         // Mockito can't reset only interactions, so just verify that this hasn't been
         // called with 'false' until after dispatchUserSwitchComplete.
-        Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
+        verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
         // Call dispatchUserSwitchComplete
         mUserController.dispatchUserSwitchComplete(newUserId);
-        Mockito.verify(observer, times(1)).onUserSwitchComplete(anyInt());
-        Mockito.verify(observer).onUserSwitchComplete(TEST_USER_ID);
-        Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
+        verify(observer, times(1)).onUserSwitchComplete(anyInt());
+        verify(observer).onUserSwitchComplete(TEST_USER_ID);
+        verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
     }
 
     private void setUpUser(int userId, int flags) {
@@ -320,7 +333,7 @@
     }
 
     // Should be public to allow mocking
-    public static class TestInjector extends UserController.Injector {
+    private static class TestInjector extends UserController.Injector {
         TestHandler handler;
         TestHandler uiHandler;
         HandlerThread handlerThread;
@@ -438,4 +451,4 @@
             return super.sendMessageAtTime(msg, uptimeMillis);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
index ced0847..f8e7403 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -30,6 +30,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.InputChannel;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -43,6 +44,7 @@
  * atest com.android.server.wm.TaskPositioningControllerTests
  */
 @SmallTest
+@FlakyTest(bugId = 117924387)
 @RunWith(AndroidJUnit4.class)
 @Presubmit
 public class TaskPositioningControllerTests extends WindowTestsBase {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 3127b35..d33a537 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -676,7 +676,7 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public static TelecomManager from(Context context) {
         return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
     }
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 1efa906..d9e7167 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.job.JobService;
@@ -2889,100 +2890,131 @@
         public static final String SUBSCRIPTION_ID = "sub_id";
 
         /**
-         * The profile_id to which the APN saved in modem
+         * The profile_id to which the APN saved in modem.
          * <p>Type: INTEGER</p>
          *@hide
          */
         public static final String PROFILE_ID = "profile_id";
 
         /**
-         * Is the apn setting to be set in modem
-         * <P>Type: INTEGER (boolean)</P>
+         * If set to {@code true}, then the APN setting will persist to the modem.
+         * <p>Type: INTEGER (boolean)</p>
          *@hide
          */
+        @SystemApi
         public static final String MODEM_COGNITIVE = "modem_cognitive";
 
         /**
-         * The max connections of this apn
+         * The max connections of this APN.
          * <p>Type: INTEGER</p>
          *@hide
          */
+        @SystemApi
         public static final String MAX_CONNS = "max_conns";
 
         /**
-         * The wait time for retry of the apn
+         * The wait time for retry of the APN.
          * <p>Type: INTEGER</p>
          *@hide
          */
+        @SystemApi
         public static final String WAIT_TIME = "wait_time";
 
         /**
-         * The time to limit max connection for the apn
+         * The time to limit max connection for the APN.
          * <p>Type: INTEGER</p>
          *@hide
          */
+        @SystemApi
         public static final String MAX_CONNS_TIME = "max_conns_time";
 
         /**
-         * The MTU size of the mobile interface to  which the APN connected
+         * The MTU(Maxinum transmit unit) size of the mobile interface to which the APN connected.
          * <p>Type: INTEGER </p>
          * @hide
          */
+        @SystemApi
         public static final String MTU = "mtu";
 
         /**
-         * Is this APN added/edited/deleted by a user or carrier?
+         * APN edit status. APN could be added/edited/deleted by a user or carrier.
          * <p>Type: INTEGER </p>
          * @hide
          */
+        @SystemApi
         public static final String EDITED = "edited";
 
         /**
-         * Is this APN visible to the user?
-         * <p>Type: INTEGER (boolean) </p>
+         * {@code true} if this APN visible to the user, {@code false} otherwise.
+         * <p>Type: INTEGER (boolean)</p>
          * @hide
          */
+        @SystemApi
         public static final String USER_VISIBLE = "user_visible";
 
         /**
-         * Is the user allowed to edit this APN?
-         * <p>Type: INTEGER (boolean) </p>
+         * {@code true} if the user allowed to edit this APN, {@code false} otherwise.
+         * <p>Type: INTEGER (boolean)</p>
          * @hide
          */
+        @SystemApi
         public static final String USER_EDITABLE = "user_editable";
 
         /**
-         * Following are possible values for the EDITED field
+         * {@link #EDITED APN edit status} indicates that this APN has not been edited or fails to
+         * edit.
+         * <p>Type: INTEGER </p>
          * @hide
          */
+        @SystemApi
         public static final int UNEDITED = 0;
+
         /**
-         *  @hide
+         * {@link #EDITED APN edit status} indicates that this APN has been edited by users.
+         * <p>Type: INTEGER </p>
+         * @hide
          */
+        @SystemApi
         public static final int USER_EDITED = 1;
+
         /**
-         *  @hide
+         * {@link #EDITED APN edit status} indicates that this APN has been deleted by users.
+         * <p>Type: INTEGER </p>
+         * @hide
          */
+        @SystemApi
         public static final int USER_DELETED = 2;
+
         /**
-         * DELETED_BUT_PRESENT is an intermediate value used to indicate that an entry deleted
-         * by the user is still present in the new APN database and therefore must remain tagged
-         * as user deleted rather than completely removed from the database
+         * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
+         * deleted by the user is still present in the new APN database and therefore must remain
+         * tagged as user deleted rather than completely removed from the database.
          * @hide
          */
         public static final int USER_DELETED_BUT_PRESENT_IN_XML = 3;
+
         /**
-         *  @hide
+         * {@link #EDITED APN edit status} indicates that this APN has been edited by carriers.
+         * <p>Type: INTEGER </p>
+         * @hide
          */
+        @SystemApi
         public static final int CARRIER_EDITED = 4;
+
         /**
-         * CARRIER_DELETED values are currently not used as there is no usecase. If they are used,
+         * {@link #EDITED APN edit status} indicates that this APN has been deleted by carriers.
+         * CARRIER_DELETED values are currently not used as there is no use case. If they are used,
          * delete() will have to change accordingly. Currently it is hardcoded to USER_DELETED.
+         * <p>Type: INTEGER </p>
          * @hide
          */
         public static final int CARRIER_DELETED = 5;
+
         /**
-         *  @hide
+         * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
+         * deleted by the carrier is still present in the new APN database and therefore must remain
+         * tagged as user deleted rather than completely removed from the database.
+         * @hide
          */
         public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
 
@@ -3011,16 +3043,20 @@
          * The APN set id. When the user manually selects an APN or the framework sets an APN as
          * preferred, all APNs with the same set id as the selected APN should be prioritized over
          * APNs in other sets.
+         * <p>Type: INTEGER</p>
          * @hide
          */
+        @SystemApi
         public static final String APN_SET_ID = "apn_set_id";
 
         /**
-         * Possible value for the APN_SET_ID field. By default APNs will not belong to a set. If the
-         * user manually selects an APN with no set set, there is no need to prioritize any specific
-         * APN set ids.
+         * Possible value for the{@link #APN_SET_ID} field. By default APNs will not belong to a
+         * set. If the user manually selects an APN with no set set, there is no need to prioritize
+         * any specific APN set ids.
+         * <p>Type: INTEGER</p>
          * @hide
          */
+        @SystemApi
         public static final int NO_SET_SET = 0;
 
     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 08b5c2d..b0997f1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1202,6 +1202,20 @@
             "always_show_data_rat_icon_bool";
 
     /**
+     * Boolean indicating if default data account should show LTE or 4G icon
+     * @hide
+     */
+    public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL =
+            "show_4g_for_lte_data_icon_bool";
+
+    /**
+     * Boolean indicating if lte+ icon should be shown if available
+     * @hide
+     */
+    public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL =
+            "hide_lte_plus_data_icon_bool";
+
+    /**
      * Boolean to decide whether to show precise call failed cause to user
      * @hide
      */
@@ -2530,6 +2544,8 @@
         sDefaults.putBoolean(KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL, false);
         sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, true);
+        sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
         sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
         sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
diff --git a/telephony/java/android/telephony/RcsManager.java b/telephony/java/android/telephony/RcsManager.java
new file mode 100644
index 0000000..00ce03a
--- /dev/null
+++ b/telephony/java/android/telephony/RcsManager.java
@@ -0,0 +1,52 @@
+/*
+ * 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.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.IRcs;
+
+/**
+ * RcsManager is the application interface to RcsProvider and provides access methods to
+ * RCS related database tables.
+ * @hide - TODO make this public
+ */
+public class RcsManager {
+    private static final String TAG = "RcsManager";
+    private static final boolean VDBG = false;
+
+    /**
+     * Delete the RcsThread identified by the given threadId.
+     * @param threadId threadId of the thread to be deleted.
+     */
+    public void deleteThread(int threadId) {
+        if (VDBG) logd("deleteThread: threadId: " + threadId);
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                iRcs.deleteThread(threadId);
+            }
+        } catch (RemoteException re) {
+
+        }
+    }
+
+    private static void logd(String msg) {
+        Rlog.d(TAG, msg);
+    }
+}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index bfbcd57..e0ec2c5 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -21,7 +21,6 @@
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Intent;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -247,7 +246,7 @@
     private String mDataOperatorAlphaLong;
     private String mDataOperatorAlphaShort;
     private String mDataOperatorNumeric;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     private boolean mIsManualNetworkSelection;
 
     private boolean mIsEmergencyOnly;
@@ -257,9 +256,9 @@
 
     @UnsupportedAppUsage
     private boolean mCssIndicator;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     private int mNetworkId;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     private int mSystemId;
     @UnsupportedAppUsage
     private int mCdmaRoamingIndicator;
@@ -457,7 +456,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public int getVoiceRegState() {
         return mVoiceRegState;
     }
@@ -472,7 +471,7 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public int getDataRegState() {
         return mDataRegState;
     }
@@ -533,7 +532,7 @@
      * @return roaming status
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public boolean getVoiceRoaming() {
         return getVoiceRoamingType() != ROAMING_TYPE_NOT_ROAMING;
     }
@@ -557,7 +556,7 @@
      * @return roaming type
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public boolean getDataRoaming() {
         return getDataRoamingType() != ROAMING_TYPE_NOT_ROAMING;
     }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d0c6c49..3b40164 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1368,7 +1368,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public static int getPhoneId(int subId) {
         if (!isValidSubscriptionId(subId)) {
             if (DBG) {
@@ -1664,7 +1664,7 @@
      * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID.
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public static boolean isUsableSubIdValue(int subId) {
         return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE;
     }
@@ -1682,7 +1682,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
         int[] subIds = SubscriptionManager.getSubId(phoneId);
         if (subIds != null && subIds.length > 0) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d6a0ae1..69901d2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import static android.content.Context.TELECOM_SERVICE;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.IntDef;
@@ -59,6 +60,7 @@
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.Log;
@@ -230,8 +232,7 @@
 
     /** @hide
     /* @deprecated - use getSystemService as described above */
-    @Deprecated
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public static TelephonyManager getDefault() {
         return sInstance;
     }
@@ -320,7 +321,7 @@
     }
 
     /** {@hide} */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public static TelephonyManager from(Context context) {
         return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
     }
@@ -706,7 +707,7 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     @Deprecated
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @UnsupportedAppUsage
     public static final String ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED =
             "android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED";
 
@@ -1231,6 +1232,38 @@
      */
     public static final String EXTRA_RECOVERY_ACTION = "recoveryAction";
 
+     /**
+     * Broadcast intent action indicating that the telephony provider DB got lost.
+     *
+     * <p>
+     * The {@link #EXTRA_IS_CORRUPTED} extra indicates whether the database is lost
+     * due to corruption or not
+     *
+     * <p class="note">
+     * Requires the MODIFY_PHONE_STATE permission.
+     *
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     *
+     * @see #EXTRA_IS_CORRUPTED
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public static final String ACTION_MMSSMS_DATABASE_LOST =
+            "android.intent.action.MMSSMS_DATABASE_LOST";
+
+    /**
+     * A boolean extra used with {@link #ACTION_MMSSMS_DATABASE_LOST} to indicate
+     * whether the database is lost due to corruption or not.
+     *
+     * @see #ACTION_MMSSMS_DATABASE_LOST
+     *
+     * @hide
+     */
+    public static final String EXTRA_IS_CORRUPTED = "isCorrupted";
+
     //
     //
     // Device Info
@@ -1278,11 +1311,11 @@
      * Returns the unique device ID, for example, the IMEI for GSM and the MEID
      * or ESN for CDMA phones. Return null if device ID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
      * MEID for CDMA.
@@ -1307,11 +1340,11 @@
      * Returns the unique device ID of a subscription, for example, the IMEI for
      * GSM and the MEID for CDMA phones. Return null if device ID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @param slotIndex of which deviceID is returned
      *
@@ -1339,11 +1372,11 @@
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      */
     @SuppressAutoDoc // No support for device / profile owner.
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -1355,11 +1388,11 @@
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @param slotIndex of which IMEI is returned
      */
@@ -1408,11 +1441,11 @@
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      */
     @SuppressAutoDoc // No support for device / profile owner.
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -1423,11 +1456,11 @@
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @param slotIndex of which MEID is returned
      */
@@ -1732,7 +1765,7 @@
     }
 
     /** {@hide} */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @UnsupportedAppUsage
     private int getPhoneTypeFromProperty(int phoneId) {
         String type = getTelephonyProperty(phoneId,
                 TelephonyProperties.CURRENT_ACTIVE_PHONE, null);
@@ -1916,7 +1949,7 @@
      * @param subId
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public String getNetworkOperatorName(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, "");
@@ -1944,7 +1977,7 @@
      * @param subId
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public String getNetworkOperator(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getNetworkOperatorForPhone(phoneId);
@@ -2268,7 +2301,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public int getDataNetworkType(int subId) {
         try{
             ITelephony telephony = getITelephony();
@@ -2304,7 +2337,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public int getVoiceNetworkType(int subId) {
         try{
             ITelephony telephony = getITelephony();
@@ -2787,7 +2820,7 @@
      * @param subId for which SimOperator is returned
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public String getSimOperator(int subId) {
         return getSimOperatorNumeric(subId);
     }
@@ -2801,7 +2834,7 @@
      * @see #getSimState
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public String getSimOperatorNumeric() {
         int subId = mSubId;
         if (!SubscriptionManager.isUsableSubIdValue(subId)) {
@@ -2830,7 +2863,7 @@
      * @param subId for which SimOperator is returned
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public String getSimOperatorNumeric(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getSimOperatorNumericForPhone(phoneId);
@@ -2844,7 +2877,7 @@
      * @param phoneId for which SimOperator is returned
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public String getSimOperatorNumericForPhone(int phoneId) {
         return getTelephonyProperty(phoneId,
                 TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
@@ -2871,7 +2904,7 @@
      * @param subId for which SimOperatorName is returned
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public String getSimOperatorName(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getSimOperatorNameForPhone(phoneId);
@@ -2901,7 +2934,7 @@
      * @param subId for which SimCountryIso is returned
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public String getSimCountryIso(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getSimCountryIsoForPhone(phoneId);
@@ -2923,11 +2956,11 @@
      * unavailable.
      *
      * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
-     * profile owner, or that the calling app has carrier privileges (see {@link
-     * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2940,11 +2973,11 @@
      * unavailable.
      *
      * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
-     * profile owner, or that the calling app has carrier privileges (see {@link
-     * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @param subId for which Sim Serial number is returned
      * @hide
@@ -3085,11 +3118,11 @@
      * Return null if it is unavailable.
      *
      * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
-     * profile owner, or that the calling app has carrier privileges (see {@link
-     * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3103,17 +3136,17 @@
      * Return null if it is unavailable.
      *
      * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
-     * profile owner, or that the calling app has carrier privileges (see {@link
-     * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @param subId whose subscriber id is returned
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public String getSubscriberId(int subId) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
@@ -3498,7 +3531,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public String getMsisdn(int subId) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
@@ -4431,7 +4464,7 @@
    /**
     * @hide
     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     private ITelephony getITelephony() {
         return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
     }
@@ -7404,7 +7437,9 @@
     @UnsupportedAppUsage
     public boolean isVolteAvailable() {
         try {
-            return getITelephony().isVolteAvailable(getSubId());
+            return getITelephony().isAvailable(getSubId(),
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE, getOpPackageName());
         } catch (RemoteException | NullPointerException ex) {
             return false;
         }
@@ -8009,7 +8044,7 @@
      * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     public ServiceState getServiceStateForSubscriber(int subId) {
         try {
             ITelephony service = getITelephony();
@@ -8343,20 +8378,31 @@
     }
 
     /**
-     * Action set from carrier signalling broadcast receivers to enable/disable metered apns
-     * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
-     * @param subId the subscription ID that this action applies to.
-     * @param enabled control enable or disable metered apns.
+     * Used to enable or disable carrier data by the system based on carrier signalling or
+     * carrier privileged apps. Different from {@link #setDataEnabled(boolean)} which is linked to
+     * user settings, carrier data on/off won't affect user settings but will bypass the
+     * settings and turns off data internally if set to {@code false}.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param enabled control enable or disable carrier data.
      * @hide
      */
-    public void carrierActionSetMeteredApnsEnabled(int subId, boolean enabled) {
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setCarrierDataEnabled(boolean enabled) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                service.carrierActionSetMeteredApnsEnabled(subId, enabled);
+                service.carrierActionSetMeteredApnsEnabled(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enabled);
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#carrierActionSetMeteredApnsEnabled", e);
+            Log.e(TAG, "Error calling ITelephony#setCarrierDataEnabled", e);
         }
     }
 
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
new file mode 100644
index 0000000..c9cf473
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -0,0 +1,760 @@
+/*
+ * 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.ims;
+
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated
+ * subscription.
+ *
+ * Allows a user to query the IMS MmTel feature information for a subscription, register for
+ * registration and MmTel capability status callbacks, as well as query/modify user settings for the
+ * associated subscription.
+ *
+ * @see #createForSubscriptionId(Context, int)
+ * @hide
+ */
+public class ImsMmTelManager {
+
+    private static final String TAG = "ImsMmTelManager";
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "WIFI_MODE_", value = {
+            WIFI_MODE_WIFI_ONLY,
+            WIFI_MODE_CELLULAR_PREFERRED,
+            WIFI_MODE_WIFI_PREFERRED
+            })
+    public @interface WiFiCallingMode {}
+
+    /**
+     * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
+     * registration if signal quality degrades.
+     * @hide
+     */
+    @SystemApi
+    public static final int WIFI_MODE_WIFI_ONLY = 0;
+
+    /**
+     * Prefer registering for IMS over LTE if LTE signal quality is high enough.
+     * @hide
+     */
+    @SystemApi
+    public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
+
+    /**
+     * Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough.
+     * @hide
+     */
+    @SystemApi
+    public static final int WIFI_MODE_WIFI_PREFERRED = 2;
+
+    /**
+     * Callback class for receiving Registration callback events.
+     * @see #addImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
+     * @see #removeImsRegistrationCallback(RegistrationCallback)
+     */
+    public static class RegistrationCallback {
+
+        private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
+
+            private final RegistrationCallback mLocalCallback;
+            private Executor mExecutor;
+
+            RegistrationBinder(RegistrationCallback localCallback) {
+                mLocalCallback = localCallback;
+            }
+
+            @Override
+            public void onRegistered(int imsRadioTech) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() -> mLocalCallback.onRegistered(imsRadioTech)));
+            }
+
+            @Override
+            public void onRegistering(int imsRadioTech) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() -> mLocalCallback.onRegistering(imsRadioTech)));
+            }
+
+            @Override
+            public void onDeregistered(ImsReasonInfo info) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() -> mLocalCallback.onDeregistered(info)));
+            }
+
+            @Override
+            public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() ->
+                                mLocalCallback.onTechnologyChangeFailed(imsRadioTech, info)));
+            }
+
+            @Override
+            public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() ->
+                                mLocalCallback.onSubscriberAssociatedUriChanged(uris)));
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final RegistrationBinder mBinder = new RegistrationBinder(this);
+
+        /**
+         * Notifies the framework when the IMS Provider is registered to the IMS network.
+         *
+         * @param imsRadioTech the radio access technology. Valid values are defined in
+         * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+         */
+        public void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is trying to register the IMS network.
+         *
+         * @param imsRadioTech the radio access technology. Valid values are defined in
+         * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+         */
+        public void onRegistering(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+         *
+         * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+         */
+        public void onDeregistered(ImsReasonInfo info) {
+        }
+
+        /**
+         * A failure has occurred when trying to handover registration to another technology type,
+         * defined in {@link ImsRegistrationImplBase.ImsRegistrationTech}
+         *
+         * @param imsRadioTech The {@link ImsRegistrationImplBase.ImsRegistrationTech} type that has
+         *         failed
+         * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+         */
+        public void onTechnologyChangeFailed(
+                @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, ImsReasonInfo info) {
+        }
+
+        /**
+         * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+         * it changes. Per RFC3455, an associated URI is a URI that the service provider has
+         * allocated to a user for their own usage. A user's phone number is typically one of the
+         * associated URIs.
+         * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+         *         subscription.
+         * @hide
+         */
+        public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+        }
+
+        /**@hide*/
+        public final IImsRegistrationCallback getBinder() {
+            return mBinder;
+        }
+
+        /**@hide*/
+        //Only exposed as public for compatibility with deprecated ImsManager APIs.
+        public void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    /**
+     * Receives IMS capability status updates from the ImsService.
+     *
+     * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
+     * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+     */
+    public static class CapabilityCallback {
+
+        private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+
+            private final CapabilityCallback mLocalCallback;
+            private Executor mExecutor;
+
+            CapabilityBinder(CapabilityCallback c) {
+                mLocalCallback = c;
+            }
+
+            @Override
+            public void onCapabilitiesStatusChanged(int config) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged(
+                                new MmTelFeature.MmTelCapabilities(config))));
+            }
+
+            @Override
+            public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                    boolean isEnabled) {
+                // This is not used for public interfaces.
+            }
+
+            @Override
+            public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                    @ImsFeature.ImsCapabilityError int reason) {
+                // This is not used for public interfaces
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final CapabilityBinder mBinder = new CapabilityBinder(this);
+
+        /**
+         * The status of the feature's capabilities has changed to either available or unavailable.
+         * If unavailable, the feature is not able to support the unavailable capability at this
+         * time.
+         *
+         * @param capabilities The new availability of the capabilities.
+         */
+        public void onCapabilitiesStatusChanged(
+                MmTelFeature.MmTelCapabilities capabilities) {
+        }
+
+        /**@hide*/
+        public final IImsCapabilityCallback getBinder() {
+            return mBinder;
+        }
+
+        /**@hide*/
+        // Only exposed as public method for compatibility with deprecated ImsManager APIs.
+        // TODO: clean up dependencies and change back to private visibility.
+        public void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    private Context mContext;
+    private int mSubId;
+
+    /**
+     * Create an instance of ImsManager for the subscription id specified.
+     *
+     * @param context
+     * @param subId The ID of the subscription that this ImsManager will use.
+     * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+     * @throws IllegalArgumentException if the subscription is invalid or
+     *         the subscription ID is not an active subscription.
+     */
+    public static ImsMmTelManager createForSubscriptionId(Context context, int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)
+                || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+
+        return new ImsMmTelManager(context, subId);
+    }
+
+    private ImsMmTelManager(Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+    }
+
+    /**
+     * Registers a {@link RegistrationCallback} with the system, which will provide registration
+     * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use
+     * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+     * events and call {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up
+     * after a subscription is removed.
+     * @param executor The executor the callback events should be run on.
+     * @param c The {@link RegistrationCallback} to be added.
+     * @see #removeImsRegistrationCallback(RegistrationCallback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public void addImsRegistrationCallback(@CallbackExecutor Executor executor,
+            @NonNull RegistrationCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        c.setExecutor(executor);
+        try {
+            getITelephony().addImsRegistrationCallback(mSubId, c.getBinder(),
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Removes an existing {@link RegistrationCallback}. Ensure to call this method when cleaning
+     * up to avoid memory leaks or when the subscription is removed.
+     * @param c The {@link RegistrationCallback} to be removed.
+     * @see SubscriptionManager.OnSubscriptionsChangedListener
+     * @see #addImsRegistrationCallback(Executor, RegistrationCallback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public void removeImsRegistrationCallback(@NonNull RegistrationCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        try {
+            getITelephony().removeImsRegistrationCallback(mSubId, c.getBinder(),
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Registers a {@link CapabilityCallback} with the system, which will provide MmTel capability
+     * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}.
+     * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+     * subscription changed events and call
+     * {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up after a subscription
+     * is removed.
+     * @param executor The executor the callback events should be run on.
+     * @param c The MmTel {@link CapabilityCallback} to be registered.
+     * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public void addMmTelCapabilityCallback(@CallbackExecutor Executor executor,
+            @NonNull CapabilityCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        c.setExecutor(executor);
+        try {
+            getITelephony().addMmTelCapabilityCallback(mSubId, c.getBinder(),
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Removes an existing MmTel {@link CapabilityCallback}. Be sure to call this when cleaning
+     * up to avoid memory leaks.
+     * @param c The MmTel {@link CapabilityCallback} to be removed.
+     * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public void removeMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        try {
+            getITelephony().removeMmTelCapabilityCallback(mSubId, c.getBinder(),
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query the user's setting for whether or not to use MmTel capabilities over IMS,
+     * such as voice and video, depending on carrier configuration for the current subscription.
+     * @see #setAdvancedCallingSetting(boolean)
+     * @return true if the user’s setting for advanced calling is enabled and false otherwise.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isAdvancedCallingSettingEnabled() {
+        try {
+            return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+     * enable MmTel IMS features, such as voice and video calling, depending on the carrier
+     * configuration for the current subscription. Modifying this value may also trigger an IMS
+     * registration or deregistration, depending on the new value.
+     * @see #isAdvancedCallingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setAdvancedCallingSetting(boolean isEnabled) {
+        try {
+            getITelephony().setAdvancedCallingSetting(mSubId, isEnabled);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query the IMS MmTel capability for a given registration technology. This does not
+     * necessarily mean that we are registered and the capability is available, but rather the
+     * subscription is capable of this service over IMS.
+     *
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
+     * @see #isAvailable(int, int)
+     *
+     * @param imsRegTech The IMS registration technology, can be one of the following:
+     *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+     *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+     * @param capability The IMS MmTel capability to query, can be one of the following:
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+     * @return {@code true} if the MmTel IMS capability is capable for this subscription, false
+     *         otherwise.
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+        try {
+            return getITelephony().isCapable(mSubId, capability, imsRegTech,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query the availability of an IMS MmTel capability for a given registration technology. If
+     * a capability is available, IMS is registered and the service is currently available over IMS.
+     *
+     * @see #isCapable(int, int)
+     *
+     * @param imsRegTech The IMS registration technology, can be one of the following:
+     *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+     *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+     * @param capability The IMS MmTel capability to query, can be one of the following:
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+     * @return {@code true} if the MmTel IMS capability is available for this subscription, false
+     *         otherwise.
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+        try {
+            return getITelephony().isAvailable(mSubId, capability, imsRegTech,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * The user's setting for whether or not they have enabled the "Video Calling" setting.
+     * @return true if the user’s “Video Calling” setting is currently enabled.
+     * @see #setVtSetting(boolean)
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public boolean isVtSettingEnabled() {
+        try {
+            return getITelephony().isVtSettingEnabled(mSubId, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Change the user's setting for Video Telephony and enable the Video Telephony capability.
+     * @see #isVtSettingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVtSetting(boolean isEnabled) {
+        try {
+            getITelephony().setVtSetting(mSubId, isEnabled);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
+     * @see #setVoWiFiSetting(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isVoWiFiSettingEnabled() {
+        try {
+            return getITelephony().isVoWiFiSettingEnabled(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Sets the user's setting for whether or not Voice over WiFi is enabled.
+     * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
+     * @see #isVoWiFiSettingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiSetting(boolean isEnabled) {
+        try {
+            getITelephony().setVoWiFiSetting(mSubId, isEnabled);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
+     * if disabled.
+     * @see #setVoWiFiRoamingSetting(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isVoWiFiRoamingSettingEnabled() {
+        try {
+            return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Change the user's setting for Voice over WiFi while roaming.
+     * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
+     *     false otherwise.
+     * @see #isVoWiFiRoamingSettingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiRoamingSetting(boolean isEnabled) {
+        try {
+            getITelephony().setVoWiFiRoamingSetting(mSubId, isEnabled);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Overrides the Voice over WiFi capability to true for IMS, but do not persist the setting.
+     * Typically used during the Voice over WiFi registration process for some carriers.
+     *
+     * @param isCapable true if the IMS stack should try to register for IMS over IWLAN, false
+     *     otherwise.
+     * @param mode the Voice over WiFi mode preference to set, which can be one of the following:
+     * - {@link #WIFI_MODE_WIFI_ONLY}
+     * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @see #setVoWiFiSetting(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
+        try {
+            getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * @return The Voice over WiFi Mode preference set by the user, which can be one of the
+     * following:
+     * - {@link #WIFI_MODE_WIFI_ONLY}
+     * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @see #setVoWiFiSetting(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @WiFiCallingMode int getVoWiFiModeSetting() {
+        try {
+            return getITelephony().getVoWiFiModeSetting(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the user's preference for Voice over WiFi calling mode.
+     * @param mode The user's preference for the technology to register for IMS over, can be one of
+     *    the following:
+     * - {@link #WIFI_MODE_WIFI_ONLY}
+     * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @see #getVoWiFiModeSetting()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
+        try {
+            getITelephony().setVoWiFiModeSetting(mSubId, mode);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the user's preference for Voice over WiFi calling mode while the device is roaming on
+     * another network.
+     *
+     * @return The user's preference for the technology to register for IMS over when roaming on
+     *     another network, can be one of the following:
+     *     - {@link #WIFI_MODE_WIFI_ONLY}
+     *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     *     - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @see #setVoWiFiRoamingSetting(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+        try {
+            return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the user's preference for Voice over WiFi mode while the device is roaming on another
+     * network.
+     *
+     * @param mode The user's preference for the technology to register for IMS over when roaming on
+     *     another network, can be one of the following:
+     *     - {@link #WIFI_MODE_WIFI_ONLY}
+     *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     *     - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @see #getVoWiFiRoamingModeSetting()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
+        try {
+            getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Change the user's setting for RTT capability of this device.
+     * @param isEnabled if true RTT will be enabled during calls.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setRttCapabilitySetting(boolean isEnabled) {
+        try {
+            getITelephony().setRttCapabilitySetting(mSubId, isEnabled);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * @return true if TTY over VoLTE is supported
+     * @see android.telecom.TelecomManager#getCurrentTtyMode
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    boolean isTtyOverVolteEnabled() {
+        try {
+            return getITelephony().isTtyOverVolteEnabled(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    private static SubscriptionManager getSubscriptionManager(Context context) {
+        SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
+        if (manager == null) {
+            throw new RuntimeException("Could not find SubscriptionManager.");
+        }
+        return manager;
+    }
+
+    private static ITelephony getITelephony() {
+        ITelephony binder = ITelephony.Stub.asInterface(
+                ServiceManager.getService(Context.TELEPHONY_SERVICE));
+        if (binder == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+        return binder;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
index 4f37caa..749b191 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
@@ -23,7 +23,7 @@
 import android.telephony.ims.ImsReasonInfo;
 
 /**
- * See ImsRegistrationImplBase.Callback for more information.
+ * See {@link ImsManager#RegistrationCallback} for more information.
  *
  * {@hide}
  */
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index b77881e..7f69f43 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -167,59 +167,6 @@
      */
     public static final int CAPABILITY_SUCCESS = 0;
 
-
-    /**
-     * The framework implements this callback in order to register for Feature Capability status
-     * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
-     * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
-     * callbacks when the ImsService can not change the capability as requested, via
-     * {@link #onChangeCapabilityConfigurationError}.
-     *
-     * @hide
-     */
-    public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
-
-        @Override
-        public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
-            onCapabilitiesStatusChanged(new Capabilities(config));
-        }
-
-        /**
-         * Returns the result of a query for the capability configuration of a requested capability.
-         *
-         * @param capability The capability that was requested.
-         * @param radioTech The IMS radio technology associated with the capability.
-         * @param isEnabled true if the capability is enabled, false otherwise.
-         */
-        @Override
-        public void onQueryCapabilityConfiguration(int capability, int radioTech,
-                boolean isEnabled) {
-
-        }
-
-        /**
-         * Called when a change to the capability configuration has returned an error.
-         *
-         * @param capability The capability that was requested to be changed.
-         * @param radioTech The IMS radio technology associated with the capability.
-         * @param reason error associated with the failure to change configuration.
-         */
-        @Override
-        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
-                @ImsCapabilityError int reason) {
-        }
-
-        /**
-         * The status of the feature's capabilities has changed to either available or unavailable.
-         * If unavailable, the feature is not able to support the unavailable capability at this
-         * time.
-         *
-         * @param config The new availability of the capabilities.
-         */
-        public void onCapabilitiesStatusChanged(Capabilities config) {
-        }
-    }
-
     /**
      * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
      * provided.
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 7681aef..96995943 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -17,6 +17,8 @@
 package android.telephony.ims.feature;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Message;
@@ -222,21 +224,31 @@
      * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
      * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
      * status can also be queried using {@link #queryCapabilityStatus()}.
+     * @see #isCapable(int)
      */
     public static class MmTelCapabilities extends Capabilities {
 
         /**
-         * @hide
+         * Create a new empty {@link MmTelCapabilities} instance.
+         * @see #addCapabilities(int)
+         * @see #removeCapabilities(int)
          */
         @VisibleForTesting
         public MmTelCapabilities() {
             super();
         }
 
+        /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.*/
+        @Deprecated
         public MmTelCapabilities(Capabilities c) {
             mCapabilities = c.mCapabilities;
         }
 
+        /**
+         * Create a new {link @MmTelCapabilities} instance with the provided capabilities.
+         * @param capabilities The capabilities that are supported for MmTel in the form of a
+         *                     bitfield.
+         */
         public MmTelCapabilities(int capabilities) {
             mCapabilities = capabilities;
         }
@@ -406,7 +418,10 @@
      * support the capability that is enabled. A capability that is disabled by the framework (via
      * {@link #changeEnabledCapabilities}) should also show the status as disabled.
      */
-    public final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
+    public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
+        if (c == null) {
+            throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
+        }
         super.notifyCapabilitiesStatusChanged(c);
     }
 
@@ -414,7 +429,12 @@
      * Notify the framework of an incoming call.
      * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
      */
-    public final void notifyIncomingCall(ImsCallSessionImplBase c, Bundle extras) {
+    public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
+            @NonNull Bundle extras) {
+        if (c == null || extras == null) {
+            throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
+                    + "null.");
+        }
         synchronized (mLock) {
             if (mListener == null) {
                 throw new IllegalStateException("Session is not available.");
@@ -434,7 +454,12 @@
      *        This can be null if no call information is available for the rejected call.
      * @param reason The {@link ImsReasonInfo} call rejection reason.
      */
-    public final void notifyRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
+    public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
+            @NonNull ImsReasonInfo reason) {
+        if (callProfile == null || reason == null) {
+            throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
+                    + "null.");
+        }
         synchronized (mLock) {
             if (mListener == null) {
                 throw new IllegalStateException("Session is not available.");
@@ -508,8 +533,8 @@
      * the framework.
      */
     @Override
-    public void changeEnabledCapabilities(CapabilityChangeRequest request,
-            CapabilityCallbackProxy c) {
+    public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
+            @NonNull CapabilityCallbackProxy c) {
         // Base implementation, no-op
     }
 
@@ -531,7 +556,7 @@
      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
      * @return a {@link ImsCallProfile} object
      */
-    public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+    public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
         // Base Implementation - Should be overridden
         return null;
     }
@@ -552,7 +577,7 @@
      *
      * @param profile a call profile to make the call
      */
-    public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
+    public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
         // Base Implementation - Should be overridden
         return null;
     }
@@ -569,7 +594,7 @@
      * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
      *        call will be placed over IMS or via CSFB.
      */
-    public @ProcessCallResult int shouldProcessCall(String[] numbers) {
+    public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
         return PROCESS_CALL_IMS;
     }
 
@@ -602,7 +627,7 @@
      * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
      * configuration.
      */
-    public ImsUtImplBase getUt() {
+    public @NonNull ImsUtImplBase getUt() {
         // Base Implementation - Should be overridden
         return new ImsUtImplBase();
     }
@@ -611,7 +636,7 @@
      * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
      * calls that support it.
      */
-    public ImsEcbmImplBase getEcbm() {
+    public @NonNull ImsEcbmImplBase getEcbm() {
         // Base Implementation - Should be overridden
         return new ImsEcbmImplBase();
     }
@@ -620,7 +645,7 @@
      * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
      * package processing for multi-endpoint.
      */
-    public ImsMultiEndpointImplBase getMultiEndpoint() {
+    public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
         // Base Implementation - Should be overridden
         return new ImsMultiEndpointImplBase();
     }
@@ -646,7 +671,7 @@
      *     }
      * }
      */
-    public void setUiTtyMode(int mode, Message onCompleteMessage) {
+    public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
         // Base Implementation - Should be overridden
     }
 
@@ -680,7 +705,7 @@
      * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
      * Provider.
      */
-    public ImsSmsImplBase getSmsImplementation() {
+    public @NonNull ImsSmsImplBase getSmsImplementation() {
         return new ImsSmsImplBase();
     }
 
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index cecf2e2..a08e031 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -76,64 +76,6 @@
     private static final int REGISTRATION_STATE_REGISTERING = 1;
     private static final int REGISTRATION_STATE_REGISTERED = 2;
 
-    /**
-     * Callback class for receiving Registration callback events.
-     * @hide
-     */
-    public static class Callback extends IImsRegistrationCallback.Stub {
-        /**
-         * Notifies the framework when the IMS Provider is connected to the IMS network.
-         *
-         * @param imsRadioTech the radio access technology. Valid values are defined in
-         * {@link ImsRegistrationTech}.
-         */
-        @Override
-        public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
-        }
-
-        /**
-         * Notifies the framework when the IMS Provider is trying to connect the IMS network.
-         *
-         * @param imsRadioTech the radio access technology. Valid values are defined in
-         * {@link ImsRegistrationTech}.
-         */
-        @Override
-        public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
-        }
-
-        /**
-         * Notifies the framework when the IMS Provider is disconnected from the IMS network.
-         *
-         * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
-         */
-        @Override
-        public void onDeregistered(ImsReasonInfo info) {
-        }
-
-        /**
-         * A failure has occurred when trying to handover registration to another technology type,
-         * defined in {@link ImsRegistrationTech}
-         *
-         * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
-         * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
-         */
-        @Override
-        public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
-                ImsReasonInfo info) {
-        }
-
-        /**
-         * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
-         * it changes.
-         * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
-         *         subscription.
-         */
-        @Override
-        public void onSubscriberAssociatedUriChanged(Uri[] uris) {
-
-        }
-    }
-
     private final IImsRegistration mBinder = new IImsRegistration.Stub() {
 
         @Override
diff --git a/telephony/java/com/android/internal/telephony/IRcs.aidl b/telephony/java/com/android/internal/telephony/IRcs.aidl
new file mode 100644
index 0000000..ede8695
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IRcs.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.internal.telephony;
+
+interface IRcs {
+    void deleteThread(int threadId);
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 006b040..dc23358 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -38,10 +38,12 @@
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyHistogram;
 import android.telephony.VisualVoicemailSmsFilterSettings;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.OperatorInfo;
@@ -1057,11 +1059,6 @@
      */
     boolean isWifiCallingAvailable(int subId);
 
-    /**
-     * Returns the Status of VoLTE for the subscription ID specified.
-     */
-    boolean isVolteAvailable(int subId);
-
      /**
      * Returns the Status of VT (video telephony) for the subscription ID specified.
      */
@@ -1505,4 +1502,117 @@
      *
      */
     int getRadioPowerState(int slotIndex, String callingPackage);
+
+    // IMS specific AIDL commands, see ImsMmTelManager.java
+
+    /**
+     * Adds an IMS registration status callback for the subscription id specified.
+     */
+    oneway void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+            String callingPackage);
+     /**
+      * Removes an existing IMS registration status callback for the subscription specified.
+      */
+    oneway void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+            String callingPackage);
+
+    /**
+     * Adds an IMS MmTel capabilities callback for the subscription specified.
+     */
+    oneway void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+            String callingPackage);
+
+    /**
+     * Removes an existing IMS MmTel capabilities callback for the subscription specified.
+     */
+    oneway void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+            String callingPackage);
+
+    /**
+     * return true if the IMS MmTel capability for the given registration tech is capable.
+     */
+    boolean isCapable(int subId, int capability, int regTech, String callingPackage);
+
+    /**
+     * return true if the IMS MmTel capability for the given registration tech is available.
+     */
+    boolean isAvailable(int subId, int capability, int regTech, String callingPackage);
+
+    /**
+     * Returns true if the user's setting for 4G LTE is enabled, for the subscription specified.
+     */
+    boolean isAdvancedCallingSettingEnabled(int subId);
+
+    /**
+     * Modify the user's setting for whether or not 4G LTE is enabled.
+     */
+    void setAdvancedCallingSetting(int subId, boolean isEnabled);
+
+    /**
+     * return true if the user's setting for VT is enabled for the subscription.
+     */
+    boolean isVtSettingEnabled(int subId, String callingPackage);
+
+    /**
+     * Modify the user's setting for whether or not VT is available for the subscrption specified.
+     */
+    void setVtSetting(int subId, boolean isEnabled);
+
+    /**
+     * return true if the user's setting for whether or not Voice over WiFi is currently enabled.
+     */
+    boolean isVoWiFiSettingEnabled(int subId);
+
+    /**
+     * sets the user's setting for Voice over WiFi enabled state.
+     */
+    void setVoWiFiSetting(int subId, boolean isEnabled);
+
+    /**
+     * return true if the user's setting for Voice over WiFi while roaming is enabled.
+     */
+    boolean isVoWiFiRoamingSettingEnabled(int subId);
+
+    /**
+     * Sets the user's preference for whether or not Voice over WiFi is enabled for the current
+     * subscription while roaming.
+     */
+    void setVoWiFiRoamingSetting(int subId, boolean isEnabled);
+
+    /**
+     * Set the Voice over WiFi enabled state, but do not persist the setting.
+     */
+    void setVoWiFiNonPersistent(int subId, boolean isCapable, int mode);
+
+    /**
+     * return the Voice over WiFi mode preference set by the user for the subscription specified.
+     */
+    int getVoWiFiModeSetting(int subId);
+
+    /**
+     * sets the user's preference for the Voice over WiFi mode for the subscription specified.
+     */
+    void setVoWiFiModeSetting(int subId, int mode);
+
+    /**
+     * return the Voice over WiFi mode preference set by the user for the subscription specified
+     * while roaming.
+     */
+    int getVoWiFiRoamingModeSetting(int subId);
+
+    /**
+     * sets the user's preference for the Voice over WiFi mode for the subscription specified
+     * while roaming.
+     */
+    void setVoWiFiRoamingModeSetting(int subId, int mode);
+
+    /**
+     * Modify the user's setting for whether or not RTT is enabled for the subscrption specified.
+     */
+    void setRttCapabilitySetting(int subId, boolean isEnabled);
+
+    /**
+     * return true if TTY over VoLTE is enabled for the subscription specified.
+     */
+    boolean isTtyOverVolteEnabled(int subId);
 }
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 976848c..eed8ae7 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -92,7 +92,9 @@
             "com.google.android.wearable.action.GOOGLE";
     private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle
     private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches
-    private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps
+    private static final int BEFORE_FORCE_STOP_SLEEP_TIMEOUT = 1000; // 1s before force stopping
+    private static final int BEFORE_KILL_APP_SLEEP_TIMEOUT = 1000; // 1s before killing
+    private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 3000; // 3s between launching apps
     private static final int PROFILE_SAVE_SLEEP_TIMEOUT = 1000; // Allow 1s for the profile to save
     private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
     private static final String LAUNCH_FILE = "applaunch.txt";
@@ -327,7 +329,14 @@
                     }
                 }
                 if(mForceStopApp) {
-                    closeApp(launch.getApp());
+                    sleep(BEFORE_FORCE_STOP_SLEEP_TIMEOUT);
+                    forceStopApp(launch.getApp());
+                    sleep(BEFORE_KILL_APP_SLEEP_TIMEOUT);
+                    // Close again for good measure (just in case).
+                    forceStopApp(launch.getApp());
+                    // Kill the backgrounded process in the case forceStopApp only sent it to
+                    // background.
+                    killBackgroundApp(launch.getApp());
                 } else {
                     startHomeIntent();
                 }
@@ -638,7 +647,7 @@
         // Kill all the apps
         for (String appName : mNameToIntent.keySet()) {
             Log.w(TAG, String.format("killing %s", appName));
-            closeApp(appName);
+            forceStopApp(appName);
         }
         // Drop all the cache.
         assertNotNull("Issue in dropping the cache",
@@ -646,7 +655,7 @@
                         .executeShellCommand(DROP_CACHE_SCRIPT));
     }
 
-    private void closeApp(String appName) {
+    private void forceStopApp(String appName) {
         Intent startIntent = mNameToIntent.get(appName);
         if (startIntent != null) {
             String packageName = startIntent.getComponent().getPackageName();
@@ -658,6 +667,18 @@
         }
     }
 
+    private void killBackgroundApp(String appName) {
+        Intent startIntent = mNameToIntent.get(appName);
+        if (startIntent != null) {
+            String packageName = startIntent.getComponent().getPackageName();
+            try {
+                mAm.killBackgroundProcesses(packageName, UserHandle.USER_CURRENT);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error closing app", e);
+            }
+        }
+    }
+
     private void sleep(int time) {
         try {
             Thread.sleep(time);
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 03a617c..6174c6c 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -219,7 +219,7 @@
         // callback triggers
         captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
         verify(callback, timeout(500).times(1)).onAvailable(any(Network.class),
-                any(NetworkCapabilities.class), any(LinkProperties.class));
+                any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
 
         // unregister callback
         manager.unregisterNetworkCallback(callback);
@@ -247,7 +247,7 @@
         // callback triggers
         captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
         verify(callback, timeout(100).times(1)).onAvailable(any(Network.class),
-                any(NetworkCapabilities.class), any(LinkProperties.class));
+                any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
 
         // unregister callback
         manager.unregisterNetworkCallback(callback);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1c77fcc..17bcea0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -51,6 +51,10 @@
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
 import static com.android.internal.util.TestUtils.waitForIdleHandler;
 import static com.android.internal.util.TestUtils.waitForIdleLooper;
@@ -92,6 +96,7 @@
 import android.net.ConnectivityManager.PacketKeepaliveCallback;
 import android.net.ConnectivityManager.TooManyRequestsException;
 import android.net.ConnectivityThread;
+import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
@@ -148,6 +153,7 @@
 import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.Tethering;
 import com.android.server.connectivity.Vpn;
 import com.android.server.net.NetworkPinner;
 import com.android.server.net.NetworkPolicyManagerInternal;
@@ -215,11 +221,13 @@
     private MockNetworkAgent mEthernetNetworkAgent;
     private MockVpn mMockVpn;
     private Context mContext;
+    private INetworkPolicyListener mPolicyListener;
 
     @Mock IpConnectivityMetrics.Logger mMetricsService;
     @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
     @Mock INetworkManagementService mNetworkManagementService;
     @Mock INetworkStatsService mStatsService;
+    @Mock INetworkPolicyManager mNpm;
 
     private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
 
@@ -934,6 +942,11 @@
         }
 
         @Override
+        protected Tethering makeTethering() {
+            return mock(Tethering.class);
+        }
+
+        @Override
         protected int reserveNetId() {
             while (true) {
                 final int netId = super.reserveNetId();
@@ -1023,6 +1036,20 @@
         public void waitForIdle() {
             waitForIdle(TIMEOUT_MS);
         }
+
+        public void setUidRulesChanged(int uidRules) {
+            try {
+                mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
+            } catch (RemoteException ignored) {
+            }
+        }
+
+        public void setRestrictBackgroundChanged(boolean restrictBackground) {
+            try {
+                mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+            } catch (RemoteException ignored) {
+            }
+        }
     }
 
     /**
@@ -1055,12 +1082,18 @@
         LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
         LocalServices.addService(
                 NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
+
         mService = new WrappedConnectivityService(mServiceContext,
                 mNetworkManagementService,
                 mStatsService,
-                mock(INetworkPolicyManager.class),
+                mNpm,
                 mock(IpConnectivityLog.class));
 
+        final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
+                ArgumentCaptor.forClass(INetworkPolicyListener.class);
+        verify(mNpm).registerListener(policyListenerCaptor.capture());
+        mPolicyListener = policyListenerCaptor.getValue();
+
         // Create local CM before sending system ready so that we can answer
         // getSystemService() correctly.
         mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
@@ -1441,7 +1474,8 @@
         RESUMED,
         LOSING,
         LOST,
-        UNAVAILABLE
+        UNAVAILABLE,
+        BLOCKED_STATUS
     }
 
     private static class CallbackInfo {
@@ -1522,6 +1556,11 @@
             setLastCallback(CallbackState.LOST, network, null);
         }
 
+        @Override
+        public void onBlockedStatusChanged(Network network, boolean blocked) {
+            setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
+        }
+
         public Network getLastAvailableNetwork() {
             return mLastAvailableNetwork;
         }
@@ -1582,6 +1621,7 @@
         // - onSuspended, iff the network was suspended when the callbacks fire.
         // - onCapabilitiesChanged.
         // - onLinkPropertiesChanged.
+        // - onBlockedStatusChanged.
         //
         // @param agent the network to expect the callbacks on.
         // @param expectSuspended whether to expect a SUSPENDED callback.
@@ -1589,7 +1629,7 @@
         //        onCapabilitiesChanged callback.
         // @param timeoutMs how long to wait for the callbacks.
         void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
-                boolean expectValidated, int timeoutMs) {
+                boolean expectValidated, boolean expectBlocked, int timeoutMs) {
             expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
             if (expectSuspended) {
                 expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
@@ -1600,19 +1640,28 @@
                 expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
             }
             expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
+            expectBlockedStatusCallback(expectBlocked, agent);
         }
 
         // Expects the available callbacks (validated), plus onSuspended.
         void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
-            expectAvailableCallbacks(agent, true, expectValidated, TEST_CALLBACK_TIMEOUT_MS);
+            expectAvailableCallbacks(agent, true, expectValidated, false, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
-            expectAvailableCallbacks(agent, false, true, TEST_CALLBACK_TIMEOUT_MS);
+            expectAvailableCallbacks(agent, false, true, false, TEST_CALLBACK_TIMEOUT_MS);
+        }
+
+        void expectAvailableCallbacksValidatedAndBlocked(MockNetworkAgent agent) {
+            expectAvailableCallbacks(agent, false, true, true, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
-            expectAvailableCallbacks(agent, false, false, TEST_CALLBACK_TIMEOUT_MS);
+            expectAvailableCallbacks(agent, false, false, false, TEST_CALLBACK_TIMEOUT_MS);
+        }
+
+        void expectAvailableCallbacksUnvalidatedAndBlocked(MockNetworkAgent agent) {
+            expectAvailableCallbacks(agent, false, false, true, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         // Expects the available callbacks (where the onCapabilitiesChanged must contain the
@@ -1623,6 +1672,9 @@
             expectCallback(CallbackState.AVAILABLE, agent, TEST_CALLBACK_TIMEOUT_MS);
             NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
             expectCallback(CallbackState.LINK_PROPERTIES, agent, TEST_CALLBACK_TIMEOUT_MS);
+            // Implicitly check the network is allowed to use.
+            // TODO: should we need to consider if network is in blocked status in this case?
+            expectBlockedStatusCallback(false, agent);
             NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
             assertEquals(nc1, nc2);
         }
@@ -1665,6 +1717,12 @@
                     fn.test((NetworkCapabilities) cbi.arg));
         }
 
+        void expectBlockedStatusCallback(boolean expectBlocked, MockNetworkAgent agent) {
+            CallbackInfo cbi = expectCallback(CallbackState.BLOCKED_STATUS, agent);
+            boolean actualBlocked = (boolean) cbi.arg;
+            assertEquals(expectBlocked, actualBlocked);
+        }
+
         void assertNoCallback() {
             waitForIdle();
             CallbackInfo c = mCallbacks.peek();
@@ -3223,7 +3281,7 @@
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
                 TEST_CALLBACK_TIMEOUT_MS);
 
         // pass timeout and validate that UNAVAILABLE is not called
@@ -3243,7 +3301,7 @@
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
                 TEST_CALLBACK_TIMEOUT_MS);
         mWiFiNetworkAgent.disconnect();
         networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -3802,6 +3860,7 @@
         networkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, networkAgent);
         CallbackInfo cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
                 networkAgent);
+        networkCallback.expectCallback(CallbackState.BLOCKED_STATUS, networkAgent);
         networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent);
         networkCallback.assertNoCallback();
         checkDirectlyConnectedRoutes(cbi.arg, Arrays.asList(myIpv4Address),
@@ -4010,6 +4069,7 @@
                 mCellNetworkAgent);
         CallbackInfo cbi = cellNetworkCallback.expectCallback(
                 CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
         assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4068,6 +4128,7 @@
                 mCellNetworkAgent);
         CallbackInfo cbi = cellNetworkCallback.expectCallback(
                 CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
         assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4444,6 +4505,101 @@
         mMockVpn.disconnect();
     }
 
+    @Test
+    public void testNetworkBlockedStatus() {
+        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .build();
+        mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+        mService.setUidRulesChanged(RULE_REJECT_ALL);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+        // ConnectivityService should cache it not to invoke the callback again.
+        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        cellNetworkCallback.assertNoCallback();
+
+        mService.setUidRulesChanged(RULE_NONE);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+        // Restrict the network based on UID rule and NOT_METERED capability change.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+        cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
+                mCellNetworkAgent);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+        mService.setUidRulesChanged(RULE_ALLOW_METERED);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+        mService.setUidRulesChanged(RULE_NONE);
+        cellNetworkCallback.assertNoCallback();
+
+        // Restrict the network based on BackgroundRestricted.
+        mService.setRestrictBackgroundChanged(true);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+        mService.setRestrictBackgroundChanged(true);
+        cellNetworkCallback.assertNoCallback();
+        mService.setRestrictBackgroundChanged(false);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+
+        mCm.unregisterNetworkCallback(cellNetworkCallback);
+    }
+
+    @Test
+    public void testNetworkBlockedStatusBeforeAndAfterConnect() {
+        final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        // No Networkcallbacks invoked before any network is active.
+        mService.setUidRulesChanged(RULE_REJECT_ALL);
+        mService.setUidRulesChanged(RULE_NONE);
+        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        defaultCallback.assertNoCallback();
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+        defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent);
+
+        // Allow to use the network after switching to NOT_METERED network.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+        defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+
+        // Switch to METERED network. Restrict the use of the network.
+        mWiFiNetworkAgent.disconnect();
+        defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacksValidatedAndBlocked(mCellNetworkAgent);
+
+        // Network becomes NOT_METERED.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+        defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+        // Verify there's no Networkcallbacks invoked after data saver on/off.
+        mService.setRestrictBackgroundChanged(true);
+        mService.setRestrictBackgroundChanged(false);
+        defaultCallback.assertNoCallback();
+
+        mCellNetworkAgent.disconnect();
+        defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+        defaultCallback.assertNoCallback();
+
+        mCm.unregisterNetworkCallback(defaultCallback);
+    }
+
     /**
      * Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
      */