Merge "Fix NPE when calling updateStateLw"
diff --git a/Android.bp b/Android.bp
index 2ae7d6c..4762cba 100644
--- a/Android.bp
+++ b/Android.bp
@@ -265,6 +265,7 @@
         "core/java/android/os/storage/IStorageEventListener.aidl",
         "core/java/android/os/storage/IStorageShutdownObserver.aidl",
         "core/java/android/os/storage/IObbActionListener.aidl",
+        "core/java/android/permission/IRuntimePermissionPresenter.aidl",
         "core/java/android/rolecontrollerservice/IRoleControllerService.aidl",
         ":keystore_aidl",
         "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
@@ -442,7 +443,6 @@
         "location/java/android/location/IGeocodeProvider.aidl",
         "location/java/android/location/IGeofenceProvider.aidl",
         "location/java/android/location/IGnssStatusListener.aidl",
-        "location/java/android/location/IGnssStatusProvider.aidl",
         "location/java/android/location/IGnssMeasurementsListener.aidl",
         "location/java/android/location/IGnssNavigationMessageListener.aidl",
         "location/java/android/location/ILocationListener.aidl",
@@ -451,6 +451,7 @@
         "location/java/android/location/IGpsGeofenceHardware.aidl",
         "location/java/android/location/INetInitiatedListener.aidl",
         "location/java/com/android/internal/location/ILocationProvider.aidl",
+        "location/java/com/android/internal/location/ILocationProviderManager.aidl",
         "media/java/android/media/IAudioFocusDispatcher.aidl",
         "media/java/android/media/IAudioRoutesObserver.aidl",
         "media/java/android/media/IAudioService.aidl",
@@ -633,7 +634,6 @@
         ":libupdate_engine_aidl",
 
         ":storaged_aidl",
-        ":netd_aidl",
         ":vold_aidl",
         ":installd_aidl",
         ":dumpstate_aidl",
@@ -784,18 +784,6 @@
     ],
 }
 
-// A host library containing the inspector annotations for inspector-annotation-processor.
-java_library_host {
-    name: "inspector-annotation",
-    srcs: [
-        "core/java/android/view/inspector/InspectableNodeName.java",
-        "core/java/android/view/inspector/InspectableProperty.java",
-        // Needed for the ResourceId.ID_NULL constant
-        "core/java/android/content/res/ResourceId.java",
-        "core/java/android/annotation/AnyRes.java",
-    ],
-}
-
 // A host library including just UnsupportedAppUsage.java so that the annotation
 // processor can also use this annotation.
 java_library_host {
@@ -1617,6 +1605,7 @@
     ],
     dex_mapping_filename: "dex-mapping.txt",
     args: metalava_framework_docs_args +
+        " --hide ReferencesHidden " +
         " --show-unannotated " +
         " --show-annotation android.annotation.SystemApi " +
         " --show-annotation android.annotation.TestApi "
diff --git a/api/current.txt b/api/current.txt
index 2ef4a39..de5a0b1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -644,6 +644,7 @@
     field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
     field public static final int foreground = 16843017; // 0x1010109
     field public static final int foregroundGravity = 16843264; // 0x1010200
+    field public static final int foregroundServiceType = 16844191; // 0x101059f
     field public static final int foregroundTint = 16843885; // 0x101046d
     field public static final int foregroundTintMode = 16843886; // 0x101046e
     field public static final int format = 16843013; // 0x1010105
@@ -6664,7 +6665,7 @@
     method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
     method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
     method public void setEndUserSessionMessage(android.content.ComponentName, java.lang.CharSequence);
-    method public void setGlobalPrivateDns(android.content.ComponentName, int, java.lang.String);
+    method public int setGlobalPrivateDns(android.content.ComponentName, int, java.lang.String);
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public void setKeepUninstalledPackages(android.content.ComponentName, java.util.List<java.lang.String>);
     method public boolean setKeyPairCertificate(android.content.ComponentName, java.lang.String, java.util.List<java.security.cert.Certificate>, boolean);
@@ -6845,6 +6846,9 @@
     field public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2; // 0x2
     field public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3; // 0x3
     field public static final int PRIVATE_DNS_MODE_UNKNOWN = 0; // 0x0
+    field public static final int PRIVATE_DNS_SET_ERROR_FAILURE_SETTING = 2; // 0x2
+    field public static final int PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING = 1; // 0x1
+    field public static final int PRIVATE_DNS_SET_SUCCESS = 0; // 0x0
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -9227,11 +9231,31 @@
     field public static final android.os.Parcelable.Creator<android.content.ComponentName> CREATOR;
   }
 
-  public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
+  public abstract interface ContentInterface {
+    method public abstract android.content.ContentProviderResult[] applyBatch(java.lang.String, java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
+    method public abstract int bulkInsert(android.net.Uri, android.content.ContentValues[]) throws android.os.RemoteException;
+    method public abstract android.os.Bundle call(java.lang.String, java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
+    method public abstract android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
+    method public abstract int delete(android.net.Uri, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
+    method public abstract java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String) throws android.os.RemoteException;
+    method public abstract java.lang.String getType(android.net.Uri) throws android.os.RemoteException;
+    method public abstract android.net.Uri insert(android.net.Uri, android.content.ContentValues) throws android.os.RemoteException;
+    method public abstract android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
+    method public abstract android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
+    method public abstract android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
+    method public abstract android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal) throws android.os.RemoteException;
+    method public abstract boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal) throws android.os.RemoteException;
+    method public abstract android.net.Uri uncanonicalize(android.net.Uri) throws android.os.RemoteException;
+    method public abstract int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
+  }
+
+  public abstract class ContentProvider implements android.content.ComponentCallbacks2 android.content.ContentInterface {
     ctor public ContentProvider();
+    method public android.content.ContentProviderResult[] applyBatch(java.lang.String, java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException;
     method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException;
     method public void attachInfo(android.content.Context, android.content.pm.ProviderInfo);
     method public int bulkInsert(android.net.Uri, android.content.ContentValues[]);
+    method public android.os.Bundle call(java.lang.String, java.lang.String, java.lang.String, android.os.Bundle);
     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();
@@ -9278,10 +9302,12 @@
     method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
   }
 
-  public class ContentProviderClient implements java.lang.AutoCloseable {
+  public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface {
     method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
+    method public android.content.ContentProviderResult[] applyBatch(java.lang.String, java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
     method public int bulkInsert(android.net.Uri, android.content.ContentValues[]) throws android.os.RemoteException;
     method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
+    method public android.os.Bundle call(java.lang.String, java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
     method public final android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
     method public void close();
     method public static void closeQuietly(android.content.ContentProviderClient);
@@ -9294,6 +9320,7 @@
     method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
+    method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
@@ -9358,7 +9385,7 @@
     method public void setKeepUpdated(boolean);
   }
 
-  public abstract class ContentResolver {
+  public abstract class ContentResolver implements android.content.ContentInterface {
     ctor public ContentResolver(android.content.Context);
     method public final android.content.ContentProviderClient acquireContentProviderClient(android.net.Uri);
     method public final android.content.ContentProviderClient acquireContentProviderClient(java.lang.String);
@@ -9369,11 +9396,13 @@
     method public android.content.ContentProviderResult[] applyBatch(java.lang.String, java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
     method public final int bulkInsert(android.net.Uri, android.content.ContentValues[]);
     method public final android.os.Bundle call(android.net.Uri, java.lang.String, java.lang.String, android.os.Bundle);
+    method public final android.os.Bundle call(java.lang.String, java.lang.String, java.lang.String, android.os.Bundle);
     method public deprecated void cancelSync(android.net.Uri);
     method public static void cancelSync(android.accounts.Account, java.lang.String);
     method public static void cancelSync(android.content.SyncRequest);
     method public final android.net.Uri canonicalize(android.net.Uri);
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+    method public android.os.Bundle getCache(android.net.Uri);
     method public static deprecated android.content.SyncInfo getCurrentSync();
     method public static java.util.List<android.content.SyncInfo> getCurrentSyncs();
     method public static int getIsSyncable(android.accounts.Account, java.lang.String);
@@ -9392,15 +9421,19 @@
     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);
+    method public final android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+    method public final android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final java.io.InputStream openInputStream(android.net.Uri) throws java.io.FileNotFoundException;
     method public final java.io.OutputStream openOutputStream(android.net.Uri) throws java.io.FileNotFoundException;
     method public final java.io.OutputStream openOutputStream(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+    method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+    method public void putCache(android.net.Uri, android.os.Bundle);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
@@ -10205,6 +10238,7 @@
     field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
     field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
     field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+    field public static final java.lang.String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE = "android.intent.extra.AUTO_LAUNCH_SINGLE_CHOICE";
     field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
     field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
     field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -11479,6 +11513,7 @@
     field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
     field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
     field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
+    field public static final java.lang.String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
     field public static final java.lang.String FEATURE_IRIS = "android.hardware.iris";
     field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
     field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
@@ -11724,12 +11759,20 @@
     ctor public ServiceInfo(android.content.pm.ServiceInfo);
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
+    method public int getForegroundServiceType();
     field public static final android.os.Parcelable.Creator<android.content.pm.ServiceInfo> CREATOR;
     field public static final int FLAG_EXTERNAL_SERVICE = 4; // 0x4
     field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
     field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
     field public static final int FLAG_USE_APP_ZYGOTE = 8; // 0x8
+    field public static final int FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION = 5; // 0x5
+    field public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 4; // 0x4
+    field public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAY = 2; // 0x2
+    field public static final int FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS = 6; // 0x6
+    field public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 3; // 0x3
+    field public static final int FOREGROUND_SERVICE_TYPE_SYNC = 1; // 0x1
+    field public static final int FOREGROUND_SERVICE_TYPE_UNSPECIFIED = 0; // 0x0
     field public int flags;
     field public java.lang.String permission;
   }
@@ -22701,8 +22744,8 @@
     method public boolean addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler);
     method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
     method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
-    method public void clearTestProviderEnabled(java.lang.String);
-    method public void clearTestProviderLocation(java.lang.String);
+    method public deprecated void clearTestProviderEnabled(java.lang.String);
+    method public deprecated void clearTestProviderLocation(java.lang.String);
     method public deprecated void clearTestProviderStatus(java.lang.String);
     method public java.util.List<java.lang.String> getAllProviders();
     method public java.lang.String getBestProvider(android.location.Criteria, boolean);
@@ -22914,6 +22957,7 @@
     method public int getChannelIndexMask();
     method public int getChannelMask();
     method public int getEncoding();
+    method public int getFrameSizeInBytes();
     method public int getSampleRate();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
@@ -23353,6 +23397,7 @@
     method public int getStreamType();
     method public boolean getTimestamp(android.media.AudioTimestamp);
     method public int getUnderrunCount();
+    method public static boolean isDirectPlaybackSupported(android.media.AudioFormat, android.media.AudioAttributes);
     method public void pause() throws java.lang.IllegalStateException;
     method public void play() throws java.lang.IllegalStateException;
     method public void registerStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
@@ -23933,6 +23978,7 @@
     field public static final deprecated int INFO_OUTPUT_BUFFERS_CHANGED = -3; // 0xfffffffd
     field public static final int INFO_OUTPUT_FORMAT_CHANGED = -2; // 0xfffffffe
     field public static final int INFO_TRY_AGAIN_LATER = -1; // 0xffffffff
+    field public static final java.lang.String PARAMETER_KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
     field public static final java.lang.String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
     field public static final java.lang.String PARAMETER_KEY_SUSPEND = "drop-input-frames";
     field public static final java.lang.String PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
@@ -24206,6 +24252,7 @@
     field public static final int HEVCProfileMain = 1; // 0x1
     field public static final int HEVCProfileMain10 = 2; // 0x2
     field public static final int HEVCProfileMain10HDR10 = 4096; // 0x1000
+    field public static final int HEVCProfileMain10HDR10Plus = 8192; // 0x2000
     field public static final int HEVCProfileMainStill = 4; // 0x4
     field public static final int MPEG2LevelH14 = 2; // 0x2
     field public static final int MPEG2LevelHL = 3; // 0x3
@@ -24267,8 +24314,10 @@
     field public static final int VP9Profile1 = 2; // 0x2
     field public static final int VP9Profile2 = 4; // 0x4
     field public static final int VP9Profile2HDR = 4096; // 0x1000
+    field public static final int VP9Profile2HDR10Plus = 16384; // 0x4000
     field public static final int VP9Profile3 = 8; // 0x8
     field public static final int VP9Profile3HDR = 8192; // 0x2000
+    field public static final int VP9Profile3HDR10Plus = 32768; // 0x8000
     field public int level;
     field public int profile;
   }
@@ -24667,6 +24716,7 @@
     field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
     field public static final java.lang.String KEY_GRID_COLUMNS = "grid-cols";
     field public static final java.lang.String KEY_GRID_ROWS = "grid-rows";
+    field public static final java.lang.String KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
     field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info";
     field public static final java.lang.String KEY_HEIGHT = "height";
     field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
@@ -24705,6 +24755,7 @@
     field public static final java.lang.String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
     field public static final java.lang.String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
     field public static final java.lang.String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
+    field public static final java.lang.String MIMETYPE_AUDIO_EAC3_JOC = "audio/eac3-joc";
     field public static final java.lang.String MIMETYPE_AUDIO_FLAC = "audio/flac";
     field public static final java.lang.String MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
     field public static final java.lang.String MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
@@ -28340,6 +28391,7 @@
     method public int describeContents();
     method public int getLinkDownstreamBandwidthKbps();
     method public int getLinkUpstreamBandwidthKbps();
+    method public android.net.TransportInfo getTransportInfo();
     method public boolean hasCapability(int);
     method public boolean hasTransport(int);
     method public void writeToParcel(android.os.Parcel, int);
@@ -28548,6 +28600,9 @@
     field public static final int UNSUPPORTED = -1; // 0xffffffff
   }
 
+  public abstract interface TransportInfo {
+  }
+
   public abstract class Uri implements java.lang.Comparable android.os.Parcelable {
     method public abstract android.net.Uri.Builder buildUpon();
     method public int compareTo(android.net.Uri);
@@ -28583,6 +28638,7 @@
     method public abstract boolean isRelative();
     method public android.net.Uri normalizeScheme();
     method public static android.net.Uri parse(java.lang.String);
+    method public java.lang.String toSafeString();
     method public abstract java.lang.String toString();
     method public static android.net.Uri withAppendedPath(android.net.Uri, java.lang.String);
     method public static void writeToParcel(android.os.Parcel, android.net.Uri);
@@ -35999,6 +36055,7 @@
     field public static final java.lang.String CALENDAR_LOCATION = "calendar_location";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DEFAULT_SORT_ORDER = "calendar_displayName";
+    field public static final android.net.Uri ENTERPRISE_CONTENT_URI;
     field public static final java.lang.String NAME = "name";
   }
 
@@ -36027,6 +36084,7 @@
   public static final class CalendarContract.Events implements android.provider.BaseColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.EventsColumns android.provider.CalendarContract.SyncColumns {
     field public static final android.net.Uri CONTENT_EXCEPTION_URI;
     field public static final android.net.Uri CONTENT_URI;
+    field public static final android.net.Uri ENTERPRISE_CONTENT_URI;
   }
 
   protected static abstract interface CalendarContract.EventsColumns {
@@ -36118,6 +36176,10 @@
     field public static final java.lang.String END = "end";
     field public static final java.lang.String END_DAY = "endDay";
     field public static final java.lang.String END_MINUTE = "endMinute";
+    field public static final android.net.Uri ENTERPRISE_CONTENT_BY_DAY_URI;
+    field public static final android.net.Uri ENTERPRISE_CONTENT_SEARCH_BY_DAY_URI;
+    field public static final android.net.Uri ENTERPRISE_CONTENT_SEARCH_URI;
+    field public static final android.net.Uri ENTERPRISE_CONTENT_URI;
     field public static final java.lang.String EVENT_ID = "event_id";
     field public static final java.lang.String START_DAY = "startDay";
     field public static final java.lang.String START_MINUTE = "startMinute";
@@ -37363,26 +37425,26 @@
     method public static android.net.Uri buildRootsUri(java.lang.String);
     method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
     method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
-    method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
-    method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
-    method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static void ejectRoot(android.content.ContentResolver, android.net.Uri);
-    method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static android.net.Uri copyDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static android.net.Uri createDocument(android.content.ContentInterface, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+    method public static android.content.IntentSender createWebLinkIntent(android.content.ContentInterface, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
+    method public static boolean deleteDocument(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static void ejectRoot(android.content.ContentInterface, android.net.Uri);
+    method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
     method public static java.lang.String getDocumentId(android.net.Uri);
-    method public static android.os.Bundle getDocumentMetadata(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+    method public static android.os.Bundle getDocumentMetadata(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentInterface, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public static java.lang.String getRootId(android.net.Uri);
     method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
     method public static java.lang.String getTreeDocumentId(android.net.Uri);
-    method public static boolean isChildDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static boolean isChildDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
     method public static boolean isRootUri(android.content.Context, android.net.Uri);
     method public static boolean isRootsUri(android.content.Context, android.net.Uri);
     method public static boolean isTreeUri(android.net.Uri);
-    method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+    method public static android.net.Uri moveDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static boolean removeDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static android.net.Uri renameDocument(android.content.ContentInterface, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     field public static final java.lang.String ACTION_DOCUMENT_SETTINGS = "android.provider.action.DOCUMENT_SETTINGS";
     field public static final java.lang.String EXTRA_ERROR = "error";
     field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -40411,6 +40473,7 @@
     method public android.service.autofill.FillResponse.Builder setHeader(android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
+    method public android.service.autofill.FillResponse.Builder setUserData(android.service.autofill.UserData);
   }
 
   public final class ImageTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -42270,8 +42333,9 @@
     method public void swapConference();
     method public void unhold();
     method public void unregisterCallback(android.telecom.Call.Callback);
-    field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
+    field public static final deprecated java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
     field public static final java.lang.String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS";
+    field public static final java.lang.String EXTRA_SUGGESTED_PHONE_ACCOUNTS = "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS";
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_CONNECTING = 9; // 0x9
     field public static final int STATE_DIALING = 1; // 0x1
@@ -42842,6 +42906,20 @@
     field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccountHandle> CREATOR;
   }
 
+  public final class PhoneAccountSuggestion implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
+    method public int getReason();
+    method public boolean shouldAutoSelect();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccountSuggestion> CREATOR;
+    field public static final int REASON_FREQUENT = 2; // 0x2
+    field public static final int REASON_INTRA_CARRIER = 1; // 0x1
+    field public static final int REASON_NONE = 0; // 0x0
+    field public static final int REASON_OTHER = 4; // 0x4
+    field public static final int REASON_USER_SET = 3; // 0x3
+  }
+
   public final class RemoteConference {
     method public void disconnect();
     method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
@@ -43729,10 +43807,10 @@
     method public static java.lang.String getStrippedReversed(java.lang.String);
     method public static final boolean is12Key(char);
     method public static final boolean isDialable(char);
-    method public static boolean isEmergencyNumber(java.lang.String);
+    method public static deprecated boolean isEmergencyNumber(java.lang.String);
     method public static boolean isGlobalPhoneNumber(java.lang.String);
     method public static boolean isISODigit(char);
-    method public static boolean isLocalEmergencyNumber(android.content.Context, java.lang.String);
+    method public static deprecated boolean isLocalEmergencyNumber(android.content.Context, java.lang.String);
     method public static final boolean isNonSeparator(char);
     method public static final boolean isReallyDialable(char);
     method public static final boolean isStartsPostDial(char);
@@ -44440,11 +44518,13 @@
     method public java.util.List<java.lang.Integer> getEmergencyNumberSources();
     method public java.util.List<java.lang.Integer> getEmergencyServiceCategories();
     method public int getEmergencyServiceCategoryBitmask();
+    method public java.lang.String getMnc();
     method public java.lang.String getNumber();
     method public boolean isFromSources(int);
     method public boolean isInEmergencyServiceCategories(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR;
+    field public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 16; // 0x10
     field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8
     field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4
     field public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 1; // 0x1
@@ -52427,6 +52507,7 @@
 
   public static final class ConversationActions.Request implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.String getCallingPackageName();
     method public java.util.List<android.view.textclassifier.ConversationActions.Message> getConversation();
     method public java.util.List<java.lang.String> getHints();
     method public int getMaxSuggestions();
@@ -52540,6 +52621,7 @@
 
   public static final class TextClassification.Request implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.String getCallingPackageName();
     method public android.os.LocaleList getDefaultLocales();
     method public int getEndIndex();
     method public android.os.Bundle getExtras();
@@ -52657,6 +52739,7 @@
 
   public static final class TextLanguage.Request implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.String getCallingPackageName();
     method public android.os.Bundle getExtras();
     method public java.lang.CharSequence getText();
     method public void writeToParcel(android.os.Parcel, int);
@@ -52688,6 +52771,7 @@
   public static final class TextLinks.Builder {
     ctor public TextLinks.Builder(java.lang.String);
     method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>);
+    method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>, android.os.Bundle);
     method public android.view.textclassifier.TextLinks build();
     method public android.view.textclassifier.TextLinks.Builder clearTextLinks();
     method public android.view.textclassifier.TextLinks.Builder setExtras(android.os.Bundle);
@@ -52695,6 +52779,7 @@
 
   public static final class TextLinks.Request implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.String getCallingPackageName();
     method public android.os.LocaleList getDefaultLocales();
     method public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig();
     method public android.os.Bundle getExtras();
@@ -52717,6 +52802,7 @@
     method public int getEnd();
     method public java.lang.String getEntity(int);
     method public int getEntityCount();
+    method public android.os.Bundle getExtras();
     method public int getStart();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks.TextLink> CREATOR;
@@ -52751,6 +52837,7 @@
 
   public static final class TextSelection.Request implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.String getCallingPackageName();
     method public android.os.LocaleList getDefaultLocales();
     method public int getEndIndex();
     method public android.os.Bundle getExtras();
@@ -53251,7 +53338,7 @@
     method public abstract boolean getDomStorageEnabled();
     method public abstract java.lang.String getFantasyFontFamily();
     method public abstract java.lang.String getFixedFontFamily();
-    method public abstract int getForceDarkMode();
+    method public int getForceDarkMode();
     method public abstract boolean getJavaScriptCanOpenWindowsAutomatically();
     method public abstract boolean getJavaScriptEnabled();
     method public abstract android.webkit.WebSettings.LayoutAlgorithm getLayoutAlgorithm();
@@ -53298,7 +53385,7 @@
     method public abstract deprecated void setEnableSmoothTransition(boolean);
     method public abstract void setFantasyFontFamily(java.lang.String);
     method public abstract void setFixedFontFamily(java.lang.String);
-    method public abstract void setForceDarkMode(int);
+    method public void setForceDarkMode(int);
     method public abstract deprecated void setGeolocationDatabasePath(java.lang.String);
     method public abstract void setGeolocationEnabled(boolean);
     method public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index a37da64..e6e252c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7,6 +7,7 @@
     field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
     field public static final java.lang.String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
     field public static final deprecated java.lang.String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
+    field public static final java.lang.String ACCESS_INSTANT_APPS = "android.permission.ACCESS_INSTANT_APPS";
     field public static final java.lang.String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
     field public static final java.lang.String ACCESS_MTP = "android.permission.ACCESS_MTP";
     field public static final java.lang.String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS";
@@ -90,6 +91,7 @@
     field public static final java.lang.String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
     field public static final java.lang.String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES";
     field public static final java.lang.String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
+    field public static final java.lang.String INTERACT_ACROSS_PROFILES = "android.permission.INTERACT_ACROSS_PROFILES";
     field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
     field public static final java.lang.String INTERACT_ACROSS_USERS_FULL = "android.permission.INTERACT_ACROSS_USERS_FULL";
     field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
@@ -298,6 +300,7 @@
     method public void killUid(int, java.lang.String);
     method public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
     method public static void setPersistentVrThread(int);
+    method public boolean switchUser(android.os.UserHandle);
   }
 
   public static abstract interface ActivityManager.OnUidImportanceListener {
@@ -314,7 +317,6 @@
     method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
     method public static java.lang.String[] getOpStrs();
     method public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, java.lang.String, int[]);
-    method public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOpStrs(java.lang.String[]);
     method public static int opToDefaultMode(java.lang.String);
     method public static java.lang.String opToPermission(java.lang.String);
     method public void setMode(java.lang.String, int, java.lang.String, int);
@@ -614,7 +616,12 @@
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
     field public static final java.lang.String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME";
     field public static final java.lang.String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL";
+    field public static final java.lang.String EXTRA_PROVISIONING_TRIGGER = "android.app.extra.PROVISIONING_TRIGGER";
     field public static final java.lang.String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
+    field public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1; // 0x1
+    field public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3
+    field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
+    field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
     field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
     field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
     field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
@@ -845,6 +852,8 @@
     method public int restoreAll(long, android.app.backup.RestoreObserver);
     method public int restorePackage(java.lang.String, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor);
     method public int restorePackage(java.lang.String, android.app.backup.RestoreObserver);
+    method public int restoreSome(long, android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor, java.lang.String[]);
+    method public int restoreSome(long, android.app.backup.RestoreObserver, java.lang.String[]);
   }
 
   public class RestoreSet implements android.os.Parcelable {
@@ -1031,6 +1040,14 @@
 
 package android.content {
 
+  public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface {
+    method public void setDetectNotResponding(long);
+  }
+
+  public abstract class ContentResolver {
+    method public android.graphics.drawable.Drawable getTypeDrawable(java.lang.String);
+  }
+
   public abstract class Context {
     method public boolean bindServiceAsUser(android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
     method public abstract android.content.Context createCredentialProtectedStorageContext();
@@ -1129,6 +1146,10 @@
     field public int targetSandboxVersion;
   }
 
+  public class CrossProfileApps {
+    method public void startAnyActivity(android.content.ComponentName, android.os.UserHandle);
+  }
+
   public final class InstantAppInfo implements android.os.Parcelable {
     ctor public InstantAppInfo(android.content.pm.ApplicationInfo, java.lang.String[], java.lang.String[]);
     ctor public InstantAppInfo(java.lang.String, java.lang.CharSequence, java.lang.String[], java.lang.String[]);
@@ -1241,6 +1262,7 @@
     method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
     method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public java.lang.String getWellbeingPackageName();
     method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -1343,6 +1365,7 @@
     field public static final int FLAG_REMOVED = 2; // 0x2
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+    field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
     field public java.lang.String backgroundPermission;
     field public int requestRes;
   }
@@ -1387,7 +1410,7 @@
 
 package android.content.pm.permission {
 
-  public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
+  public final deprecated class RuntimePermissionPresentationInfo implements android.os.Parcelable {
     ctor public RuntimePermissionPresentationInfo(java.lang.CharSequence, boolean, boolean);
     method public int describeContents();
     method public java.lang.CharSequence getLabel();
@@ -2920,6 +2943,13 @@
     field public static final int RADIO_TUNER = 1998; // 0x7ce
   }
 
+  public static class MediaTimestamp.Builder {
+    ctor public MediaTimestamp.Builder();
+    ctor public MediaTimestamp.Builder(android.media.MediaTimestamp);
+    method public android.media.MediaTimestamp build();
+    method public android.media.MediaTimestamp.Builder setMediaTimestamp(long, long, float);
+  }
+
   public class PlayerProxy {
     method public void pause();
     method public void setPan(float);
@@ -2940,7 +2970,7 @@
     ctor public TimedMetaData.Builder();
     ctor public TimedMetaData.Builder(android.media.TimedMetaData);
     method public android.media.TimedMetaData build();
-    method public android.media.TimedMetaData.Builder setTimedMetaData(int, byte[]);
+    method public android.media.TimedMetaData.Builder setTimedMetaData(long, byte[]);
   }
 
 }
@@ -3030,7 +3060,6 @@
 package android.media.session {
 
   public final class MediaSessionManager {
-    method public android.media.session.ISession createSession(android.media.session.MediaSession.CallbackStub, java.lang.String, int);
     method public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, android.os.Handler);
     method public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler);
   }
@@ -4318,6 +4347,7 @@
   }
 
   public class UserManager {
+    method public boolean canSwitchUsers();
     method public void clearSeedAccountData();
     method public android.os.UserHandle getProfileParent(android.os.UserHandle);
     method public java.lang.String getSeedAccountName();
@@ -4333,6 +4363,7 @@
     method public boolean isManagedProfile(int);
     method public boolean isPrimaryUser();
     method public boolean isRestrictedProfile();
+    method public boolean removeUser(android.os.UserHandle);
     field public static final java.lang.String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
     field public static final deprecated java.lang.String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
     field public static final java.lang.String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
@@ -4392,11 +4423,31 @@
     method public int getTargetSdk();
   }
 
+  public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
+    ctor public RuntimePermissionPresentationInfo(java.lang.CharSequence, boolean, boolean);
+    method public int describeContents();
+    method public java.lang.CharSequence getLabel();
+    method public boolean isGranted();
+    method public boolean isStandard();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionPresentationInfo> CREATOR;
+  }
+
+  public abstract class RuntimePermissionPresenterService extends android.app.Service {
+    ctor public RuntimePermissionPresenterService();
+    method public final void attachBaseContext(android.content.Context);
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
+    method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
+    method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.permission.RuntimePermissionPresenterService";
+  }
+
 }
 
 package android.permissionpresenterservice {
 
-  public abstract class RuntimePermissionPresenterService extends android.app.Service {
+  public abstract deprecated class RuntimePermissionPresenterService extends android.app.Service {
     ctor public RuntimePermissionPresenterService();
     method public final void attachBaseContext(android.content.Context);
     method public final android.os.IBinder onBind(android.content.Intent);
@@ -4522,6 +4573,11 @@
     field public static final int FLAG_REMOVABLE_USB = 524288; // 0x80000
   }
 
+  public final class MediaStore {
+    method public static void deleteContributedMedia(android.content.Context, java.lang.String);
+    method public static long getContributedMediaSize(android.content.Context, java.lang.String);
+  }
+
   public abstract class SearchIndexableData {
     ctor public SearchIndexableData();
     ctor public SearchIndexableData(android.content.Context);
@@ -5584,6 +5640,10 @@
     field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
   }
 
+  public final class PhoneAccountSuggestion implements android.os.Parcelable {
+    ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
+  }
+
   public final class RemoteConference {
     method public deprecated void setAudioState(android.telecom.AudioState);
   }
@@ -5924,6 +5984,7 @@
     method public int getVoiceActivationState();
     method public boolean handlePinMmi(java.lang.String);
     method public boolean handlePinMmiForSubscriber(int, java.lang.String);
+    method public boolean isCurrentPotentialEmergencyNumber(java.lang.String);
     method public boolean isDataConnectivityPossible();
     method public deprecated boolean isIdle();
     method public deprecated boolean isOffhook();
diff --git a/api/test-current.txt b/api/test-current.txt
index 46cbb52..7ef58f1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -276,6 +276,23 @@
 
 }
 
+package android.app.role {
+
+  public final class RoleManager {
+    method public void addRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+    method public void clearRoleHoldersAsUser(java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+    method public java.util.List<java.lang.String> getRoleHolders(java.lang.String);
+    method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
+    method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
+  }
+
+  public abstract interface RoleManagerCallback {
+    method public abstract void onFailure();
+    method public abstract void onSuccess();
+  }
+
+}
+
 package android.app.usage {
 
   public class NetworkStatsManager {
@@ -299,7 +316,11 @@
 
 package android.content {
 
-  public abstract class ContentResolver {
+  public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface {
+    method public void setDetectNotResponding(long);
+  }
+
+  public abstract class ContentResolver implements android.content.ContentInterface {
     method public static java.lang.String[] getSyncAdapterPackagesForAuthorityAsUser(java.lang.String, int);
   }
 
@@ -353,6 +374,7 @@
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
+    field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
     field public java.lang.String backgroundPermission;
   }
 
@@ -983,6 +1005,11 @@
     field public static final android.net.Uri CORP_CONTENT_URI;
   }
 
+  public final class MediaStore {
+    method public static void deleteContributedMedia(android.content.Context, java.lang.String);
+    method public static long getContributedMediaSize(android.content.Context, java.lang.String);
+  }
+
   public final class Settings {
     field public static final java.lang.String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
   }
@@ -1240,6 +1267,10 @@
     ctor public CallAudioState(boolean, int, int, android.bluetooth.BluetoothDevice, java.util.Collection<android.bluetooth.BluetoothDevice>);
   }
 
+  public final class PhoneAccountSuggestion implements android.os.Parcelable {
+    ctor public PhoneAccountSuggestion(android.telecom.PhoneAccountHandle, int, boolean);
+  }
+
 }
 
 package android.telephony {
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 52a2ab4..55dbc17 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -557,7 +557,7 @@
 
         @Override
         public void onExecute(IContentProvider provider) throws Exception {
-            Bundle result = provider.call(null, mMethod, mArg, mExtras);
+            Bundle result = provider.call(null, mUri.getAuthority(), mMethod, mArg, mExtras);
             if (result != null) {
                 result.size(); // unpack
             }
diff --git a/cmds/device_config/Android.mk b/cmds/device_config/Android.mk
new file mode 100644
index 0000000..4041e01
--- /dev/null
+++ b/cmds/device_config/Android.mk
@@ -0,0 +1,10 @@
+# Copyright 2018 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := device_config
+LOCAL_SRC_FILES := device_config
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
diff --git a/cmds/device_config/device_config b/cmds/device_config/device_config
new file mode 100755
index 0000000..a949bd5
--- /dev/null
+++ b/cmds/device_config/device_config
@@ -0,0 +1,2 @@
+#!/system/bin/sh
+cmd device_config "$@"
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 392d40a..78d8e29 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -30,6 +30,7 @@
 import "frameworks/base/core/proto/android/server/location/enums.proto";
 import "frameworks/base/core/proto/android/service/procstats_enum.proto";
 import "frameworks/base/core/proto/android/stats/enums.proto";
+import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto";
 import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
 import "frameworks/base/core/proto/android/telecomm/enums.proto";
 import "frameworks/base/core/proto/android/telephony/enums.proto";
@@ -159,6 +160,15 @@
         PhenotypeFlagStateChanged phenotype_flag_state_changed = 101;
         BinaryPushStateChanged binary_push_state_changed = 102;
         DevicePolicyEvent device_policy_event = 103;
+        DocsUIFileOperationCanceledReported docs_ui_file_op_canceled = 104;
+        DocsUIFileOperationCopyMoveModeReported docs_ui_file_op_copy_move_mode_reported = 105;
+        DocsUIFileOperationFailureReported docs_ui_file_op_failure = 106;
+        DocsUIFileOperationReported docs_ui_provider_file_op = 107;
+        DocsUIInvalidScopedAccessRequestReported docs_ui_invalid_scoped_access_request = 108;
+        DocsUILaunchReported docs_ui_launch_reported = 109;
+        DocsUIRootVisitedReported docs_ui_root_visited = 110;
+        DocsUIStartupMsReported docs_ui_startup_ms = 111;
+        DocsUIUserActionReported docs_ui_user_action_reported = 112;
     }
 
     // Pulled events will start at field 10000.
@@ -3467,3 +3477,103 @@
     // A parameter specifying a list of package names, bundle extras or string parameters.
     optional android.stats.devicepolicy.StringList string_list_value = 6 [(log_mode) = MODE_BYTES];
 }
+
+/**
+ * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUILaunchReported {
+    optional android.stats.docsui.LaunchAction launch_action = 1;
+    optional bool has_initial_uri = 2;
+    optional android.stats.docsui.MimeType mime_type = 3;
+    optional android.stats.docsui.Root initial_root = 4;
+}
+
+/**
+ * Logs root/app visited event in file managers/picker. Call this when the user
+ * taps on root/app in hamburger menu.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIRootVisitedReported {
+    optional android.stats.docsui.ContextScope scope = 1;
+    optional android.stats.docsui.Root root = 2;
+}
+
+/**
+ * Logs file operation stats. Call this when a file operation has completed.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIFileOperationReported {
+    optional android.stats.docsui.Provider provider = 1;
+    optional android.stats.docsui.FileOperation file_op = 2;
+}
+
+/**
+ * Logs file operation stats. Call this when a copy/move operation has completed with a specific
+ * mode.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIFileOperationCopyMoveModeReported {
+    optional android.stats.docsui.FileOperation file_op = 1;
+    optional android.stats.docsui.CopyMoveOpMode mode = 2;
+}
+
+
+/**
+ * Logs file sub operation stats. Call this when a file operation has failed.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIFileOperationFailureReported {
+    optional android.stats.docsui.Authority authority = 1;
+    optional android.stats.docsui.SubFileOperation sub_op = 2;
+}
+
+/**
+* Logs the cancellation of a file operation. Call this when a job is canceled
+*
+* Logged from:
+*     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+*/
+message DocsUIFileOperationCanceledReported {
+    optional android.stats.docsui.FileOperation file_op = 1;
+}
+
+/**
+ * Logs startup time in milliseconds.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIStartupMsReported {
+    optional int32 startup_millis = 1;
+}
+
+/**
+ * Logs the action that was started by user.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIUserActionReported {
+    optional android.stats.docsui.UserAction action = 1;
+}
+
+/**
+ * Logs the invalid type when invalid scoped access is requested.
+ *
+ * Logged from:
+ *     package/app/DocumentsUI/src/com/android/documentsui/ScopedAccessMetrics.java
+ */
+message DocsUIInvalidScopedAccessRequestReported {
+    optional android.stats.docsui.InvalidScopedAccess type = 1;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index f501574..7043d66 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -59,15 +59,21 @@
     mLastPullTimeNs = elapsedTimeNs;
     int64_t pullStartTimeNs = getElapsedRealtimeNs();
     bool ret = PullInternal(&mCachedData);
+    if (!ret) {
+        mCachedData.clear();
+        return false;
+    }
     StatsdStats::getInstance().notePullTime(mTagId, getElapsedRealtimeNs() - pullStartTimeNs);
     for (const shared_ptr<LogEvent>& data : mCachedData) {
         data->setElapsedTimestampNs(elapsedTimeNs);
         data->setLogdWallClockTimestampNs(wallClockTimeNs);
     }
-    if (ret && mCachedData.size() > 0) {
+
+    if (mCachedData.size() > 0) {
         mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
         (*data) = mCachedData;
     }
+
     StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
     return ret;
 }
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 22cb2f5..cafd797 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -39,6 +39,7 @@
 
     // Pulls the data. The returned data will have elapsedTimeNs set as timeNs
     // and will have wallClockTimeNs set as current wall clock time.
+    // Return true if the pull is successful.
     bool Pull(const int64_t timeNs, std::vector<std::shared_ptr<LogEvent>>* data);
 
     // Clear cache immediately
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 5ca8814..14f2de0 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -49,6 +49,8 @@
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
 const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
+const int FIELD_ID_IS_ACTIVE = 13;
+
 // for CountMetricDataWrapper
 const int FIELD_ID_DATA = 1;
 // for CountMetricData
@@ -151,10 +153,13 @@
     } else {
         flushIfNeededLocked(dumpTimeNs);
     }
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+
+
     if (mPastBuckets.empty()) {
         return;
     }
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 35deffe..7797bd9 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -48,6 +48,7 @@
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
 const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
+const int FIELD_ID_IS_ACTIVE = 13;
 // for DurationMetricDataWrapper
 const int FIELD_ID_DATA = 1;
 // for DurationMetricData
@@ -461,12 +462,14 @@
     } else {
         flushIfNeededLocked(dumpTimeNs);
     }
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+
     if (mPastBuckets.empty()) {
         VLOG(" Duration metric, empty return");
         return;
     }
 
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index a18e406..31a4361 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -44,6 +44,7 @@
 // for StatsLogReport
 const int FIELD_ID_ID = 1;
 const int FIELD_ID_EVENT_METRICS = 4;
+const int FIELD_ID_IS_ACTIVE = 13;
 // for EventMetricDataWrapper
 const int FIELD_ID_DATA = 1;
 // for EventMetricData
@@ -108,10 +109,11 @@
                                              const bool erase_data,
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
     if (mProto->size() <= 0) {
         return;
     }
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
 
     size_t bufferSize = mProto->size();
     VLOG("metric %lld dump report now... proto size: %zu ",
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 3a34743..03e42ce 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -49,6 +49,7 @@
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
 const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
+const int FIELD_ID_IS_ACTIVE = 13;
 // for GaugeMetricDataWrapper
 const int FIELD_ID_DATA = 1;
 const int FIELD_ID_SKIPPED = 2;
@@ -192,11 +193,13 @@
         flushIfNeededLocked(dumpTimeNs);
     }
 
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+
     if (mPastBuckets.empty()) {
         return;
     }
 
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 127cbbd..09e2409 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -223,6 +223,10 @@
 
     void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
 
+    inline bool isActiveLocked() const {
+        return mIsActive;
+    }
+
     /**
      * Flushes the current bucket if the eventTime is after the current bucket's end time. This will
        also flush the current partial bucket in memory.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index a34df8aa..f25520b 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -52,6 +52,7 @@
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
 const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
+const int FIELD_ID_IS_ACTIVE = 13;
 // for ValueMetricDataWrapper
 const int FIELD_ID_DATA = 1;
 const int FIELD_ID_SKIPPED = 2;
@@ -72,17 +73,15 @@
 const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
 const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
 
+const Value ZERO_LONG((int64_t)0);
+const Value ZERO_DOUBLE((int64_t)0);
+
 // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
-ValueMetricProducer::ValueMetricProducer(const ConfigKey& key,
-                                         const ValueMetric& metric,
-                                         const int conditionIndex,
-                                         const sp<ConditionWizard>& conditionWizard,
-                                         const int whatMatcherIndex,
-                                         const sp<EventMatcherWizard>& matcherWizard,
-                                         const int pullTagId,
-                                         const int64_t timeBaseNs,
-                                         const int64_t startTimeNs,
-                                         const sp<StatsPullerManager>& pullerManager)
+ValueMetricProducer::ValueMetricProducer(
+        const ConfigKey& key, const ValueMetric& metric, const int conditionIndex,
+        const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+        const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
+        const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
@@ -102,7 +101,9 @@
       mAggregationType(metric.aggregation_type()),
       mUseDiff(metric.has_use_diff() ? metric.use_diff() : (mIsPulled ? true : false)),
       mValueDirection(metric.value_direction()),
-      mSkipZeroDiffOutput(metric.skip_zero_diff_output()) {
+      mSkipZeroDiffOutput(metric.skip_zero_diff_output()),
+      mUseZeroDefaultBase(metric.use_zero_default_base()),
+      mHasGlobalBase(false) {
     int64_t bucketSizeMills = 0;
     if (metric.has_bucket()) {
         bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
@@ -190,10 +191,12 @@
     } else {
         flushIfNeededLocked(dumpTimeNs);
     }
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_IS_ACTIVE, isActiveLocked());
+
     if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
         return;
     }
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
     // Fills the dimension path if not slicing by ALL.
@@ -302,6 +305,15 @@
     }
 }
 
+void ValueMetricProducer::resetBase() {
+    for (auto& slice : mCurrentSlicedBucket) {
+        for (auto& interval : slice.second) {
+            interval.hasBase = false;
+        }
+    }
+    mHasGlobalBase = false;
+}
+
 void ValueMetricProducer::onConditionChangedLocked(const bool condition,
                                                    const int64_t eventTimeNs) {
     if (eventTimeNs < mCurrentBucketStartTimeNs) {
@@ -317,13 +329,10 @@
         pullAndMatchEventsLocked(eventTimeNs);
     }
 
-    // when condition change from true to false, clear diff base
+    // when condition change from true to false, clear diff base but don't
+    // reset other counters as we may accumulate more value in the bucket.
     if (mUseDiff && mCondition && !condition) {
-        for (auto& slice : mCurrentSlicedBucket) {
-            for (auto& interval : slice.second) {
-                interval.hasBase = false;
-            }
-        }
+        resetBase();
     }
 
     mCondition = condition;
@@ -332,15 +341,17 @@
 void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
     vector<std::shared_ptr<LogEvent>> allData;
     if (mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
-        if (allData.size() == 0) {
-            return;
-        }
         for (const auto& data : allData) {
             if (mEventMatcherWizard->matchLogEvent(
                 *data, mWhatMatcherIndex) == MatchingState::kMatched) {
                 onMatchedLogEventLocked(mWhatMatcherIndex, *data);
             }
         }
+        mHasGlobalBase = true;
+    } else {
+        // for pulled data, every pull is needed. So we reset the base if any
+        // pull fails.
+        resetBase();
     }
 }
 
@@ -376,6 +387,7 @@
                 onMatchedLogEventLocked(mWhatMatcherIndex, *data);
             }
         }
+        mHasGlobalBase = true;
     } else {
         VLOG("No need to commit data on condition false.");
     }
@@ -486,11 +498,18 @@
         }
 
         if (mUseDiff) {
-            // no base. just update base and return.
             if (!interval.hasBase) {
-                interval.base = value;
-                interval.hasBase = true;
-                return;
+                if (mHasGlobalBase && mUseZeroDefaultBase) {
+                    // The bucket has global base. This key does not.
+                    // Optionally use zero as base.
+                    interval.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
+                    interval.hasBase = true;
+                } else {
+                    // no base. just update base and return.
+                    interval.base = value;
+                    interval.hasBase = true;
+                    return;
+                }
             }
             Value diff;
             switch (mValueDirection) {
@@ -580,11 +599,7 @@
     if (numBucketsForward > 1) {
         VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
         // take base again in future good bucket.
-        for (auto& slice : mCurrentSlicedBucket) {
-            for (auto& interval : slice.second) {
-                interval.hasBase = false;
-            }
-        }
+        resetBase();
     }
     VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
          (long long)mCurrentBucketStartTimeNs);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 9fe84dc..36ae214 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -148,6 +148,9 @@
 
     void pullAndMatchEventsLocked(const int64_t timestampNs);
 
+    // Reset diff base and mHasGlobalBase
+    void resetBase();
+
     static const size_t kBucketSize = sizeof(ValueBucket{});
 
     const size_t mDimensionSoftLimit;
@@ -164,6 +167,18 @@
 
     const bool mSkipZeroDiffOutput;
 
+    // If true, use a zero value as base to compute the diff.
+    // This is used for new keys which are present in the new data but was not
+    // present in the base data.
+    // The default base will only be used if we have a global base.
+    const bool mUseZeroDefaultBase;
+
+    // For pulled metrics, this is always set to true whenever a pull succeeds.
+    // It is set to false when a pull fails, or upon condition change to false.
+    // This is used to decide if we have the right base data to compute the
+    // diff against.
+    bool mHasGlobalBase;
+
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
@@ -185,6 +200,8 @@
     FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
     FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime);
     FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
+    FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
+    FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 32ee5af..a6f27c8 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -219,6 +219,8 @@
   optional DimensionsValue dimensions_path_in_what = 11;
 
   optional DimensionsValue dimensions_path_in_condition = 12;
+
+  optional bool is_active = 13;
 }
 
 message UidMapping {
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 61854a4..f485185 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -274,6 +274,8 @@
 
   optional bool use_diff = 12;
 
+  optional bool use_zero_default_base = 15 [default = false];
+
   enum ValueDirection {
       UNKNOWN = 0;
       INCREASING = 1;
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 2b0285b..370c36c 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -119,7 +119,8 @@
 
     ConfigMetricsReport report = GetReports(service.mProcessor, start + 3);
     // Expect no metrics since the bucket has not finished yet.
-    EXPECT_EQ(0, report.metrics_size());
+    EXPECT_EQ(1, report.metrics_size());
+    EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
 }
 
 TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
@@ -138,7 +139,8 @@
     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
 
     ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
-    EXPECT_EQ(0, report.metrics_size());
+    EXPECT_EQ(1, report.metrics_size());
+    EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
 }
 
 TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 6d1317c..16be3d7 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -262,7 +262,8 @@
     // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as
     // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by
     // itself.
-    EXPECT_EQ(0, reports.reports(0).metrics_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size());
 }
 
 TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) {
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 44aa00b..1bd34f5 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -1468,6 +1468,219 @@
     EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
 }
 
+/*
+ * Tests pulled atoms with no conditions
+ */
+TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.mutable_dimensions_in_what()->set_field(tagId);
+    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+    metric.set_use_zero_default_base(true);
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+                event->write(1);
+                event->write(3);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    auto iter = valueProducer.mCurrentSlicedBucket.begin();
+    auto& interval1 = iter->second[0];
+    EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, interval1.hasBase);
+    EXPECT_EQ(3, interval1.base.long_value);
+    EXPECT_EQ(false, interval1.hasValue);
+    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    vector<shared_ptr<LogEvent>> allData;
+
+    allData.clear();
+    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event1->write(2);
+    event1->write(4);
+    event1->init();
+    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event2->write(1);
+    event2->write(11);
+    event2->init();
+    allData.push_back(event1);
+    allData.push_back(event2);
+
+    valueProducer.onDataPulled(allData);
+    EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(true, interval1.hasBase);
+    EXPECT_EQ(11, interval1.base.long_value);
+    EXPECT_EQ(true, interval1.hasValue);
+    EXPECT_EQ(8, interval1.value.long_value);
+
+    auto it = valueProducer.mCurrentSlicedBucket.begin();
+    for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) {
+        if (it != iter) {
+            break;
+        }
+    }
+    EXPECT_TRUE(it != iter);
+    auto& interval2 = it->second[0];
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, interval2.hasBase);
+    EXPECT_EQ(4, interval2.base.long_value);
+    EXPECT_EQ(true, interval2.hasValue);
+    EXPECT_EQ(4, interval2.value.long_value);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+}
+
+/*
+ * Tests pulled atoms with no conditions
+ */
+TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.mutable_dimensions_in_what()->set_field(tagId);
+    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+    metric.set_use_zero_default_base(true);
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+                event->write(1);
+                event->write(3);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    auto iter = valueProducer.mCurrentSlicedBucket.begin();
+    auto& interval1 = iter->second[0];
+    EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, interval1.hasBase);
+    EXPECT_EQ(3, interval1.base.long_value);
+    EXPECT_EQ(false, interval1.hasValue);
+    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    vector<shared_ptr<LogEvent>> allData;
+
+    allData.clear();
+    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event1->write(2);
+    event1->write(4);
+    event1->init();
+    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event2->write(1);
+    event2->write(11);
+    event2->init();
+    allData.push_back(event1);
+    allData.push_back(event2);
+
+    valueProducer.onDataPulled(allData);
+    EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(true, interval1.hasBase);
+    EXPECT_EQ(11, interval1.base.long_value);
+    EXPECT_EQ(true, interval1.hasValue);
+    EXPECT_EQ(8, interval1.value.long_value);
+
+    auto it = valueProducer.mCurrentSlicedBucket.begin();
+    for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) {
+        if (it != iter) {
+            break;
+        }
+    }
+    EXPECT_TRUE(it != iter);
+    auto& interval2 = it->second[0];
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, interval2.hasBase);
+    EXPECT_EQ(4, interval2.base.long_value);
+    EXPECT_EQ(true, interval2.hasValue);
+    EXPECT_EQ(4, interval2.value.long_value);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+    // next pull somehow did not happen, skip to end of bucket 3
+    allData.clear();
+    event1 = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
+    event1->write(2);
+    event1->write(5);
+    event1->init();
+    allData.push_back(event1);
+    valueProducer.onDataPulled(allData);
+
+    EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(true, interval2.hasBase);
+    EXPECT_EQ(5, interval2.base.long_value);
+    EXPECT_EQ(false, interval2.hasValue);
+    EXPECT_EQ(false, interval1.hasBase);
+    EXPECT_EQ(false, interval1.hasValue);
+    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
+
+    allData.clear();
+    event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
+    event1->write(2);
+    event1->write(13);
+    event1->init();
+    allData.push_back(event1);
+    event2 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
+    event2->write(1);
+    event2->write(5);
+    event2->init();
+    allData.push_back(event2);
+    valueProducer.onDataPulled(allData);
+
+    EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(true, interval2.hasBase);
+    EXPECT_EQ(13, interval2.base.long_value);
+    EXPECT_EQ(true, interval2.hasValue);
+    EXPECT_EQ(8, interval2.value.long_value);
+    EXPECT_EQ(true, interval1.hasBase);
+    EXPECT_EQ(5, interval1.base.long_value);
+    EXPECT_EQ(true, interval1.hasValue);
+    EXPECT_EQ(5, interval1.value.long_value);
+    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 01c7028..bacb991 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1639,10 +1639,6 @@
 Landroid/widget/QuickContactBadge$QueryHandler;-><init>(Landroid/widget/QuickContactBadge;Landroid/content/ContentResolver;)V
 Landroid/widget/RelativeLayout$DependencyGraph$Node;-><init>()V
 Landroid/widget/ScrollBarDrawable;-><init>()V
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->values()[Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
 Lcom/android/ims/ImsCall;->deflect(Ljava/lang/String;)V
 Lcom/android/ims/ImsCall;->isMultiparty()Z
 Lcom/android/ims/ImsCall;->reject(I)V
@@ -1849,13 +1845,16 @@
 Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z
 Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
 Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
-Lcom/android/internal/location/ILocationProvider;->disable()V
-Lcom/android/internal/location/ILocationProvider;->enable()V
-Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
 Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
 Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
-Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
+Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)V
+Lcom/android/internal/location/ILocationProvider;->setLocationProviderManager(Lcom/android/internal/location/ILocationProviderManager;)V
 Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
+Lcom/android/internal/location/ILocationProviderManager$Stub;-><init>()V
+Lcom/android/internal/location/ILocationProviderManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProviderManager;
+Lcom/android/internal/location/ILocationProviderManager;->onReportLocation(Landroid/location/Location;)V
+Lcom/android/internal/location/ILocationProviderManager;->onSetEnabled(Z)V
+Lcom/android/internal/location/ILocationProviderManager;->onSetProperties(Lcom/android/internal/location/ProviderProperties;)V
 Lcom/android/internal/logging/MetricsLogger;-><init>()V
 Lcom/android/internal/net/LegacyVpnInfo;-><init>()V
 Lcom/android/internal/net/VpnConfig;-><init>()V
diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java
index 28145a0..ac3daaf 100644
--- a/core/java/android/annotation/UnsupportedAppUsage.java
+++ b/core/java/android/annotation/UnsupportedAppUsage.java
@@ -18,29 +18,48 @@
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.CLASS;
 
+import java.lang.annotation.Repeatable;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 /**
- * Indicates that a class member, that is not part of the SDK, is used by apps.
- * Since the member is not part of the SDK, such use is not supported.
+ * Indicates that this non-SDK interface is used by apps. A non-SDK interface is a
+ * class member (field or method) that is not part of the public SDK. Since the
+ * member is not part of the SDK, usage by apps is not supported.
  *
- * <p>This annotation acts as a heads up that changing a given method or field
+ * <h2>If you are an Android App developer</h2>
+ *
+ * This annotation indicates that you may be able to access the member, but that
+ * this access is discouraged and not supported by Android. If there is a value
+ * for {@link #maxTargetSdk()} on the annotation, access will be restricted based
+ * on the {@code targetSdkVersion} value set in your manifest.
+ *
+ * <p>Fields and methods annotated with this are likely to be restricted, changed
+ * or removed in future Android releases. If you rely on these members for
+ * functionality that is not otherwise supported by Android, consider filing a
+ * <a href="http://g.co/dev/appcompat">feature request</a>.
+ *
+ * <h2>If you are an Android OS developer</h2>
+ *
+ * This annotation acts as a heads up that changing a given method or field
  * may affect apps, potentially breaking them when the next Android version is
  * released. In some cases, for members that are heavily used, this annotation
  * may imply restrictions on changes to the member.
  *
  * <p>This annotation also results in access to the member being permitted by the
- * runtime, with a warning being generated in debug builds.
+ * runtime, with a warning being generated in debug builds. Which apps can access
+ * the member is determined by the value of {@link #maxTargetSdk()}.
  *
  * <p>For more details, see go/UnsupportedAppUsage.
  *
  * {@hide}
  */
 @Retention(CLASS)
-@Target({CONSTRUCTOR, METHOD, FIELD})
+@Target({CONSTRUCTOR, METHOD, FIELD, TYPE})
+@Repeatable(UnsupportedAppUsage.Container.class)
 public @interface UnsupportedAppUsage {
 
     /**
@@ -90,4 +109,27 @@
      * @return A dex API signature.
      */
     String expectedSignature() default "";
+
+    /**
+     * The signature of an implicit (not present in the source) member that forms part of the
+     * hiddenapi.
+     *
+     * <p>Allows access to non-SDK API elements that are not represented in the input source to be
+     * managed.
+     *
+     * <p>This must only be used when applying the annotation to a type, using it in any other
+     * situation is an error.
+     *
+     * @return A dex API signature.
+     */
+    String implicitMember() default "";
+
+    /**
+     * Container for {@link UnsupportedAppUsage} that allows it to be applied repeatedly to types.
+     */
+    @Retention(CLASS)
+    @Target(TYPE)
+    @interface Container {
+        UnsupportedAppUsage[] value();
+    }
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index cfda803..61b9d55 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1723,15 +1723,7 @@
         if (mAutoFillResetNeeded) {
             if (!mAutoFillIgnoreFirstResumePause) {
                 View focus = getCurrentFocus();
-                // On Activity rotation situation (mRestoredFromBundle is true),
-                // we should not call on AutofillManager in onResume()
-                // since the next Layout pass will do that.
-                // However, there are both cases where Activity#getCurrentFocus()
-                // will return null (window not preserved) and not null (window IS
-                // preserved), so we need to explicitly check for mRestoredFromBundle
-                // here.
-                if (!mRestoredFromBundle && focus != null
-                        && focus.canNotifyAutofillEnterExitEvent()) {
+                if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
                     // TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
                     // testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
                     // window visibility after recreation is INVISIBLE in onResume() and next frame
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 1cf042f..84c7785 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3676,6 +3676,18 @@
     }
 
     /**
+     * Returns whether switching to provided user was successful.
+     *
+     * @param user the user to switch to.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public boolean switchUser(UserHandle user) {
+        return switchUser(user.getIdentifier());
+    }
+
+    /**
      * Logs out current current foreground user by switching to the system user and stopping the
      * user being switched from.
      * @hide
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 41166dd..fe9b1ff 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -162,7 +162,6 @@
 import com.android.org.conscrypt.TrustedCertificateStore;
 import com.android.server.am.MemInfoDumpProto;
 
-import dalvik.system.BaseDexClassLoader;
 import dalvik.system.CloseGuard;
 import dalvik.system.VMDebug;
 import dalvik.system.VMRuntime;
@@ -2282,6 +2281,15 @@
         }
     }
 
+    /**
+     * Create the context instance base on system resources & display information which used for UI.
+     * @param displayId The ID of the display where the UI is shown.
+     * @see ContextImpl#createSystemUiContext(ContextImpl, int)
+     */
+    public ContextImpl createSystemUiContext(int displayId) {
+        return ContextImpl.createSystemUiContext(getSystemUiContext(), displayId);
+    }
+
     public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
         synchronized (this) {
             getSystemContext().installSystemApplicationInfo(info, classLoader);
@@ -5948,16 +5956,6 @@
             HardwareRenderer.setIsolatedProcess(true);
         }
 
-        // If we use profiles, setup the dex reporter to notify package manager
-        // of any relevant dex loads. The idle maintenance job will use the information
-        // reported to optimize the loaded dex files.
-        // Note that we only need one global reporter per app.
-        // Make sure we do this before calling onCreate so that we can capture the
-        // complete application startup.
-        if (SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
-            BaseDexClassLoader.setReporter(DexLoadReporter.getInstance());
-        }
-
         // Install the Network Security Config Provider. This must happen before the application
         // code is loaded to prevent issues with instances of TLS objects being created before
         // the provider is installed.
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 3069be6..6905cb5 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -476,9 +476,11 @@
     public static final int OP_READ_MEDIA_IMAGES = 85;
     /** @hide Write media of image type. */
     public static final int OP_WRITE_MEDIA_IMAGES = 86;
+    /** @hide Has a legacy (non-isolated) view of storage. */
+    public static final int OP_LEGACY_STORAGE = 87;
     /** @hide */
     @UnsupportedAppUsage
-    public static final int _NUM_OP = 87;
+    public static final int _NUM_OP = 88;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -745,6 +747,8 @@
     public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
     /** @hide Write media of image type. */
     public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
+    /** @hide Has a legacy (non-isolated) view of storage. */
+    public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
 
     // Warning: If an permission is added here it also has to be added to
     // com.android.packageinstaller.permission.utils.EventLogger
@@ -903,6 +907,7 @@
             OP_WRITE_MEDIA_VIDEO,               // WRITE_MEDIA_VIDEO
             OP_READ_MEDIA_IMAGES,               // READ_MEDIA_IMAGES
             OP_WRITE_MEDIA_IMAGES,              // WRITE_MEDIA_IMAGES
+            OP_LEGACY_STORAGE,                  // LEGACY_STORAGE
     };
 
     /**
@@ -996,6 +1001,7 @@
             OPSTR_WRITE_MEDIA_VIDEO,
             OPSTR_READ_MEDIA_IMAGES,
             OPSTR_WRITE_MEDIA_IMAGES,
+            OPSTR_LEGACY_STORAGE,
     };
 
     /**
@@ -1090,6 +1096,7 @@
             "WRITE_MEDIA_VIDEO",
             "READ_MEDIA_IMAGES",
             "WRITE_MEDIA_IMAGES",
+            "LEGACY_STORAGE",
     };
 
     /**
@@ -1185,6 +1192,7 @@
             null, // no permission for OP_WRITE_MEDIA_VIDEO
             Manifest.permission.READ_MEDIA_IMAGES,
             null, // no permission for OP_WRITE_MEDIA_IMAGES
+            null, // no permission for OP_LEGACY_STORAGE
     };
 
     /**
@@ -1280,6 +1288,7 @@
             null, // WRITE_MEDIA_VIDEO
             null, // READ_MEDIA_IMAGES
             null, // WRITE_MEDIA_IMAGES
+            null, // LEGACY_STORAGE
     };
 
     /**
@@ -1374,6 +1383,7 @@
             false, // WRITE_MEDIA_VIDEO
             false, // READ_MEDIA_IMAGES
             false, // WRITE_MEDIA_IMAGES
+            false, // LEGACY_STORAGE
     };
 
     /**
@@ -1467,6 +1477,7 @@
             AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_VIDEO
             AppOpsManager.MODE_ALLOWED, // READ_MEDIA_IMAGES
             AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES
+            AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE
     };
 
     /**
@@ -1564,6 +1575,7 @@
             false, // WRITE_MEDIA_VIDEO
             false, // READ_MEDIA_IMAGES
             false, // WRITE_MEDIA_IMAGES
+            false, // LEGACY_STORAGE
     };
 
     /**
@@ -2490,30 +2502,6 @@
     }
 
     /**
-     * Retrieve current operation state for all applications.
-     *
-     * @param ops The set of operations you are interested in, or null if you want all of them.
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
-    @SystemApi
-    public List<AppOpsManager.PackageOps> getPackagesForOpStrs(String[] ops) {
-        if (ops == null) {
-            return getPackagesForOps(null);
-        }
-        final int[] opCodes = new int[ops.length];
-        for (int i = 0; i < ops.length; ++i) {
-            final Integer opCode = sOpStrToOp.get(ops[i]);
-            if (opCode == null) {
-                opCodes[i] = OP_NONE;
-            } else {
-                opCodes[i] = opCode;
-            }
-        }
-        return getPackagesForOps(opCodes);
-    }
-
-    /**
      * Retrieve current operation state for one application.
      *
      * @param uid The uid of the application of interest.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7312b2c..67d9ad6e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2974,6 +2974,15 @@
     }
 
     @Override
+    public String getWellbeingPackageName() {
+        try {
+            return mPM.getWellbeingPackageName();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
     public boolean isPackageStateProtected(String packageName, int userId) {
         try {
             return mPM.isPackageStateProtected(packageName, userId);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c7a9d99..92cdb20 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2407,16 +2407,28 @@
     /**
      * System Context to be used for UI. This Context has resources that can be themed.
      * Make sure that the created system UI context shares the same LoadedApk as the system context.
+     * @param systemContext The system context which created by
+     *                      {@link #createSystemContext(ActivityThread)}.
+     * @param displayId The ID of the display where the UI is shown.
      */
-    static ContextImpl createSystemUiContext(ContextImpl systemContext) {
+    static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) {
         final LoadedApk packageInfo = systemContext.mPackageInfo;
         ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
                 null, null, 0, null);
-        context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null,
+        context.setResources(createResources(null, packageInfo, null, displayId, null,
                 packageInfo.getCompatibilityInfo()));
+        context.updateDisplay(displayId);
         return context;
     }
 
+    /**
+     * The overloaded method of {@link #createSystemUiContext(ContextImpl, int)}.
+     * Uses {@Code Display.DEFAULT_DISPLAY} as the target display.
+     */
+    static ContextImpl createSystemUiContext(ContextImpl systemContext) {
+        return createSystemUiContext(systemContext, Display.DEFAULT_DISPLAY);
+    }
+
     @UnsupportedAppUsage
     static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
         if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 759763b..d46dbed 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -57,6 +57,7 @@
 
 import com.android.internal.util.ArrayUtils;
 
+import dalvik.system.BaseDexClassLoader;
 import dalvik.system.VMRuntime;
 
 import java.io.File;
@@ -949,6 +950,15 @@
         if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
             return;
         }
+
+        // If we use profiles, setup the dex reporter to notify package manager
+        // of any relevant dex loads. The idle maintenance job will use the information
+        // reported to optimize the loaded dex files.
+        // Note that we only need one global reporter per app.
+        // Make sure we do this before invoking app code for the first time so that we
+        // can capture the complete application startup.
+        BaseDexClassLoader.setReporter(DexLoadReporter.getInstance());
+
         // Only set up profile support if the loaded apk has the same uid as the
         // current process.
         // Currently, we do not support profiling across different apps.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index aa1b5af..b9d5907 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4770,14 +4770,16 @@
                 TemplateBindResult result) {
             boolean largeIconShown = bindLargeIcon(contentView, p);
             boolean replyIconShown = bindReplyIcon(contentView, p);
+            boolean iconContainerVisible = largeIconShown || replyIconShown;
             contentView.setViewVisibility(R.id.right_icon_container,
-                    largeIconShown || replyIconShown ? View.VISIBLE : View.GONE);
+                    iconContainerVisible ? View.VISIBLE : View.GONE);
             int marginEnd = calculateMarginEnd(largeIconShown, replyIconShown);
             contentView.setViewLayoutMarginEnd(R.id.line1, marginEnd);
             contentView.setViewLayoutMarginEnd(R.id.text, marginEnd);
             contentView.setViewLayoutMarginEnd(R.id.progress, marginEnd);
             if (result != null) {
                 result.setIconMarginEnd(marginEnd);
+                result.setRightIconContainerVisible(iconContainerVisible);
             }
         }
 
@@ -6777,7 +6779,8 @@
             mBuilder.setTextViewColorSecondary(contentView, R.id.big_text, p);
             contentView.setViewVisibility(R.id.big_text,
                     TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
-            contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.hasLargeIcon());
+            contentView.setBoolean(R.id.big_text, "setHasImage",
+                    result.isRightIconContainerVisible());
 
             return contentView;
         }
@@ -9914,6 +9917,7 @@
      */
     private static class TemplateBindResult {
         int mIconMarginEnd;
+        boolean mRightIconContainerVisible;
 
         /**
          * Get the margin end that needs to be added to any fields that may overlap
@@ -9923,9 +9927,21 @@
             return mIconMarginEnd;
         }
 
+        /**
+         * Is the icon container visible on the right size because of the reply button or the
+         * right icon.
+         */
+        public boolean isRightIconContainerVisible() {
+            return mRightIconContainerVisible;
+        }
+
         public void setIconMarginEnd(int iconMarginEnd) {
             this.mIconMarginEnd = iconMarginEnd;
         }
+
+        public void setRightIconContainerVisible(boolean iconContainerVisible) {
+            mRightIconContainerVisible = iconContainerVisible;
+        }
     }
 
     private static class StandardTemplateParams {
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 16f6bda..5fa8526 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -685,6 +685,13 @@
      * the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use
      * this API.</p>
      *
+     * <p>To use this API, apps targeting API {@link android.os.Build.VERSION_CODES#Q} or later must
+     * specify the foreground service type using attribute
+     * {@link android.R.attr#foregroundServiceType} in service element of manifest file, otherwise
+     * a SecurityException is thrown when this API is called. Apps targeting API older than
+     * {@link android.os.Build.VERSION_CODES#Q} do not need to specify the foreground service type
+     * </p>
+     *
      * @param id The identifier for this notification as per
      * {@link NotificationManager#notify(int, Notification)
      * NotificationManager.notify(int, Notification)}; must not be 0.
@@ -700,7 +707,7 @@
         } catch (RemoteException ex) {
         }
     }
-    
+
     /**
      * Synonym for {@link #stopForeground(int)}.
      * @param removeNotification If true, the {@link #STOP_FOREGROUND_REMOVE} flag
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index dc2f983..31521a3 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -331,7 +331,7 @@
                     writeTo.flush();
                 }
             } catch (IOException ioe) {
-                throw new RuntimeException("Error while reading/writing ", ioe);
+                Log.w(TAG, "Error while reading/writing to streams");
             } finally {
                 IoUtils.closeQuietly(readFrom);
                 IoUtils.closeQuietly(writeTo);
diff --git a/core/java/android/app/admin/DevicePolicyEventLogger.java b/core/java/android/app/admin/DevicePolicyEventLogger.java
index f39a5f4..c89d868 100644
--- a/core/java/android/app/admin/DevicePolicyEventLogger.java
+++ b/core/java/android/app/admin/DevicePolicyEventLogger.java
@@ -16,6 +16,8 @@
 
 package android.app.admin;
 
+import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.stats.devicepolicy.nano.StringList;
 import android.util.StatsLog;
 
@@ -34,7 +36,7 @@
  *
  * DevicePolicyEventLogger
  *     .createEvent(DevicePolicyEnums.USER_RESTRICTION_CHANGED)
- *     .setAdminPackageName(who)
+ *     .setAdmin(who)
  *     .setString(key)
  *     .setBoolean(enabledFromThisOwner)
  *     .write();
@@ -170,12 +172,20 @@
     /**
      * Sets the package name of the admin application.
      */
-    public DevicePolicyEventLogger setAdminPackageName(String packageName) {
+    public DevicePolicyEventLogger setAdmin(@Nullable String packageName) {
         mAdminPackageName = packageName;
         return this;
     }
 
     /**
+     * Retrieves the package name of the admin application from the {@link ComponentName}.
+     */
+    public DevicePolicyEventLogger setAdmin(@Nullable ComponentName componentName) {
+        mAdminPackageName = (componentName != null ? componentName.getPackageName() : null);
+        return this;
+    }
+
+    /**
      * Returns the package name of the admin application.
      */
     @VisibleForTesting
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3a97284..8e54961 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -49,6 +49,8 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
+import android.net.NetworkUtils;
+import android.net.PrivateDnsConnectivityChecker;
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.Binder;
@@ -79,6 +81,7 @@
 import android.service.restrictions.RestrictionsReceiver;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -1121,6 +1124,64 @@
             "android.app.extra.PROVISIONING_USE_MOBILE_DATA";
 
     /**
+     * A String extra holding the provisioning trigger. It could be one of
+     * {@link #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT}, {@link #PROVISIONING_TRIGGER_QR_CODE},
+     * {@link #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER} or {@link
+     * #PROVISIONING_TRIGGER_UNSPECIFIED}.
+     *
+     * <p>Use in an intent with action {@link
+     * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PROVISIONING_TRIGGER =
+            "android.app.extra.PROVISIONING_TRIGGER";
+
+    /**
+     * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
+     * trigger has not been specified.
+     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
+     * @see #PROVISIONING_TRIGGER_QR_CODE
+     * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER
+     * @hide
+     */
+    @SystemApi
+    public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0;
+
+    /**
+     * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
+     * trigger is cloud enrollment.
+     * @see #PROVISIONING_TRIGGER_QR_CODE
+     * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER
+     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+     * @hide
+     */
+    @SystemApi
+    public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1;
+
+    /**
+     * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
+     * trigger is the QR code scanner.
+     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
+     * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER
+     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+     * @hide
+     */
+    @SystemApi
+    public static final int PROVISIONING_TRIGGER_QR_CODE = 2;
+
+    /**
+     * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
+     * trigger is persistent device owner enrollment.
+     * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
+     * @see #PROVISIONING_TRIGGER_QR_CODE
+     * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+     * @hide
+     */
+    @SystemApi
+    public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3;
+
+    /**
      * This MIME type is used for starting the device owner provisioning.
      *
      * <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -1974,6 +2035,35 @@
     public @interface InstallUpdateCallbackErrorConstants {}
 
     /**
+     * The selected mode has been set successfully. If the mode is
+     * {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} then it implies the supplied host is valid
+     * and reachable.
+     */
+    public static final int PRIVATE_DNS_SET_SUCCESS = 0;
+
+    /**
+     * If the {@code privateDnsHost} provided was of a valid hostname but that host was found
+     * to not support DNS-over-TLS.
+     */
+    public static final int PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING = 1;
+
+    /**
+     * General failure to set the Private DNS mode, not due to one of the reasons listed above.
+     */
+    public static final int PRIVATE_DNS_SET_ERROR_FAILURE_SETTING = 2;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"PRIVATE_DNS_SET_"}, value = {
+            PRIVATE_DNS_SET_SUCCESS,
+            PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING,
+            PRIVATE_DNS_SET_ERROR_FAILURE_SETTING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SetPrivateDnsModeResultConstants {}
+
+    /**
      * Return true if the given administrator component is currently active (enabled) in the system.
      *
      * @param admin The administrator component to check for.
@@ -9839,6 +9929,16 @@
      * Sets the global Private DNS mode and host to be used.
      * May only be called by the device owner.
      *
+     * <p>Note that in case a Private DNS resolver is specified, the method is blocking as it
+     * will perform a connectivity check to the resolver, to ensure it is valid. Because of that,
+     * the method should not be called on any thread that relates to user interaction, such as the
+     * UI thread.
+     *
+     * <p>In case a VPN is used in conjunction with Private DNS resolver, the Private DNS resolver
+     * must be reachable both from within and outside the VPN. Otherwise, the device may lose
+     * the ability to resolve hostnames as system traffic to the resolver may not go through the
+     * VPN.
+     *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with.
      * @param mode Which mode to set - either {@code PRIVATE_DNS_MODE_OPPORTUNISTIC} or
      *             {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME}.
@@ -9848,6 +9948,9 @@
      * @param privateDnsHost The hostname of a server that implements DNS over TLS (RFC7858), if
      *                       {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} was specified as the mode,
      *                       null otherwise.
+     *
+     * @return One of the values in {@link SetPrivateDnsModeResultConstants}.
+     *
      * @throws IllegalArgumentException in the following cases: if a {@code privateDnsHost} was
      * provided but the mode was not {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME}, if the mode
      * specified was {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} but {@code privateDnsHost} does
@@ -9855,15 +9958,23 @@
      *
      * @throws SecurityException if the caller is not the device owner.
      */
-    public void setGlobalPrivateDns(@NonNull ComponentName admin,
+    public int setGlobalPrivateDns(@NonNull ComponentName admin,
             @PrivateDnsMode int mode, @Nullable String privateDnsHost) {
         throwIfParentInstance("setGlobalPrivateDns");
+
         if (mService == null) {
-            return;
+            return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
+        }
+
+        if (mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME && !TextUtils.isEmpty(privateDnsHost)
+                && NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
+            if (!PrivateDnsConnectivityChecker.canConnectToPrivateDnsServer(privateDnsHost)) {
+                return PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING;
+            }
         }
 
         try {
-            mService.setGlobalPrivateDns(admin, mode, privateDnsHost);
+            return mService.setGlobalPrivateDns(admin, mode, privateDnsHost);
         } 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 fcf74ee..1148685 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -415,7 +415,7 @@
 
     boolean isMeteredDataDisabledPackageForUser(in ComponentName admin, String packageName, int userId);
 
-    void setGlobalPrivateDns(in ComponentName admin, int mode, in String privateDnsHost);
+    int setGlobalPrivateDns(in ComponentName admin, int mode, in String privateDnsHost);
     int getGlobalPrivateDnsMode(in ComponentName admin);
     String getGlobalPrivateDnsHost(in ComponentName admin);
 
diff --git a/core/java/android/app/backup/RestoreSession.java b/core/java/android/app/backup/RestoreSession.java
index 2e0f940..79925ec 100644
--- a/core/java/android/app/backup/RestoreSession.java
+++ b/core/java/android/app/backup/RestoreSession.java
@@ -143,8 +143,6 @@
      * @param packages The set of packages for which to attempt a restore.  Regardless of
      *   the contents of the actual back-end dataset named by {@code token}, only
      *   applications mentioned in this list will have their data restored.
-     *
-     * @hide
      */
     public int restoreSome(long token, RestoreObserver observer, BackupManagerMonitor monitor,
             String[] packages) {
@@ -181,8 +179,6 @@
      * @param packages The set of packages for which to attempt a restore.  Regardless of
      *   the contents of the actual back-end dataset named by {@code token}, only
      *   applications mentioned in this list will have their data restored.
-     *
-     * @hide
      */
     public int restoreSome(long token, RestoreObserver observer, String[] packages) {
         return restoreSome(token, observer, null, packages);
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 556ffa24..e84517d 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -200,12 +200,23 @@
     public static final int PRIORITY_SYNC_INITIALIZATION = 20;
 
     /**
-     * Value of {@link #getPriority} for a foreground app (overrides the supplied
+     * Value of {@link #getPriority} for a BFGS app (overrides the supplied
+     * JobInfo priority if it is smaller).
+     * @hide
+     */
+    public static final int PRIORITY_BOUND_FOREGROUND_SERVICE = 30;
+
+    /** @hide For backward compatibility. */
+    @UnsupportedAppUsage
+    public static final int PRIORITY_FOREGROUND_APP = PRIORITY_BOUND_FOREGROUND_SERVICE;
+
+    /**
+     * Value of {@link #getPriority} for a FG service app (overrides the supplied
      * JobInfo priority if it is smaller).
      * @hide
      */
     @UnsupportedAppUsage
-    public static final int PRIORITY_FOREGROUND_APP = 30;
+    public static final int PRIORITY_FOREGROUND_SERVICE = 35;
 
     /**
      * Value of {@link #getPriority} for the current top app (overrides the supplied
@@ -1593,4 +1604,29 @@
             return new JobInfo(this);
         }
     }
+
+    /**
+     * Convert a priority integer into a human readable string for debugging.
+     * @hide
+     */
+    public static String getPriorityString(int priority) {
+        switch (priority) {
+            case PRIORITY_DEFAULT:
+                return PRIORITY_DEFAULT + " [DEFAULT]";
+            case PRIORITY_SYNC_EXPEDITED:
+                return PRIORITY_SYNC_EXPEDITED + " [SYNC_EXPEDITED]";
+            case PRIORITY_SYNC_INITIALIZATION:
+                return PRIORITY_SYNC_INITIALIZATION + " [SYNC_INITIALIZATION]";
+            case PRIORITY_BOUND_FOREGROUND_SERVICE:
+                return PRIORITY_BOUND_FOREGROUND_SERVICE + " [BFGS_APP]";
+            case PRIORITY_FOREGROUND_SERVICE:
+                return PRIORITY_FOREGROUND_SERVICE + " [FGS_APP]";
+            case PRIORITY_TOP_APP:
+                return PRIORITY_TOP_APP + " [TOP_APP]";
+
+                // PRIORITY_ADJ_* are adjustments and not used as real priorities.
+                // No need to convert to strings.
+        }
+        return priority + " [UNKNOWN]";
+    }
 }
diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
index 578a9ae..3cc56ae 100644
--- a/core/java/android/app/job/JobParameters.java
+++ b/core/java/android/app/job/JobParameters.java
@@ -47,6 +47,8 @@
     public static final int REASON_TIMEOUT = JobProtoEnums.STOP_REASON_TIMEOUT; // 3.
     /** @hide */
     public static final int REASON_DEVICE_IDLE = JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
+    /** @hide */
+    public static final int REASON_DEVICE_THERMAL = JobProtoEnums.STOP_REASON_DEVICE_THERMAL; // 5.
 
     /** @hide */
     public static String getReasonName(int reason) {
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 7cb245a..f3b2153 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -22,6 +22,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
@@ -212,6 +213,7 @@
     @NonNull
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     @SystemApi
+    @TestApi
     public List<String> getRoleHolders(@NonNull String roleName) {
         return getRoleHoldersAsUser(roleName, Process.myUserHandle());
     }
@@ -239,6 +241,7 @@
     @NonNull
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     @SystemApi
+    @TestApi
     public List<String> getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) {
         Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
         Preconditions.checkNotNull(user, "user cannot be null");
@@ -273,6 +276,7 @@
      */
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     @SystemApi
+    @TestApi
     public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
             @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
             @NonNull RoleManagerCallback callback) {
@@ -312,6 +316,7 @@
      */
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     @SystemApi
+    @TestApi
     public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
             @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
             @NonNull RoleManagerCallback callback) {
@@ -350,6 +355,7 @@
      */
     @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
     @SystemApi
+    @TestApi
     public void clearRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user,
             @CallbackExecutor @NonNull Executor executor, @NonNull RoleManagerCallback callback) {
         Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
diff --git a/core/java/android/app/role/RoleManagerCallback.java b/core/java/android/app/role/RoleManagerCallback.java
index ca68ebc..d9f0a6c 100644
--- a/core/java/android/app/role/RoleManagerCallback.java
+++ b/core/java/android/app/role/RoleManagerCallback.java
@@ -17,6 +17,7 @@
 package android.app.role;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 /**
  * Callback for a {@link RoleManager} request.
@@ -24,6 +25,7 @@
  * @hide
  */
 @SystemApi
+@TestApi
 public interface RoleManagerCallback {
 
     /**
diff --git a/core/java/android/content/ContentInterface.java b/core/java/android/content/ContentInterface.java
new file mode 100644
index 0000000..3d732eb
--- /dev/null
+++ b/core/java/android/content/ContentInterface.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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * Interface representing calls that can be made to {@link ContentProvider}
+ * instances.
+ * <p>
+ * These methods have been extracted into a general interface so that APIs can
+ * be flexible in accepting either a {@link ContentProvider}, a
+ * {@link ContentResolver}, or a {@link ContentProviderClient}.
+ */
+public interface ContentInterface {
+    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
+            throws RemoteException;
+
+    public @Nullable String getType(@NonNull Uri uri) throws RemoteException;
+
+    public @Nullable String[] getStreamTypes(@NonNull Uri uri, @NonNull String mimeTypeFilter)
+            throws RemoteException;
+
+    public @Nullable Uri canonicalize(@NonNull Uri uri) throws RemoteException;
+
+    public @Nullable Uri uncanonicalize(@NonNull Uri uri) throws RemoteException;
+
+    public boolean refresh(@NonNull Uri uri, @Nullable Bundle args,
+            @Nullable CancellationSignal cancellationSignal) throws RemoteException;
+
+    public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues)
+            throws RemoteException;
+
+    public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] initialValues)
+            throws RemoteException;
+
+    public int delete(@NonNull Uri uri, @Nullable String selection,
+            @Nullable String[] selectionArgs) throws RemoteException;
+
+    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
+            @Nullable String[] selectionArgs) throws RemoteException;
+
+    public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException;
+
+    public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException;
+
+    public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+            @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException;
+
+    public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
+            @NonNull ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException;
+
+    public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+            @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 0f72fd3..5a12e4e 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -105,7 +105,7 @@
  * developer guide.</p>
  * </div>
  */
-public abstract class ContentProvider implements ComponentCallbacks2 {
+public abstract class ContentProvider implements ContentInterface, ComponentCallbacks2 {
 
     private static final String TAG = "ContentProvider";
 
@@ -324,7 +324,7 @@
         }
 
         @Override
-        public ContentProviderResult[] applyBatch(String callingPkg,
+        public ContentProviderResult[] applyBatch(String callingPkg, String authority,
                 ArrayList<ContentProviderOperation> operations)
                 throws OperationApplicationException {
             int numOperations = operations.size();
@@ -356,7 +356,8 @@
             Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch");
             final String original = setCallingPackage(callingPkg);
             try {
-                ContentProviderResult[] results = ContentProvider.this.applyBatch(operations);
+                ContentProviderResult[] results = ContentProvider.this.applyBatch(authority,
+                        operations);
                 if (results != null) {
                     for (int i = 0; i < results.length ; i++) {
                         if (userIds[i] != UserHandle.USER_CURRENT) {
@@ -444,13 +445,13 @@
         }
 
         @Override
-        public Bundle call(
-                String callingPkg, String method, @Nullable String arg, @Nullable Bundle extras) {
+        public Bundle call(String callingPkg, String authority, String method, @Nullable String arg,
+                @Nullable Bundle extras) {
             Bundle.setDefusable(extras, true);
             Trace.traceBegin(TRACE_TAG_DATABASE, "call");
             final String original = setCallingPackage(callingPkg);
             try {
-                return ContentProvider.this.call(method, arg, extras);
+                return ContentProvider.this.call(authority, method, arg, extras);
             } finally {
                 setCallingPackage(original);
                 Trace.traceEnd(TRACE_TAG_DATABASE);
@@ -1255,6 +1256,7 @@
      *            or {@code null}.
      * @return a Cursor or {@code null}.
      */
+    @Override
     public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
             @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
         queryArgs = queryArgs != null ? queryArgs : Bundle.EMPTY;
@@ -1293,6 +1295,7 @@
      * @param uri the URI to query.
      * @return a MIME type string, or {@code null} if there is no type.
      */
+    @Override
     public abstract @Nullable String getType(@NonNull Uri uri);
 
     /**
@@ -1325,6 +1328,7 @@
      * @return Return the canonical representation of <var>url</var>, or null if
      * canonicalization of that Uri is not supported.
      */
+    @Override
     public @Nullable Uri canonicalize(@NonNull Uri url) {
         return null;
     }
@@ -1343,6 +1347,7 @@
      * the data identified by the canonical representation can not be found in
      * the current environment.
      */
+    @Override
     public @Nullable Uri uncanonicalize(@NonNull Uri url) {
         return url;
     }
@@ -1369,6 +1374,7 @@
      *            canceled the refresh request.
      * @return true if the provider actually tried refreshing.
      */
+    @Override
     public boolean refresh(Uri uri, @Nullable Bundle args,
             @Nullable CancellationSignal cancellationSignal) {
         return false;
@@ -1403,6 +1409,7 @@
      *     This must not be {@code null}.
      * @return The URI for the newly inserted item.
      */
+    @Override
     public abstract @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues values);
 
     /**
@@ -1420,6 +1427,7 @@
      *    This must not be {@code null}.
      * @return The number of values that were inserted.
      */
+    @Override
     public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
         int numValues = values.length;
         for (int i = 0; i < numValues; i++) {
@@ -1448,6 +1456,7 @@
      * @return The number of rows affected.
      * @throws SQLException
      */
+    @Override
     public abstract int delete(@NonNull Uri uri, @Nullable String selection,
             @Nullable String[] selectionArgs);
 
@@ -1468,6 +1477,7 @@
      * @param selection An optional filter to match rows to update.
      * @return the number of rows affected.
      */
+    @Override
     public abstract int update(@NonNull Uri uri, @Nullable ContentValues values,
             @Nullable String selection, @Nullable String[] selectionArgs);
 
@@ -1604,6 +1614,7 @@
      * @see #getType(android.net.Uri)
      * @see ParcelFileDescriptor#parseMode(String)
      */
+    @Override
     public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
             @Nullable CancellationSignal signal) throws FileNotFoundException {
         return openFile(uri, mode);
@@ -1723,6 +1734,7 @@
      * @see #openFileHelper(Uri, String)
      * @see #getType(android.net.Uri)
      */
+    @Override
     public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
             @Nullable CancellationSignal signal) throws FileNotFoundException {
         return openAssetFile(uri, mode);
@@ -1789,6 +1801,7 @@
      * @see #openTypedAssetFile(Uri, String, Bundle)
      * @see ClipDescription#compareMimeTypes(String, String)
      */
+    @Override
     public @Nullable String[] getStreamTypes(@NonNull Uri uri, @NonNull String mimeTypeFilter) {
         return null;
     }
@@ -1905,6 +1918,7 @@
      * @see #openAssetFile(Uri, String)
      * @see ClipDescription#compareMimeTypes(String, String)
      */
+    @Override
     public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
             @NonNull String mimeTypeFilter, @Nullable Bundle opts,
             @Nullable CancellationSignal signal) throws FileNotFoundException {
@@ -2059,6 +2073,13 @@
      * @throws OperationApplicationException thrown if any operation fails.
      * @see ContentProviderOperation#apply
      */
+    @Override
+    public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
+            @NonNull ArrayList<ContentProviderOperation> operations)
+                    throws OperationApplicationException {
+        return applyBatch(operations);
+    }
+
     public @NonNull ContentProviderResult[] applyBatch(
             @NonNull ArrayList<ContentProviderOperation> operations)
                     throws OperationApplicationException {
@@ -2088,6 +2109,12 @@
      * @return provider-defined return value.  May be {@code null}, which is also
      *   the default for providers which don't implement any call methods.
      */
+    @Override
+    public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+            @Nullable String arg, @Nullable Bundle extras) {
+        return call(method, arg, extras);
+    }
+
     public @Nullable Bundle call(@NonNull String method, @Nullable String arg,
             @Nullable Bundle extras) {
         return null;
@@ -2133,6 +2160,19 @@
         writer.println("nothing to dump");
     }
 
+    private void validateIncomingAuthority(String authority) throws SecurityException {
+        if (!matchesOurAuthorities(getAuthorityWithoutUserId(authority))) {
+            String message = "The authority " + authority + " does not match the one of the "
+                    + "contentProvider: ";
+            if (mAuthority != null) {
+                message += mAuthority;
+            } else {
+                message += Arrays.toString(mAuthorities);
+            }
+            throw new SecurityException(message);
+        }
+    }
+
     /** @hide */
     @VisibleForTesting
     public Uri validateIncomingUri(Uri uri) throws SecurityException {
@@ -2144,16 +2184,7 @@
                         + mContext.getUserId() + " with a uri belonging to user " + userId);
             }
         }
-        if (!matchesOurAuthorities(getAuthorityWithoutUserId(auth))) {
-            String message = "The authority of the uri " + uri + " does not match the one of the "
-                    + "contentProvider: ";
-            if (mAuthority != null) {
-                message += mAuthority;
-            } else {
-                message += Arrays.toString(mAuthorities);
-            }
-            throw new SecurityException(message);
-        }
+        validateIncomingAuthority(auth);
 
         // Normalize the path by removing any empty path segments, which can be
         // a source of security issues.
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index d315f49..0b5bdb5 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -16,8 +16,12 @@
 
 package android.content;
 
+import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetFileDescriptor;
 import android.database.CrossProcessCursorWrapper;
@@ -65,7 +69,7 @@
  * on the ContentProviderClient those calls are made from until you are finished
  * with the data they have returned.
  */
-public class ContentProviderClient implements AutoCloseable {
+public class ContentProviderClient implements ContentInterface, AutoCloseable {
     private static final String TAG = "ContentProviderClient";
 
     @GuardedBy("ContentProviderClient.class")
@@ -76,6 +80,7 @@
     private final IContentProvider mContentProvider;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final String mPackageName;
+    private final String mAuthority;
     private final boolean mStable;
 
     private final AtomicBoolean mClosed = new AtomicBoolean();
@@ -86,19 +91,39 @@
 
     /** {@hide} */
     @VisibleForTesting
-    public ContentProviderClient(
-            ContentResolver contentResolver, IContentProvider contentProvider, boolean stable) {
+    public ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider,
+            boolean stable) {
+        // Only used for testing, so use a fake authority
+        this(contentResolver, contentProvider, "unknown", stable);
+    }
+
+    /** {@hide} */
+    public ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider,
+            String authority, boolean stable) {
         mContentResolver = contentResolver;
         mContentProvider = contentProvider;
         mPackageName = contentResolver.mPackageName;
 
+        mAuthority = authority;
         mStable = stable;
 
         mCloseGuard.open("close");
     }
 
-    /** {@hide} */
-    public void setDetectNotResponding(long timeoutMillis) {
+    /**
+     * Configure this client to automatically detect and kill the remote
+     * provider when an "application not responding" event is detected.
+     *
+     * @param timeoutMillis the duration for which a pending call is allowed
+     *            block before the remote provider is considered to be
+     *            unresponsive. Set to {@code 0} to allow pending calls to block
+     *            indefinitely with no action taken.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.REMOVE_TASKS)
+    public void setDetectNotResponding(@DurationMillisLong long timeoutMillis) {
         synchronized (ContentProviderClient.class) {
             mAnrTimeout = timeoutMillis;
 
@@ -153,6 +178,7 @@
     }
 
     /** See {@link ContentProvider#query ContentProvider.query} */
+    @Override
     public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
             Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
                     throws RemoteException {
@@ -183,6 +209,7 @@
     }
 
     /** See {@link ContentProvider#getType ContentProvider.getType} */
+    @Override
     public @Nullable String getType(@NonNull Uri url) throws RemoteException {
         Preconditions.checkNotNull(url, "url");
 
@@ -200,6 +227,7 @@
     }
 
     /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
+    @Override
     public @Nullable String[] getStreamTypes(@NonNull Uri url, @NonNull String mimeTypeFilter)
             throws RemoteException {
         Preconditions.checkNotNull(url, "url");
@@ -219,6 +247,7 @@
     }
 
     /** See {@link ContentProvider#canonicalize} */
+    @Override
     public final @Nullable Uri canonicalize(@NonNull Uri url) throws RemoteException {
         Preconditions.checkNotNull(url, "url");
 
@@ -236,6 +265,7 @@
     }
 
     /** See {@link ContentProvider#uncanonicalize} */
+    @Override
     public final @Nullable Uri uncanonicalize(@NonNull Uri url) throws RemoteException {
         Preconditions.checkNotNull(url, "url");
 
@@ -253,6 +283,7 @@
     }
 
     /** See {@link ContentProvider#refresh} */
+    @Override
     public boolean refresh(Uri url, @Nullable Bundle args,
             @Nullable CancellationSignal cancellationSignal) throws RemoteException {
         Preconditions.checkNotNull(url, "url");
@@ -277,6 +308,7 @@
     }
 
     /** See {@link ContentProvider#insert ContentProvider.insert} */
+    @Override
     public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues)
             throws RemoteException {
         Preconditions.checkNotNull(url, "url");
@@ -295,6 +327,7 @@
     }
 
     /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
+    @Override
     public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] initialValues)
             throws RemoteException {
         Preconditions.checkNotNull(url, "url");
@@ -314,6 +347,7 @@
     }
 
     /** See {@link ContentProvider#delete ContentProvider.delete} */
+    @Override
     public int delete(@NonNull Uri url, @Nullable String selection,
             @Nullable String[] selectionArgs) throws RemoteException {
         Preconditions.checkNotNull(url, "url");
@@ -332,6 +366,7 @@
     }
 
     /** See {@link ContentProvider#update ContentProvider.update} */
+    @Override
     public int update(@NonNull Uri url, @Nullable ContentValues values, @Nullable String selection,
             @Nullable String[] selectionArgs) throws RemoteException {
         Preconditions.checkNotNull(url, "url");
@@ -368,6 +403,7 @@
      * you use the {@link ContentResolver#openFileDescriptor
      * ContentResolver.openFileDescriptor} API instead.
      */
+    @Override
     public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode,
             @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
         Preconditions.checkNotNull(url, "url");
@@ -411,6 +447,7 @@
      * you use the {@link ContentResolver#openAssetFileDescriptor
      * ContentResolver.openAssetFileDescriptor} API instead.
      */
+    @Override
     public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode,
             @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
         Preconditions.checkNotNull(url, "url");
@@ -446,8 +483,15 @@
     public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
             @NonNull String mimeType, @Nullable Bundle opts, @Nullable CancellationSignal signal)
                     throws RemoteException, FileNotFoundException {
+        return openTypedAssetFile(uri, mimeType, opts, signal);
+    }
+
+    @Override
+    public final @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+            @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+            @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
         Preconditions.checkNotNull(uri, "uri");
-        Preconditions.checkNotNull(mimeType, "mimeType");
+        Preconditions.checkNotNull(mimeTypeFilter, "mimeTypeFilter");
 
         beforeRemote();
         try {
@@ -458,7 +502,7 @@
                 signal.setRemote(remoteSignal);
             }
             return mContentProvider.openTypedAssetFile(
-                    mPackageName, uri, mimeType, opts, remoteSignal);
+                    mPackageName, uri, mimeTypeFilter, opts, remoteSignal);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -472,12 +516,20 @@
     /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
     public @NonNull ContentProviderResult[] applyBatch(
             @NonNull ArrayList<ContentProviderOperation> operations)
-                    throws RemoteException, OperationApplicationException {
+            throws RemoteException, OperationApplicationException {
+        return applyBatch(mAuthority, operations);
+    }
+
+    /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
+    @Override
+    public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
+            @NonNull ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException {
         Preconditions.checkNotNull(operations, "operations");
 
         beforeRemote();
         try {
-            return mContentProvider.applyBatch(mPackageName, operations);
+            return mContentProvider.applyBatch(mPackageName, authority, operations);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -491,11 +543,19 @@
     /** See {@link ContentProvider#call(String, String, Bundle)} */
     public @Nullable Bundle call(@NonNull String method, @Nullable String arg,
             @Nullable Bundle extras) throws RemoteException {
+        return call(mAuthority, method, arg, extras);
+    }
+
+    /** See {@link ContentProvider#call(String, String, Bundle)} */
+    @Override
+    public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+            @Nullable String arg, @Nullable Bundle extras) throws RemoteException {
+        Preconditions.checkNotNull(authority, "authority");
         Preconditions.checkNotNull(method, "method");
 
         beforeRemote();
         try {
-            return mContentProvider.call(mPackageName, method, arg, extras);
+            return mContentProvider.call(mPackageName, authority, method, arg, extras);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 6bede13..ca657b1 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -174,13 +174,15 @@
                 {
                     data.enforceInterface(IContentProvider.descriptor);
                     String callingPkg = data.readString();
+                    String authority = data.readString();
                     final int numOperations = data.readInt();
                     final ArrayList<ContentProviderOperation> operations =
                             new ArrayList<>(numOperations);
                     for (int i = 0; i < numOperations; i++) {
                         operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
                     }
-                    final ContentProviderResult[] results = applyBatch(callingPkg, operations);
+                    final ContentProviderResult[] results = applyBatch(callingPkg, authority,
+                            operations);
                     reply.writeNoException();
                     reply.writeTypedArray(results, 0);
                     return true;
@@ -267,11 +269,12 @@
                     data.enforceInterface(IContentProvider.descriptor);
 
                     String callingPkg = data.readString();
+                    String authority = data.readString();
                     String method = data.readString();
                     String stringArg = data.readString();
                     Bundle args = data.readBundle();
 
-                    Bundle responseBundle = call(callingPkg, method, stringArg, args);
+                    Bundle responseBundle = call(callingPkg, authority, method, stringArg, args);
 
                     reply.writeNoException();
                     reply.writeBundle(responseBundle);
@@ -507,7 +510,7 @@
     }
 
     @Override
-    public ContentProviderResult[] applyBatch(String callingPkg,
+    public ContentProviderResult[] applyBatch(String callingPkg, String authority,
             ArrayList<ContentProviderOperation> operations)
                     throws RemoteException, OperationApplicationException {
         Parcel data = Parcel.obtain();
@@ -515,6 +518,7 @@
         try {
             data.writeInterfaceToken(IContentProvider.descriptor);
             data.writeString(callingPkg);
+            data.writeString(authority);
             data.writeInt(operations.size());
             for (ContentProviderOperation operation : operations) {
                 operation.writeToParcel(data, 0);
@@ -636,14 +640,15 @@
     }
 
     @Override
-    public Bundle call(String callingPkg, String method, String request, Bundle args)
-            throws RemoteException {
+    public Bundle call(String callingPkg, String authority, String method, String request,
+            Bundle args) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         try {
             data.writeInterfaceToken(IContentProvider.descriptor);
 
             data.writeString(callingPkg);
+            data.writeString(authority);
             data.writeString(method);
             data.writeString(request);
             data.writeBundle(args);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 7d5202d..6704a45 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.UserIdInt;
@@ -52,7 +53,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.text.TextUtils;
@@ -88,7 +88,7 @@
  * <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>
  * developer guide.</p>
  */
-public abstract class ContentResolver {
+public abstract class ContentResolver implements ContentInterface {
     /**
      * Enables logic that supports deprecation of {@code _data} columns,
      * typically by replacing values with fake paths that the OS then offers to
@@ -655,6 +655,7 @@
      * using the content:// scheme.
      * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
      */
+    @Override
     public final @Nullable String getType(@NonNull Uri url) {
         Preconditions.checkNotNull(url, "url");
 
@@ -708,6 +709,7 @@
      * data streams that match the given mimeTypeFilter.  If there are none,
      * null is returned.
      */
+    @Override
     public @Nullable String[] getStreamTypes(@NonNull Uri url, @NonNull String mimeTypeFilter) {
         Preconditions.checkNotNull(url, "url");
         Preconditions.checkNotNull(mimeTypeFilter, "mimeTypeFilter");
@@ -835,6 +837,7 @@
      * @return A Cursor object, which is positioned before the first entry, or null
      * @see Cursor
      */
+    @Override
     public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
             @Nullable String[] projection, @Nullable Bundle queryArgs,
             @Nullable CancellationSignal cancellationSignal) {
@@ -935,6 +938,7 @@
      *
      * @see #uncanonicalize
      */
+    @Override
     public final @Nullable Uri canonicalize(@NonNull Uri url) {
         Preconditions.checkNotNull(url, "url");
         IContentProvider provider = acquireProvider(url);
@@ -971,6 +975,7 @@
      *
      * @see #canonicalize
      */
+    @Override
     public final @Nullable Uri uncanonicalize(@NonNull Uri url) {
         Preconditions.checkNotNull(url, "url");
         IContentProvider provider = acquireProvider(url);
@@ -1005,6 +1010,7 @@
      *            canceled the refresh request.
      * @return true if the provider actually tried refreshing.
      */
+    @Override
     public final boolean refresh(@NonNull Uri url, @Nullable Bundle args,
             @Nullable CancellationSignal cancellationSignal) {
         Preconditions.checkNotNull(url, "url");
@@ -1116,6 +1122,12 @@
         }
     }
 
+    @Override
+    public final @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws FileNotFoundException {
+        return openFileDescriptor(uri, mode, signal);
+    }
+
     /**
      * Open a raw file descriptor to access data under a URI.  This
      * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
@@ -1221,6 +1233,12 @@
         throw new FileNotFoundException("Not a whole file");
     }
 
+    @Override
+    public final @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode,
+            @Nullable CancellationSignal signal) throws FileNotFoundException {
+        return openAssetFileDescriptor(uri, mode, signal);
+    }
+
     /**
      * Open a raw file descriptor to access data under a URI.  This
      * interacts with the underlying {@link ContentProvider#openAssetFile}
@@ -1425,6 +1443,13 @@
         }
     }
 
+    @Override
+    public final @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
+            @NonNull String mimeTypeFilter, @Nullable Bundle opts,
+            @Nullable CancellationSignal signal) throws FileNotFoundException {
+        return openTypedAssetFileDescriptor(uri, mimeTypeFilter, opts, signal);
+    }
+
     /**
      * Open a raw file descriptor to access (potentially type transformed)
      * data from a "content:" URI.  This interacts with the underlying
@@ -1634,6 +1659,7 @@
      *               the field. Passing an empty ContentValues will create an empty row.
      * @return the URL of the newly created row.
      */
+    @Override
     public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
                 @Nullable ContentValues values) {
         Preconditions.checkNotNull(url, "url");
@@ -1672,6 +1698,7 @@
      * @throws RemoteException thrown if a RemoteException is encountered while attempting
      *   to communicate with a remote provider.
      */
+    @Override
     public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
             @NonNull ArrayList<ContentProviderOperation> operations)
                     throws RemoteException, OperationApplicationException {
@@ -1698,6 +1725,7 @@
      *               the field. Passing null will create an empty row.
      * @return the number of newly created rows.
      */
+    @Override
     public final int bulkInsert(@RequiresPermission.Write @NonNull Uri url,
                 @NonNull ContentValues[] values) {
         Preconditions.checkNotNull(url, "url");
@@ -1731,6 +1759,7 @@
                     (excluding the WHERE itself).
      * @return The number of rows deleted.
      */
+    @Override
     public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where,
             @Nullable String[] selectionArgs) {
         Preconditions.checkNotNull(url, "url");
@@ -1766,6 +1795,7 @@
      * @return the number of rows updated.
      * @throws NullPointerException if uri or values are null
      */
+    @Override
     public final int update(@RequiresPermission.Write @NonNull Uri uri,
             @Nullable ContentValues values, @Nullable String where,
             @Nullable String[] selectionArgs) {
@@ -1805,14 +1835,20 @@
      */
     public final @Nullable Bundle call(@NonNull Uri uri, @NonNull String method,
             @Nullable String arg, @Nullable Bundle extras) {
-        Preconditions.checkNotNull(uri, "uri");
+        return call(uri.getAuthority(), method, arg, extras);
+    }
+
+    @Override
+    public final @Nullable Bundle call(@NonNull String authority, @NonNull String method,
+            @Nullable String arg, @Nullable Bundle extras) {
+        Preconditions.checkNotNull(authority, "authority");
         Preconditions.checkNotNull(method, "method");
-        IContentProvider provider = acquireProvider(uri);
+        IContentProvider provider = acquireProvider(authority);
         if (provider == null) {
-            throw new IllegalArgumentException("Unknown URI " + uri);
+            throw new IllegalArgumentException("Unknown authority " + authority);
         }
         try {
-            final Bundle res = provider.call(mPackageName, method, arg, extras);
+            final Bundle res = provider.call(mPackageName, authority, method, arg, extras);
             Bundle.setDefusable(res, true);
             return res;
         } catch (RemoteException e) {
@@ -1918,7 +1954,7 @@
         Preconditions.checkNotNull(uri, "uri");
         IContentProvider provider = acquireProvider(uri);
         if (provider != null) {
-            return new ContentProviderClient(this, provider, true);
+            return new ContentProviderClient(this, provider, uri.getAuthority(), true);
         }
         return null;
     }
@@ -1939,7 +1975,7 @@
         Preconditions.checkNotNull(name, "name");
         IContentProvider provider = acquireProvider(name);
         if (provider != null) {
-            return new ContentProviderClient(this, provider, true);
+            return new ContentProviderClient(this, provider, name, true);
         }
 
         return null;
@@ -1966,7 +2002,7 @@
         Preconditions.checkNotNull(uri, "uri");
         IContentProvider provider = acquireUnstableProvider(uri);
         if (provider != null) {
-            return new ContentProviderClient(this, provider, false);
+            return new ContentProviderClient(this, provider, uri.getAuthority(), false);
         }
 
         return null;
@@ -1993,7 +2029,7 @@
         Preconditions.checkNotNull(name, "name");
         IContentProvider provider = acquireUnstableProvider(name);
         if (provider != null) {
-            return new ContentProviderClient(this, provider, false);
+            return new ContentProviderClient(this, provider, name, false);
         }
 
         return null;
@@ -2943,7 +2979,12 @@
         }
     }
 
-    /** {@hide} */
+    /**
+     * Put the cache with the key.
+     *
+     * @param key the key to add
+     * @param value the value to add
+     */
     public void putCache(Uri key, Bundle value) {
         try {
             getContentService().putCache(mContext.getPackageName(), key, value,
@@ -2953,7 +2994,13 @@
         }
     }
 
-    /** {@hide} */
+    /**
+     * Get the cache with the key.
+     *
+     * @param key the key to get the value
+     * @return the matched value. If the key doesn't exist, will return null.
+     * @see #putCache(Uri, Bundle)
+     */
     public Bundle getCache(Uri key) {
         try {
             final Bundle bundle = getContentService().getCache(mContext.getPackageName(), key,
@@ -3141,7 +3188,14 @@
         return mContext.getUserId();
     }
 
-    /** @hide */
+    /**
+     * Get the system drawable of the mime type.
+     *
+     * @param mimeType the requested mime type
+     * @return the matched drawable
+     * @hide
+     */
+    @SystemApi
     public Drawable getTypeDrawable(String mimeType) {
         return MimeIconUtils.loadMimeIcon(mContext, mimeType);
     }
@@ -3248,10 +3302,10 @@
     }
 
     /** {@hide} */
-    public static Bitmap loadThumbnail(@NonNull ContentProviderClient client, @NonNull Uri uri,
+    public static Bitmap loadThumbnail(@NonNull ContentInterface content, @NonNull Uri uri,
             @NonNull Size size, @Nullable CancellationSignal signal, int allocator)
             throws IOException {
-        Objects.requireNonNull(client);
+        Objects.requireNonNull(content);
         Objects.requireNonNull(uri);
         Objects.requireNonNull(size);
 
@@ -3260,7 +3314,7 @@
         opts.putParcelable(EXTRA_SIZE, Point.convert(size));
 
         return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
-            return client.openTypedAssetFileDescriptor(uri, "image/*", opts, signal);
+            return content.openTypedAssetFile(uri, "image/*", opts, signal);
         }), (ImageDecoder decoder, ImageInfo info, Source source) -> {
             decoder.setAllocator(allocator);
 
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 044ed61..0427c2f 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -60,13 +60,28 @@
     public AssetFileDescriptor openAssetFile(
             String callingPkg, Uri url, String mode, ICancellationSignal signal)
             throws RemoteException, FileNotFoundException;
-    public ContentProviderResult[] applyBatch(String callingPkg,
+
+    @Deprecated
+    public default ContentProviderResult[] applyBatch(String callingPkg,
             ArrayList<ContentProviderOperation> operations)
-                    throws RemoteException, OperationApplicationException;
+                    throws RemoteException, OperationApplicationException {
+        return applyBatch(callingPkg, "unknown", operations);
+    }
+
+    public ContentProviderResult[] applyBatch(String callingPkg, String authority,
+            ArrayList<ContentProviderOperation> operations)
+            throws RemoteException, OperationApplicationException;
+
+    @Deprecated
     @UnsupportedAppUsage
-    public Bundle call(
-            String callingPkg, String method, @Nullable String arg, @Nullable Bundle extras)
-            throws RemoteException;
+    public default Bundle call(String callingPkg, String method,
+            @Nullable String arg, @Nullable Bundle extras) throws RemoteException {
+        return call(callingPkg, "unknown", method, arg, extras);
+    }
+
+    public Bundle call(String callingPkg, String authority, String method,
+            @Nullable String arg, @Nullable Bundle extras) throws RemoteException;
+
     public ICancellationSignal createCancellationSignal() throws RemoteException;
 
     public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b6f9d15..2f0618c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5267,8 +5267,6 @@
      * Used as a boolean extra field in {@link #ACTION_CHOOSER} intents to specify
      * whether to show the chooser or not when there is only one application available
      * to choose from.
-     *
-     * @hide
      */
     public static final String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE =
             "android.intent.extra.AUTO_LAUNCH_SINGLE_CHOICE";
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 1c564f3..740fd7f 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -16,6 +16,8 @@
 package android.content.pm;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
@@ -66,7 +68,30 @@
                     mContext.getIApplicationThread(),
                     mContext.getPackageName(),
                     component,
-                    targetUser);
+                    targetUser.getIdentifier(),
+                    true);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Starts the specified activity of the caller package in the specified profile if the caller
+     * has {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} permission and
+     * both the caller and target user profiles are in the same user group.
+     *
+     * @param component The ComponentName of the activity to launch. It must be exported.
+     * @param targetUser The UserHandle of the profile, must be one of the users returned by
+     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+     *        be thrown.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)
+    public void startAnyActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
+        try {
+            mService.startActivityAsUser(mContext.getIApplicationThread(),
+                    mContext.getPackageName(), component, targetUser.getIdentifier(), false);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index bc2f92a..d2d66cb 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -28,6 +28,6 @@
  */
 interface ICrossProfileApps {
     void startActivityAsUser(in IApplicationThread caller, in String callingPackage,
-            in ComponentName component, in UserHandle user);
+            in ComponentName component, int userId, boolean launchMainActivity);
     List<UserHandle> getTargetUserProfiles(in String callingPackage);
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index dbea821..eea2b88 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -675,6 +675,8 @@
 
     String getSystemTextClassifierPackageName();
 
+    String getWellbeingPackageName();
+
     boolean isPackageStateProtected(String packageName, int userId);
 
     void sendDeviceCustomizationReadyBroadcast();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b7df2bf..0150f6a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2687,6 +2687,16 @@
             "android.software.device_id_attestation";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+     * the requisite kernel support for multinetworking-capable IPsec tunnels.
+     *
+     * <p>This feature implies that the device supports XFRM Interfaces (CONFIG_XFRM_INTERFACE), or
+     * VTIs with kernel patches allowing updates of output/set mark via UPDSA.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
+
+    /**
      * Extra field name for the URI to a verification file. Passed to a package
      * verifier.
      *
@@ -6424,6 +6434,17 @@
     }
 
     /**
+     * @return the wellbeing app package name, or null if it's not defined by the OEM.
+     *
+     * @hide
+     */
+    @SystemApi
+    public String getWellbeingPackageName() {
+        throw new UnsupportedOperationException(
+                "getWellbeingPackageName not implemented in subclass");
+    }
+
+    /**
      * @return whether a given package's state is protected, e.g. package cannot be disabled,
      *         suspended, hidden or force stopped.
      *
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index b49c447..43c0222 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -53,6 +53,7 @@
     public static final int PACKAGE_BROWSER = 4;
     public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
     public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
+    public static final int PACKAGE_WELLBEING = 7;
     @IntDef(value = {
         PACKAGE_SYSTEM,
         PACKAGE_SETUP_WIZARD,
@@ -61,6 +62,7 @@
         PACKAGE_BROWSER,
         PACKAGE_SYSTEM_TEXT_CLASSIFIER,
         PACKAGE_PERMISSION_CONTROLLER,
+        PACKAGE_WELLBEING,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ac18dca..d0de9a1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -5380,6 +5380,10 @@
         s.info.splitName =
                 sa.getNonConfigurationString(R.styleable.AndroidManifestService_splitName, 0);
 
+        s.info.mForegroundServiceType = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestService_foregroundServiceType,
+                ServiceInfo.FOREGROUND_SERVICE_TYPE_UNSPECIFIED);
+
         s.info.flags = 0;
         if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestService_stopWithTask,
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index d9d6b5f..20997d6 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -181,6 +181,17 @@
     @TestApi
     public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 0x10000;
 
+    /**
+     * Additional flag for {${link #protectionLevel}, corresponding
+     * to the <code>wellbeing</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_WELLBEING = 0x20000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
             PROTECTION_FLAG_PRIVILEGED,
@@ -197,6 +208,7 @@
             PROTECTION_FLAG_OEM,
             PROTECTION_FLAG_VENDOR_PRIVILEGED,
             PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
+            PROTECTION_FLAG_WELLBEING,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProtectionFlags {}
@@ -386,6 +398,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
             protLevel += "|textClassifier";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0) {
+            protLevel += "|wellbeing";
+        }
         return protLevel;
     }
 
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index ad2c72274..8300c0c 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -16,10 +16,14 @@
 
 package android.content.pm;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Printer;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Information you can retrieve about a particular application
  * service. This corresponds to information collected from the
@@ -94,6 +98,81 @@
      */
     public int flags;
 
+    /**
+     * The default foreground service type if not been set in manifest file.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_UNSPECIFIED = 0;
+
+    /**
+     * Constant corresponding to <code>sync</code> in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Data(photo, file, account) upload/download, backup/restore, import/export, fetch,
+     * transfer over network between device and cloud.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_SYNC = 1;
+
+    /**
+     * Constant corresponding to <code>mediaPlay</code> in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Music, video, news or other media play.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAY = 2;
+
+    /**
+     * Constant corresponding to <code>phoneCall</code> in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Ongoing phone call or video conference.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 3;
+
+    /**
+     * Constant corresponding to <code>location</code> in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * GPS, map, navigation location update.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 4;
+
+    /**
+     * Constant corresponding to <code>deviceCompanion</code> in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Auto, bluetooth, TV or other devices connection, monitoring and interaction.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION = 5;
+
+    /**
+     * Constant corresponding to <code>ongoingProcess</code> in
+     * the {@link android.R.attr#foregroundServiceType} attribute.
+     * Process that should not be interrupted, including installation, setup, photo
+     * compression etc.
+     */
+    public static final int FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS = 6;
+
+    /**
+     * The enumeration of values for foreground service type.
+     * The foreground service type is set in {@link android.R.attr#foregroundServiceType}
+     * attribute.
+     * @hide
+     */
+    @IntDef(flag = false, prefix = { "FOREGROUND_SERVICE_TYPE_" }, value = {
+            FOREGROUND_SERVICE_TYPE_UNSPECIFIED,
+            FOREGROUND_SERVICE_TYPE_SYNC,
+            FOREGROUND_SERVICE_TYPE_MEDIA_PLAY,
+            FOREGROUND_SERVICE_TYPE_PHONE_CALL,
+            FOREGROUND_SERVICE_TYPE_LOCATION,
+            FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION,
+            FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ForegroundServiceType {}
+
+    /**
+     * The type of foreground service, set in
+     * {@link android.R.attr#foregroundServiceType} attribute, one value in
+     * {@link ForegroundServiceType}
+     * @hide
+     */
+    public @ForegroundServiceType int mForegroundServiceType = FOREGROUND_SERVICE_TYPE_UNSPECIFIED;
+
     public ServiceInfo() {
     }
 
@@ -101,6 +180,15 @@
         super(orig);
         permission = orig.permission;
         flags = orig.flags;
+        mForegroundServiceType = orig.mForegroundServiceType;
+    }
+
+    /**
+     * Return the current foreground service type.
+     * @return the current foreground service type.
+     */
+    public int getForegroundServiceType() {
+        return mForegroundServiceType;
     }
 
     public void dump(Printer pw, String prefix) {
diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java
index 5afc8a9..a607a9f 100644
--- a/core/java/android/content/pm/SharedLibraryNames.java
+++ b/core/java/android/content/pm/SharedLibraryNames.java
@@ -22,15 +22,15 @@
  */
 public class SharedLibraryNames {
 
-    public static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
+    static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
 
-    public static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
+    static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
 
-    public static final String ANDROID_TEST_BASE = "android.test.base";
+    static final String ANDROID_TEST_BASE = "android.test.base";
 
-    public static final String ANDROID_TEST_MOCK = "android.test.mock";
+    static final String ANDROID_TEST_MOCK = "android.test.mock";
 
-    public static final String ANDROID_TEST_RUNNER = "android.test.runner";
+    static final String ANDROID_TEST_RUNNER = "android.test.runner";
 
     public static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
 }
diff --git a/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
index 9490e27..2faf3ad 100644
--- a/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
+++ b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
@@ -22,6 +22,8 @@
  * Interface for communication with the permission presenter service.
  *
  * @hide
+ * @deprecated Only available to keep
+ *             android.permissionpresenterservice.RuntimePermissionPresenterService functional
  */
 oneway interface IRuntimePermissionPresenter {
     void getAppPermissions(String packageName, in RemoteCallback callback);
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java b/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java
index 352e8ad..9fa863c 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.java
@@ -29,7 +29,11 @@
  * coarse and fine platform permissions.
  *
  * @hide
+ *
+ * @deprecated Not used anymore. Use {@link android.permission.RuntimePermissionPresentationInfo}
+ * instead
  */
+@Deprecated
 @SystemApi
 public final class RuntimePermissionPresentationInfo implements Parcelable {
     private static final int FLAG_GRANTED = 1 << 0;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 0bdfca7..0c44a56 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
@@ -79,6 +80,7 @@
         mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
         mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
         mNetworkSpecifier = null;
+        mTransportInfo = null;
         mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
         mUids = null;
         mEstablishingVpnAppUid = INVALID_UID;
@@ -95,6 +97,7 @@
         mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
         mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
         mNetworkSpecifier = nc.mNetworkSpecifier;
+        mTransportInfo = nc.mTransportInfo;
         mSignalStrength = nc.mSignalStrength;
         setUids(nc.mUids); // Will make the defensive copy
         mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
@@ -874,6 +877,7 @@
     }
 
     private NetworkSpecifier mNetworkSpecifier = null;
+    private TransportInfo mTransportInfo = null;
 
     /**
      * Sets the optional bearer specific network specifier.
@@ -899,6 +903,19 @@
     }
 
     /**
+     * Sets the optional transport specific information.
+     *
+     * @param transportInfo A concrete, parcelable framework class that extends
+     * {@link TransportInfo}.
+     * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
+     */
+    public NetworkCapabilities setTransportInfo(TransportInfo transportInfo) {
+        mTransportInfo = transportInfo;
+        return this;
+    }
+
+    /**
      * Gets the optional bearer specific network specifier.
      *
      * @return The optional {@link NetworkSpecifier} specifying the bearer specific network
@@ -910,6 +927,19 @@
         return mNetworkSpecifier;
     }
 
+    /**
+     * Returns a transport-specific information container. The application may cast this
+     * container to a concrete sub-class based on its knowledge of the network request. The
+     * application should be able to deal with a {@code null} return value or an invalid case,
+     * e.g. use {@code instanceof} operation to verify expected type.
+     *
+     * @return A concrete implementation of the {@link TransportInfo} class or null if not
+     * available for the network.
+     */
+    @Nullable public TransportInfo getTransportInfo() {
+        return mTransportInfo;
+    }
+
     private void combineSpecifiers(NetworkCapabilities nc) {
         if (mNetworkSpecifier != null && !mNetworkSpecifier.equals(nc.mNetworkSpecifier)) {
             throw new IllegalStateException("Can't combine two networkSpecifiers");
@@ -926,6 +956,17 @@
         return Objects.equals(mNetworkSpecifier, nc.mNetworkSpecifier);
     }
 
+    private void combineTransportInfos(NetworkCapabilities nc) {
+        if (mTransportInfo != null && !mTransportInfo.equals(nc.mTransportInfo)) {
+            throw new IllegalStateException("Can't combine two TransportInfos");
+        }
+        setTransportInfo(nc.mTransportInfo);
+    }
+
+    private boolean equalsTransportInfo(NetworkCapabilities nc) {
+        return Objects.equals(mTransportInfo, nc.mTransportInfo);
+    }
+
     /**
      * Magic value that indicates no signal strength provided. A request specifying this value is
      * always satisfied.
@@ -1238,6 +1279,7 @@
         combineTransportTypes(nc);
         combineLinkBandwidths(nc);
         combineSpecifiers(nc);
+        combineTransportInfos(nc);
         combineSignalStrength(nc);
         combineUids(nc);
         combineSSIDs(nc);
@@ -1347,6 +1389,7 @@
                 && equalsLinkBandwidths(that)
                 && equalsSignalStrength(that)
                 && equalsSpecifier(that)
+                && equalsTransportInfo(that)
                 && equalsUids(that)
                 && equalsSSID(that));
     }
@@ -1364,7 +1407,8 @@
                 + Objects.hashCode(mNetworkSpecifier) * 23
                 + (mSignalStrength * 29)
                 + Objects.hashCode(mUids) * 31
-                + Objects.hashCode(mSSID) * 37;
+                + Objects.hashCode(mSSID) * 37
+                + Objects.hashCode(mTransportInfo) * 41;
     }
 
     @Override
@@ -1379,6 +1423,7 @@
         dest.writeInt(mLinkUpBandwidthKbps);
         dest.writeInt(mLinkDownBandwidthKbps);
         dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
+        dest.writeParcelable((Parcelable) mTransportInfo, flags);
         dest.writeInt(mSignalStrength);
         dest.writeArraySet(mUids);
         dest.writeString(mSSID);
@@ -1396,6 +1441,7 @@
                 netCap.mLinkUpBandwidthKbps = in.readInt();
                 netCap.mLinkDownBandwidthKbps = in.readInt();
                 netCap.mNetworkSpecifier = in.readParcelable(null);
+                netCap.mTransportInfo = in.readParcelable(null);
                 netCap.mSignalStrength = in.readInt();
                 netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
                         null /* ClassLoader, null for default */);
@@ -1435,6 +1481,9 @@
         if (mNetworkSpecifier != null) {
             sb.append(" Specifier: <").append(mNetworkSpecifier).append(">");
         }
+        if (mTransportInfo != null) {
+            sb.append(" TransportInfo: <").append(mTransportInfo).append(">");
+        }
         if (hasSignalStrength()) {
             sb.append(" SignalStrength: ").append(mSignalStrength);
         }
@@ -1501,6 +1550,9 @@
         if (mNetworkSpecifier != null) {
             proto.write(NetworkCapabilitiesProto.NETWORK_SPECIFIER, mNetworkSpecifier.toString());
         }
+        if (mTransportInfo != null) {
+            // TODO b/120653863: write transport-specific info to proto?
+        }
 
         proto.write(NetworkCapabilitiesProto.CAN_REPORT_SIGNAL_STRENGTH, hasSignalStrength());
         proto.write(NetworkCapabilitiesProto.SIGNAL_STRENGTH, mSignalStrength);
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 5447f59..a00b9a3 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -44,6 +44,7 @@
  *
  * @hide
  */
+// @NotThreadSafe
 public class NetworkStats implements Parcelable {
     private static final String TAG = "NetworkStats";
     /** {@link #iface} value when interface details unavailable. */
@@ -443,6 +444,26 @@
         return entry;
     }
 
+    /**
+     * If @{code dest} is not equal to @{code src}, copy entry from index @{code src} to index
+     * @{code dest}.
+     */
+    private void maybeCopyEntry(int dest, int src) {
+        if (dest == src) return;
+        iface[dest] = iface[src];
+        uid[dest] = uid[src];
+        set[dest] = set[src];
+        tag[dest] = tag[src];
+        metered[dest] = metered[src];
+        roaming[dest] = roaming[src];
+        defaultNetwork[dest] = defaultNetwork[src];
+        rxBytes[dest] = rxBytes[src];
+        rxPackets[dest] = rxPackets[src];
+        txBytes[dest] = txBytes[src];
+        txPackets[dest] = txPackets[src];
+        operations[dest] = operations[src];
+    }
+
     public long getElapsedRealtime() {
         return elapsedRealtime;
     }
@@ -941,21 +962,18 @@
     }
 
     /**
-     * Return all rows except those attributed to the requested UID; doesn't
-     * mutate the original structure.
+     * Remove all rows that match one of specified UIDs.
      */
-    public NetworkStats withoutUids(int[] uids) {
-        final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
-
-        Entry entry = new Entry();
+    public void removeUids(int[] uids) {
+        int nextOutputEntry = 0;
         for (int i = 0; i < size; i++) {
-            entry = getValues(i, entry);
-            if (!ArrayUtils.contains(uids, entry.uid)) {
-                stats.addValues(entry);
+            if (!ArrayUtils.contains(uids, uid[i])) {
+                maybeCopyEntry(nextOutputEntry, i);
+                nextOutputEntry++;
             }
         }
 
-        return stats;
+        size = nextOutputEntry;
     }
 
     /**
diff --git a/core/java/android/net/PrivateDnsConnectivityChecker.java b/core/java/android/net/PrivateDnsConnectivityChecker.java
new file mode 100644
index 0000000..cfd458c
--- /dev/null
+++ b/core/java/android/net/PrivateDnsConnectivityChecker.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * Class for testing connectivity to DNS-over-TLS servers.
+ * {@hide}
+ */
+public class PrivateDnsConnectivityChecker {
+    private static final String TAG = "NetworkUtils";
+
+    private static final int PRIVATE_DNS_PORT = 853;
+    private static final int CONNECTION_TIMEOUT_MS = 5000;
+
+    private PrivateDnsConnectivityChecker() { }
+
+    /**
+     * checks that a provided host can perform a TLS handshake on port 853.
+     * @param hostname host to connect to.
+     */
+    public static boolean canConnectToPrivateDnsServer(@NonNull String hostname) {
+        final SocketFactory factory = SSLSocketFactory.getDefault();
+        TrafficStats.setThreadStatsTag(TrafficStats.TAG_SYSTEM_APP);
+
+        try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
+            socket.setSoTimeout(CONNECTION_TIMEOUT_MS);
+            socket.connect(new InetSocketAddress(hostname, PRIVATE_DNS_PORT));
+            if (!socket.isConnected()) {
+                Log.w(TAG, String.format("Connection to %s failed.", hostname));
+                return false;
+            }
+            socket.startHandshake();
+            Log.w(TAG, String.format("TLS handshake to %s succeeded.", hostname));
+            return true;
+        } catch (IOException e) {
+            Log.w(TAG, String.format("TLS handshake to %s failed.", hostname), e);
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/net/TransportInfo.java b/core/java/android/net/TransportInfo.java
new file mode 100644
index 0000000..b78d3fe
--- /dev/null
+++ b/core/java/android/net/TransportInfo.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+/**
+ * A container for transport-specific capabilities which is returned by
+ * {@link NetworkCapabilities#getTransportInfo()}. Specific networks
+ * may provide concrete implementations of this interface.
+ */
+public interface TransportInfo {
+}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index d09f33b..af3ee09 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -374,11 +374,12 @@
     public abstract String toString();
 
     /**
-     * Return a string representation of the URI that is safe to print
-     * to logs and other places where PII should be avoided.
-     * @hide
+     * Return a string representation of this URI that has common forms of PII redacted,
+     * making it safer to use for logging purposes.  For example, {@code tel:800-466-4411} is
+     * returned as {@code tel:xxx-xxx-xxxx} and {@code http://example.com/path/to/item/} is
+     * returned as {@code http://example.com/...}.
+     * @return the common forms PII redacted string of this URI
      */
-    @UnsupportedAppUsage
     public String toSafeString() {
         String scheme = getScheme();
         String ssp = getSchemeSpecificPart();
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
old mode 100644
new mode 100755
index 292543c..9fea873
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1226,7 +1226,8 @@
      * null (if, for instance, the radio is not currently on).
      */
     public static String getRadioVersion() {
-        return SystemProperties.get(TelephonyProperties.PROPERTY_BASEBAND_VERSION, null);
+        String propVal = SystemProperties.get(TelephonyProperties.PROPERTY_BASEBAND_VERSION);
+        return TextUtils.isEmpty(propVal) ? null : propVal;
     }
 
     private static String getString(String property) {
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8cafbde..7abe913 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -16,14 +16,13 @@
 
 package android.os;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
 import android.opengl.EGL14;
-import android.os.Build;
-import android.os.SystemProperties;
 import android.provider.Settings;
 import android.util.Log;
 
@@ -37,6 +36,11 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /** @hide */
 public class GraphicsEnvironment {
@@ -67,7 +71,7 @@
      */
     public void setup(Context context, Bundle coreSettings) {
         setupGpuLayers(context, coreSettings);
-        setupAngle(context, coreSettings);
+        setupAngle(context, context.getPackageName());
         chooseDriver(context, coreSettings);
     }
 
@@ -192,24 +196,101 @@
         setLayerPaths(mClassLoader, layerPaths);
     }
 
+    enum OpenGlDriverChoice {
+        DEFAULT,
+        NATIVE,
+        ANGLE
+    }
+
+    private static final Map<OpenGlDriverChoice, String> sDriverMap = buildMap();
+    private static Map<OpenGlDriverChoice, String> buildMap() {
+        Map<OpenGlDriverChoice, String> map = new HashMap<>();
+        map.put(OpenGlDriverChoice.DEFAULT, "default");
+        map.put(OpenGlDriverChoice.ANGLE, "angle");
+        map.put(OpenGlDriverChoice.NATIVE, "native");
+
+        return map;
+    }
+
+
+    private static List<String> getGlobalSettingsString(Context context, String globalSetting) {
+        List<String> valueList = null;
+        ContentResolver contentResolver = context.getContentResolver();
+        String settingsValue = Settings.Global.getString(contentResolver, globalSetting);
+
+        if (settingsValue != null) {
+            valueList = new ArrayList<>(Arrays.asList(settingsValue.split(",")));
+        } else {
+            valueList = new ArrayList<>();
+        }
+
+        return valueList;
+    }
+
+    private static int getGlobalSettingsPkgIndex(String pkgName,
+                                                 List<String> globalSettingsDriverPkgs) {
+        for (int pkgIndex = 0; pkgIndex < globalSettingsDriverPkgs.size(); pkgIndex++) {
+            if (globalSettingsDriverPkgs.get(pkgIndex).equals(pkgName)) {
+                return pkgIndex;
+            }
+        }
+
+        return -1;
+    }
+
+    private static String getDriverForPkg(Context context, String packageName) {
+        try {
+            ContentResolver contentResolver = context.getContentResolver();
+            int allUseAngle = Settings.Global.getInt(contentResolver,
+                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE);
+            if (allUseAngle == 1) {
+                return sDriverMap.get(OpenGlDriverChoice.ANGLE);
+            }
+        } catch (Settings.SettingNotFoundException e) {
+            // Do nothing and move on
+        }
+
+        List<String> globalSettingsDriverPkgs =
+                getGlobalSettingsString(context,
+                        Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS);
+        List<String> globalSettingsDriverValues =
+                getGlobalSettingsString(context,
+                        Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES);
+
+        // Make sure we have a good package name
+        if ((packageName == null) || (packageName.isEmpty())) {
+            return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
+        }
+        // Make sure we have good settings to use
+        if (globalSettingsDriverPkgs.isEmpty() || globalSettingsDriverValues.isEmpty()
+                || (globalSettingsDriverPkgs.size() != globalSettingsDriverValues.size())) {
+            Log.w(TAG,
+                    "Global.Settings values are invalid: "
+                        + "globalSettingsDriverPkgs.size = "
+                            + globalSettingsDriverPkgs.size() + ", "
+                        + "globalSettingsDriverValues.size = "
+                            + globalSettingsDriverValues.size());
+            return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
+        }
+
+        int pkgIndex = getGlobalSettingsPkgIndex(packageName, globalSettingsDriverPkgs);
+
+        if (pkgIndex < 0) {
+            return sDriverMap.get(OpenGlDriverChoice.DEFAULT);
+        }
+
+        return globalSettingsDriverValues.get(pkgIndex);
+    }
+
     /**
      * Pass ANGLE details down to trigger enable logic
      */
-    private static void setupAngle(Context context, Bundle coreSettings) {
+    private void setupAngle(Context context, String packageName) {
+        String devOptIn = getDriverForPkg(context, packageName);
 
-        String angleEnabledApp =
-                coreSettings.getString(Settings.Global.ANGLE_ENABLED_APP);
-
-        String packageName = context.getPackageName();
-
-        boolean devOptIn = false;
-        if ((angleEnabledApp != null && packageName != null)
-                && (!angleEnabledApp.isEmpty() && !packageName.isEmpty())
-                && angleEnabledApp.equals(packageName)) {
-
-            Log.i(TAG, packageName + " opted in for ANGLE via Developer Setting");
-
-            devOptIn = true;
+        if (DEBUG) {
+            Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
+                    + "set to: '" + devOptIn + "'");
         }
 
         ApplicationInfo angleInfo;
@@ -303,8 +384,7 @@
         // Further opt-in logic is handled in native, so pass relevant info down
         // TODO: Move the ANGLE selection logic earlier so we don't need to keep these
         //       file descriptors open.
-        setAngleInfo(paths, packageName, devOptIn,
-                     rulesFd, rulesOffset, rulesLength);
+        setAngleInfo(paths, packageName, devOptIn, rulesFd, rulesOffset, rulesLength);
     }
 
     /**
@@ -452,6 +532,6 @@
     private static native void setDebugLayersGLES(String layers);
     private static native void setDriverPath(String path);
     private static native void setAngleInfo(String path, String appPackage,
-                                            boolean devOptIn, FileDescriptor rulesFd,
+                                            String devOptIn, FileDescriptor rulesFd,
                                             long rulesOffset, long rulesLength);
 }
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index 5499181..eee2b52 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -70,6 +70,7 @@
             TYPE_BCL_VOLTAGE,
             TYPE_BCL_CURRENT,
             TYPE_BCL_PERCENTAGE,
+            TYPE_NPU,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Type {}
@@ -85,6 +86,7 @@
     public static final int TYPE_BCL_VOLTAGE = TemperatureType.BCL_VOLTAGE;
     public static final int TYPE_BCL_CURRENT = TemperatureType.BCL_CURRENT;
     public static final int TYPE_BCL_PERCENTAGE = TemperatureType.BCL_PERCENTAGE;
+    public static final int TYPE_NPU = TemperatureType.NPU;
 
     /**
      * Verify a valid temperature type.
@@ -92,7 +94,7 @@
      * @return true if a temperature type is valid otherwise false.
      */
     public static boolean isValidType(@Type int type) {
-        return type >= TYPE_UNKNOWN && type <= TYPE_BCL_PERCENTAGE;
+        return type >= TYPE_UNKNOWN && type <= TYPE_NPU;
     }
 
     /**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 86f81d8..17ce79b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1225,6 +1225,9 @@
      * system user hasn't been unlocked yet, or {@link #DISALLOW_USER_SWITCH} is set.
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
     public boolean canSwitchUsers() {
         boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt(
                 mContext.getContentResolver(),
@@ -2646,6 +2649,19 @@
     }
 
     /**
+     * Removes a user and all associated data.
+     *
+     * @param user the user that needs to be removed.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public boolean removeUser(UserHandle user) {
+        return removeUser(user.getIdentifier());
+    }
+
+
+    /**
      * Similar to {@link #removeUser(int)} except bypassing the checking of
      * {@link UserManager#DISALLOW_REMOVE_USER}
      * or {@link UserManager#DISALLOW_REMOVE_MANAGED_PROFILE}.
diff --git a/core/java/android/permission/IRuntimePermissionPresenter.aidl b/core/java/android/permission/IRuntimePermissionPresenter.aidl
new file mode 100644
index 0000000..e95428a
--- /dev/null
+++ b/core/java/android/permission/IRuntimePermissionPresenter.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import android.os.RemoteCallback;
+
+/**
+ * Interface for communication with the permission presenter service.
+ *
+ * @hide
+ */
+oneway interface IRuntimePermissionPresenter {
+    void getAppPermissions(String packageName, in RemoteCallback callback);
+    void revokeRuntimePermission(String packageName, String permissionName);
+    void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
+            boolean countSystem, in RemoteCallback callback);
+}
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl b/core/java/android/permission/RuntimePermissionPresentationInfo.aidl
similarity index 94%
rename from core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl
rename to core/java/android/permission/RuntimePermissionPresentationInfo.aidl
index f96a32f..533c1ff 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresentationInfo.aidl
+++ b/core/java/android/permission/RuntimePermissionPresentationInfo.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.content.pm.permission;
+package android.permission;
 
 parcelable RuntimePermissionPresentationInfo;
diff --git a/core/java/android/permission/RuntimePermissionPresentationInfo.java b/core/java/android/permission/RuntimePermissionPresentationInfo.java
new file mode 100644
index 0000000..ed7b05c
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionPresentationInfo.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information about how a runtime permission
+ * is to be presented in the UI. A single runtime permission
+ * presented to the user may correspond to multiple platform defined
+ * permissions, e.g. the location permission may control both the
+ * coarse and fine platform permissions.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RuntimePermissionPresentationInfo implements Parcelable {
+    private static final int FLAG_GRANTED = 1 << 0;
+    private static final int FLAG_STANDARD = 1 << 1;
+
+    private final CharSequence mLabel;
+    private final int mFlags;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param label The permission label.
+     * @param granted Whether the permission is granted.
+     * @param standard Whether this is a platform-defined permission.
+     */
+    public RuntimePermissionPresentationInfo(CharSequence label,
+            boolean granted, boolean standard) {
+        mLabel = label;
+        int flags = 0;
+        if (granted) {
+            flags |= FLAG_GRANTED;
+        }
+        if (standard) {
+            flags |= FLAG_STANDARD;
+        }
+        mFlags = flags;
+    }
+
+    private RuntimePermissionPresentationInfo(Parcel parcel) {
+        mLabel = parcel.readCharSequence();
+        mFlags = parcel.readInt();
+    }
+
+    /**
+     * @return Whether the permission is granted.
+     */
+    public boolean isGranted() {
+        return (mFlags & FLAG_GRANTED) != 0;
+    }
+
+    /**
+     * @return Whether the permission is platform-defined.
+     */
+    public boolean isStandard() {
+        return (mFlags & FLAG_STANDARD) != 0;
+    }
+
+    /**
+     * Gets the permission label.
+     *
+     * @return The label.
+     */
+    public @NonNull CharSequence getLabel() {
+        return mLabel;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeCharSequence(mLabel);
+        parcel.writeInt(mFlags);
+    }
+
+    public static final Creator<RuntimePermissionPresentationInfo> CREATOR =
+            new Creator<RuntimePermissionPresentationInfo>() {
+        public RuntimePermissionPresentationInfo createFromParcel(Parcel source) {
+            return new RuntimePermissionPresentationInfo(source);
+        }
+
+        public RuntimePermissionPresentationInfo[] newArray(int size) {
+            return new RuntimePermissionPresentationInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/permission/RuntimePermissionPresenter.java
similarity index 71%
rename from core/java/android/content/pm/permission/RuntimePermissionPresenter.java
rename to core/java/android/permission/RuntimePermissionPresenter.java
index 73addb7..3ce3c9d 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/permission/RuntimePermissionPresenter.java
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package android.content.pm.permission;
+package android.permission;
 
+import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
@@ -30,7 +31,6 @@
 import android.os.Message;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
-import android.permissionpresenterservice.RuntimePermissionPresenterService;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -60,20 +60,32 @@
      * @hide
      */
     public static final String KEY_RESULT =
-            "android.content.pm.permission.RuntimePermissionPresenter.key.result";
+            "android.permission.RuntimePermissionPresenter.key.result";
 
     /**
-     * Listener for delivering a result.
+     * Listener for delivering the result of {@link #getAppPermissions}.
      */
-    public static abstract class OnResultCallback {
+    public interface OnGetAppPermissionResultCallback {
         /**
-         * The result for {@link #getAppPermissions(String, OnResultCallback, Handler)}.
+         * The result for {@link #getAppPermissions(String, OnGetAppPermissionResultCallback,
+         * Handler)}.
+         *
          * @param permissions The permissions list.
          */
-        public void onGetAppPermissions(@NonNull
-                List<RuntimePermissionPresentationInfo> permissions) {
-            /* do nothing - stub */
-        }
+        void onGetAppPermissions(@NonNull List<RuntimePermissionPresentationInfo> permissions);
+    }
+
+    /**
+     * Listener for delivering the result of {@link #countPermissionApps}.
+     */
+    public interface OnCountPermissionAppsResultCallback {
+        /**
+         * The result for {@link #countPermissionApps(List, boolean,
+         * OnCountPermissionAppsResultCallback, Handler)}.
+         *
+         * @param numApps The number of apps that have one of the permissions
+         */
+        void onCountPermissionApps(int numApps);
     }
 
     private static final Object sLock = new Object();
@@ -110,7 +122,7 @@
      * @param handler Handler on which to invoke the callback.
      */
     public void getAppPermissions(@NonNull String packageName,
-            @NonNull OnResultCallback callback, @Nullable Handler handler) {
+            @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
         checkNotNull(packageName);
         checkNotNull(callback);
 
@@ -133,6 +145,25 @@
                 mRemoteService, packageName, permissionName));
     }
 
+    /**
+     * Count how many apps have one of a set of permissions.
+     *
+     * @param permissionNames The permissions the app might have
+     * @param countOnlyGranted Count an app only if the permission is granted to the app
+     * @param countSystem Also count system apps
+     * @param callback Callback to receive the result
+     * @param handler Handler on which to invoke the callback
+     */
+    public void countPermissionApps(@NonNull List<String> permissionNames,
+            boolean countOnlyGranted, boolean countSystem,
+            @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
+        checkCollectionElementsNotNull(permissionNames, "permissionNames");
+        checkNotNull(callback);
+
+        mRemoteService.processMessage(obtainMessage(RemoteService::countPermissionApps,
+                mRemoteService, permissionNames, countOnlyGranted, countSystem, callback, handler));
+    }
+
     private static final class RemoteService
             extends Handler implements ServiceConnection {
         private static final long UNBIND_TIMEOUT_MILLIS = 10000;
@@ -152,7 +183,7 @@
         @GuardedBy("mLock")
         private boolean mBound;
 
-        public RemoteService(Context context) {
+        RemoteService(Context context) {
             super(context.getMainLooper(), null, false);
             mContext = context;
         }
@@ -188,7 +219,7 @@
         }
 
         private void getAppPermissions(@NonNull String packageName,
-                @NonNull OnResultCallback callback, @Nullable Handler handler) {
+                @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
             final IRuntimePermissionPresenter remoteInstance;
             synchronized (mLock) {
                 remoteInstance = mRemoteInstance;
@@ -245,6 +276,45 @@
             }
         }
 
+        private void countPermissionApps(@NonNull List<String> permissionNames,
+                boolean countOnlyGranted, boolean countSystem,
+                @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
+            final IRuntimePermissionPresenter remoteInstance;
+
+            synchronized (mLock) {
+                remoteInstance = mRemoteInstance;
+            }
+            if (remoteInstance == null) {
+                return;
+            }
+
+            try {
+                remoteInstance.countPermissionApps(permissionNames, countOnlyGranted, countSystem,
+                        new RemoteCallback(result -> {
+                            final int numApps;
+                            if (result != null) {
+                                numApps = result.getInt(KEY_RESULT);
+                            } else {
+                                numApps = 0;
+                            }
+
+                            if (handler != null) {
+                                handler.post(() -> callback.onCountPermissionApps(numApps));
+                            } else {
+                                callback.onCountPermissionApps(numApps);
+                            }
+                        }, this));
+            } catch (RemoteException re) {
+                Log.e(TAG, "Error counting permission apps", re);
+            }
+
+            scheduleUnbind();
+
+            synchronized (mLock) {
+                scheduleNextMessageIfNeededLocked();
+            }
+        }
+
         private void unbind() {
             synchronized (mLock) {
                 if (mBound) {
diff --git a/core/java/android/permission/RuntimePermissionPresenterService.java b/core/java/android/permission/RuntimePermissionPresenterService.java
new file mode 100644
index 0000000..81ec7be
--- /dev/null
+++ b/core/java/android/permission/RuntimePermissionPresenterService.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+
+import java.util.List;
+
+/**
+ * This service presents information regarding runtime permissions that is
+ * used for presenting them in the UI. Runtime permissions are presented as
+ * a single permission in the UI but may be composed of several individual
+ * permissions.
+ *
+ * @see RuntimePermissionPresenter
+ * @see RuntimePermissionPresentationInfo
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class RuntimePermissionPresenterService extends Service {
+
+    /**
+     * The {@link Intent} action that must be declared as handled by a service
+     * in its manifest for the system to recognize it as a runtime permission
+     * presenter service.
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.permission.RuntimePermissionPresenterService";
+
+    // No need for locking - always set first and never modified
+    private Handler mHandler;
+
+    @Override
+    public final void attachBaseContext(Context base) {
+        super.attachBaseContext(base);
+        mHandler = new Handler(base.getMainLooper());
+    }
+
+    /**
+     * Gets the runtime permissions for an app.
+     *
+     * @param packageName The package for which to query.
+     *
+     * @return descriptions of the runtime permissions of the app
+     */
+    public abstract @NonNull List<RuntimePermissionPresentationInfo> onGetAppPermissions(
+            @NonNull String packageName);
+
+    /**
+     * Revokes the permission {@code permissionName} for app {@code packageName}
+     *
+     * @param packageName The package for which to revoke
+     * @param permissionName The permission to revoke
+     */
+    public abstract void onRevokeRuntimePermission(@NonNull String packageName,
+            @NonNull String permissionName);
+
+    /**
+     * Count how many apps have one of a set of permissions.
+     *
+     * @param permissionNames The permissions the app might have
+     * @param countOnlyGranted Count an app only if the permission is granted to the app
+     * @param countSystem Also count system apps
+     *
+     * @return the number of apps that have one of the permissions
+     */
+    public abstract int onCountPermissionApps(@NonNull List<String> permissionNames,
+            boolean countOnlyGranted, boolean countSystem);
+
+    @Override
+    public final IBinder onBind(Intent intent) {
+        return new IRuntimePermissionPresenter.Stub() {
+            @Override
+            public void getAppPermissions(String packageName, RemoteCallback callback) {
+                checkNotNull(packageName, "packageName");
+                checkNotNull(callback, "callback");
+
+                mHandler.sendMessage(
+                        obtainMessage(
+                                RuntimePermissionPresenterService::getAppPermissions,
+                                RuntimePermissionPresenterService.this, packageName, callback));
+            }
+
+            @Override
+            public void revokeRuntimePermission(String packageName, String permissionName) {
+                checkNotNull(packageName, "packageName");
+                checkNotNull(permissionName, "permissionName");
+
+                mHandler.sendMessage(
+                        obtainMessage(
+                                RuntimePermissionPresenterService::onRevokeRuntimePermission,
+                                RuntimePermissionPresenterService.this, packageName,
+                                permissionName));
+            }
+
+            @Override
+            public void countPermissionApps(List<String> permissionNames, boolean countOnlyGranted,
+                    boolean countSystem, RemoteCallback callback) {
+                checkCollectionElementsNotNull(permissionNames, "permissionNames");
+                checkNotNull(callback, "callback");
+
+                mHandler.sendMessage(
+                        obtainMessage(
+                                RuntimePermissionPresenterService::countPermissionApps,
+                                RuntimePermissionPresenterService.this, permissionNames,
+                                countOnlyGranted, countSystem, callback));
+            }
+        };
+    }
+
+    private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
+        List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
+        if (permissions != null && !permissions.isEmpty()) {
+            Bundle result = new Bundle();
+            result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, permissions);
+            callback.sendResult(result);
+        } else {
+            callback.sendResult(null);
+        }
+    }
+
+    private void countPermissionApps(@NonNull List<String> permissionNames,
+            boolean countOnlyGranted, boolean countSystem, @NonNull RemoteCallback callback) {
+        int numApps = onCountPermissionApps(permissionNames, countOnlyGranted, countSystem);
+
+        Bundle result = new Bundle();
+        result.putInt(RuntimePermissionPresenter.KEY_RESULT, numApps);
+        callback.sendResult(result);
+    }
+}
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index a41a644..2b3f0f5 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -26,7 +26,6 @@
 import android.content.Intent;
 import android.content.pm.permission.IRuntimePermissionPresenter;
 import android.content.pm.permission.RuntimePermissionPresentationInfo;
-import android.content.pm.permission.RuntimePermissionPresenter;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -40,11 +39,13 @@
  * a single permission in the UI but may be composed of several individual
  * permissions.
  *
- * @see RuntimePermissionPresenter
  * @see RuntimePermissionPresentationInfo
  *
  * @hide
+ *
+ * @deprecated use {@link android.permission.RuntimePermissionPresenterService} instead
  */
+@Deprecated
 @SystemApi
 public abstract class RuntimePermissionPresenterService extends Service {
 
@@ -56,6 +57,9 @@
     public static final String SERVICE_INTERFACE =
             "android.permissionpresenterservice.RuntimePermissionPresenterService";
 
+    private static final String KEY_RESULT =
+            "android.content.pm.permission.RuntimePermissionPresenter.key.result";
+
     // No need for locking - always set first and never modified
     private Handler mHandler;
 
@@ -112,7 +116,7 @@
         List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
         if (permissions != null && !permissions.isEmpty()) {
             Bundle result = new Bundle();
-            result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, permissions);
+            result.putParcelableList(KEY_RESULT, permissions);
             callback.sendResult(result);
         } else {
             callback.sendResult(null);
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index a8f3665..865b8f8 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -16,13 +16,14 @@
 
 package android.provider;
 
-
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -694,6 +695,37 @@
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendars");
 
         /**
+         * The content:// style URL for querying Calendars table in the work profile. Appending a
+         * calendar id using {@link ContentUris#withAppendedId(Uri, long)} will
+         * specify a single calendar.
+         *
+         * <p>The following columns are allowed to be queried via this uri:
+         * <ul>
+         * <li>{@link #_ID}</li>
+         * <li>{@link #NAME}</li>
+         * <li>{@link #CALENDAR_DISPLAY_NAME}</li>
+         * <li>{@link #CALENDAR_COLOR}</li>
+         * <li>{@link #VISIBLE}</li>
+         * <li>{@link #CALENDAR_LOCATION}</li>
+         * <li>{@link #CALENDAR_TIME_ZONE}</li>
+         * <li>{@link #IS_PRIMARY}</li>
+         * </ul>
+         *
+         * <p>{@link IllegalArgumentException} will be thrown if there exist columns in the
+         * projection of the query to this uri that are not contained in the above list.
+         *
+         * <p>This uri will return an empty cursor if the calling user is not a parent profile
+         * of a work profile, or cross profile calendar is disabled in Settings, or this uri is
+         * queried from a package that is not whitelisted by profile owner of the work profile via
+         * {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
+         *
+         * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
+         * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
+         */
+        public static final Uri ENTERPRISE_CONTENT_URI =
+                Uri.parse("content://" + AUTHORITY + "/enterprise/calendars");
+
+        /**
          * The default sort order for this table
          */
         public static final String DEFAULT_SORT_ORDER = CALENDAR_DISPLAY_NAME;
@@ -1641,6 +1673,50 @@
                 Uri.parse("content://" + AUTHORITY + "/events");
 
         /**
+         * The content:// style URL for querying Events table in the work profile. Appending an
+         * event id using {@link ContentUris#withAppendedId(Uri, long)} will
+         * specify a single event.
+         *
+         * <p>The following columns are allowed to be queried via this uri:
+         * <ul>
+         * <li>{@link #_ID}</li>
+         * <li>{@link #CALENDAR_ID}</li>
+         * <li>{@link #TITLE}</li>
+         * <li>{@link #EVENT_LOCATION}</li>
+         * <li>{@link #EVENT_COLOR}</li>
+         * <li>{@link #STATUS}</li>
+         * <li>{@link #DTSTART}</li>
+         * <li>{@link #DTEND}</li>
+         * <li>{@link #EVENT_TIMEZONE}</li>
+         * <li>{@link #EVENT_END_TIMEZONE}</li>
+         * <li>{@link #DURATION}</li>
+         * <li>{@link #ALL_DAY}</li>
+         * <li>{@link #AVAILABILITY}</li>
+         * <li>{@link #RRULE}</li>
+         * <li>{@link #RDATE}</li>
+         * <li>{@link #EXRULE}</li>
+         * <li>{@link #EXDATE}</li>
+         * <li>{@link #CALENDAR_DISPLAY_NAME}</li>
+         * <li>{@link #CALENDAR_COLOR}</li>
+         * <li>{@link #VISIBLE}</li>
+         * <li>{@link #CALENDAR_TIME_ZONE}</li>
+         * </ul>
+         *
+         * <p>{@link IllegalArgumentException} will be thrown if there exist columns in the
+         * projection of the query to this uri that are not contained in the above list.
+         *
+         * <p>This uri will return an empty cursor if the calling user is not a parent profile
+         * of a work profile, or cross profile calendar is disabled in Settings, or this uri is
+         * queried from a package that is not whitelisted by profile owner of the work profile via
+         * {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
+         *
+         * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
+         * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
+         */
+        public static final Uri ENTERPRISE_CONTENT_URI =
+                Uri.parse("content://" + AUTHORITY + "/enterprise/events");
+
+        /**
          * The content:// style URI for recurring event exceptions.  Insertions require an
          * appended event ID.  Deletion of exceptions requires both the original event ID and
          * the exception event ID (see {@link Uri.Builder#appendPath}).
@@ -1820,6 +1896,63 @@
             Uri.parse("content://" + AUTHORITY + "/instances/searchbyday");
 
         /**
+         * The content:// style URL for querying an instance range in the work profile.
+         * It supports similar semantics as {@link #CONTENT_URI}.
+         *
+         * <p>The following columns plus the columns that are whitelisted by
+         * {@link Events#ENTERPRISE_CONTENT_URI} are allowed to be queried via this uri:
+         * <ul>
+         * <li>{@link #_ID}</li>
+         * <li>{@link #EVENT_ID}</li>
+         * <li>{@link #BEGIN}</li>
+         * <li>{@link #END}</li>
+         * <li>{@link #START_DAY}</li>
+         * <li>{@link #END_DAY}</li>
+         * <li>{@link #START_MINUTE}</li>
+         * <li>{@link #END_MINUTE}</li>
+         * </ul>
+         *
+         * <p>{@link IllegalArgumentException} will be thrown if there exist columns in the
+         * projection of the query to this uri that are not contained in the above list.
+         *
+         * <p>This uri will return an empty cursor if the calling user is not a parent profile
+         * of a work profile, or cross profile calendar for the work profile is disabled in
+         * Settings, or this uri is queried from a package that is not whitelisted by
+         * profile owner of the work profile via
+         * {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}.
+         *
+         * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
+         * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
+         */
+        public static final Uri ENTERPRISE_CONTENT_URI =
+                Uri.parse("content://" + AUTHORITY + "/enterprise/instances/when");
+
+        /**
+         * The content:// style URL for querying an instance range by Julian
+         * Day in the work profile. It supports similar semantics as {@link #CONTENT_BY_DAY_URI}
+         * and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}.
+         */
+        public static final Uri ENTERPRISE_CONTENT_BY_DAY_URI =
+                Uri.parse("content://" + AUTHORITY + "/enterprise/instances/whenbyday");
+
+        /**
+         * The content:// style URL for querying an instance range with a search
+         * term in the work profile. It supports similar semantics as {@link #CONTENT_SEARCH_URI}
+         * and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}.
+         */
+        public static final Uri ENTERPRISE_CONTENT_SEARCH_URI =
+                Uri.parse("content://" + AUTHORITY + "/enterprise/instances/search");
+
+        /**
+         * The content:// style URL for querying an instance range with a search
+         * term in the work profile. It supports similar semantics as
+         * {@link #CONTENT_SEARCH_BY_DAY_URI} and performs similar checks as
+         * {@link #ENTERPRISE_CONTENT_URI}.
+         */
+        public static final Uri ENTERPRISE_CONTENT_SEARCH_BY_DAY_URI =
+                Uri.parse("content://" + AUTHORITY + "/enterprise/instances/searchbyday");
+
+        /**
          * The default sort order for this table.
          */
         private static final String DEFAULT_SORT_ORDER = "begin ASC";
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 4737577..ff77228 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -24,7 +24,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
-import android.content.ContentProviderClient;
+import android.content.ContentInterface;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -48,10 +48,10 @@
 import android.os.Parcelable;
 import android.os.ParcelableException;
 import android.os.RemoteException;
-import android.os.storage.StorageVolume;
-import android.util.DataUnit;
 import android.util.Log;
 
+import dalvik.system.VMRuntime;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -68,8 +68,7 @@
  * All client apps must hold a valid URI permission grant to access documents,
  * typically issued when a user makes a selection through
  * {@link Intent#ACTION_OPEN_DOCUMENT}, {@link Intent#ACTION_CREATE_DOCUMENT},
- * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}, or
- * {@link StorageVolume#createAccessIntent(String) StorageVolume.createAccessIntent}.
+ * or {@link Intent#ACTION_OPEN_DOCUMENT_TREE}.
  *
  * @see DocumentsProvider
  */
@@ -234,11 +233,6 @@
     public static final String
             ACTION_DOCUMENT_ROOT_SETTINGS = "android.provider.action.DOCUMENT_ROOT_SETTINGS";
 
-    /**
-     * Buffer is large enough to rewind past any EXIF headers.
-     */
-    private static final int THUMBNAIL_BUFFER_SIZE = (int) DataUnit.KIBIBYTES.toBytes(128);
-
     /** {@hide} */
     public static final String EXTERNAL_STORAGE_PROVIDER_AUTHORITY =
             "com.android.externalstorage.documents";
@@ -381,7 +375,7 @@
          * Flag indicating that a document can be represented as a thumbnail.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#getDocumentThumbnail(ContentResolver, Uri,
+         * @see DocumentsContract#getDocumentThumbnail(ContentInterface, Uri,
          *      Point, CancellationSignal)
          * @see DocumentsProvider#openDocumentThumbnail(String, Point,
          *      android.os.CancellationSignal)
@@ -407,7 +401,7 @@
          * Flag indicating that a document is deletable.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#deleteDocument(ContentResolver, Uri)
+         * @see DocumentsContract#deleteDocument(ContentInterface, Uri)
          * @see DocumentsProvider#deleteDocument(String)
          */
         public static final int FLAG_SUPPORTS_DELETE = 1 << 2;
@@ -445,8 +439,7 @@
          * Flag indicating that a document can be renamed.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#renameDocument(ContentResolver, Uri,
-         *      String)
+         * @see DocumentsContract#renameDocument(ContentInterface, Uri, String)
          * @see DocumentsProvider#renameDocument(String, String)
          */
         public static final int FLAG_SUPPORTS_RENAME = 1 << 6;
@@ -456,7 +449,7 @@
          * within the same document provider.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#copyDocument(ContentResolver, Uri, Uri)
+         * @see DocumentsContract#copyDocument(ContentInterface, Uri, Uri)
          * @see DocumentsProvider#copyDocument(String, String)
          */
         public static final int FLAG_SUPPORTS_COPY = 1 << 7;
@@ -466,7 +459,7 @@
          * within the same document provider.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#moveDocument(ContentResolver, Uri, Uri, Uri)
+         * @see DocumentsContract#moveDocument(ContentInterface, Uri, Uri, Uri)
          * @see DocumentsProvider#moveDocument(String, String, String)
          */
         public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
@@ -490,7 +483,7 @@
          * Flag indicating that a document can be removed from a parent.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#removeDocument(ContentResolver, Uri, Uri)
+         * @see DocumentsContract#removeDocument(ContentInterface, Uri, Uri)
          * @see DocumentsProvider#removeDocument(String, String)
          */
         public static final int FLAG_SUPPORTS_REMOVE = 1 << 10;
@@ -684,7 +677,7 @@
          * Flag indicating that this root can be ejected.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#ejectRoot(ContentResolver, Uri)
+         * @see DocumentsContract#ejectRoot(ContentInterface, Uri)
          * @see DocumentsProvider#ejectRoot(String)
          */
         public static final int FLAG_SUPPORTS_EJECT = 1 << 5;
@@ -807,10 +800,7 @@
     /** {@hide} */
     public static final String EXTRA_URI_PERMISSIONS = "uriPermissions";
 
-    /**
-     * @see #createWebLinkIntent(ContentResolver, Uri, Bundle)
-     * {@hide}
-     */
+    /** {@hide} */
     public static final String EXTRA_OPTIONS = "options";
 
     private static final String PATH_ROOT = "root";
@@ -1255,32 +1245,20 @@
      * @see DocumentsProvider#openDocumentThumbnail(String, Point,
      *      android.os.CancellationSignal)
      */
-    public static Bitmap getDocumentThumbnail(
-            ContentResolver resolver, Uri documentUri, Point size, CancellationSignal signal)
-            throws FileNotFoundException {
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                documentUri.getAuthority());
+    public static Bitmap getDocumentThumbnail(ContentInterface content, Uri documentUri, Point size,
+            CancellationSignal signal) throws FileNotFoundException {
         try {
-            return getDocumentThumbnail(client, documentUri, size, signal);
+            return ContentResolver.loadThumbnail(content, documentUri, Point.convert(size), signal,
+                    ImageDecoder.ALLOCATOR_SOFTWARE);
         } catch (Exception e) {
             if (!(e instanceof OperationCanceledException)) {
                 Log.w(TAG, "Failed to load thumbnail for " + documentUri + ": " + e);
             }
-            rethrowIfNecessary(resolver, e);
+            rethrowIfNecessary(e);
             return null;
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
-    /** {@hide} */
-    @UnsupportedAppUsage
-    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_SOFTWARE);
-    }
-
     /**
      * Create a new document with given MIME type and display name.
      *
@@ -1289,33 +1267,24 @@
      * @param displayName name of new document
      * @return newly created document, or {@code null} if failed
      */
-    public static Uri createDocument(ContentResolver resolver, Uri parentDocumentUri,
+    public static Uri createDocument(ContentInterface content, Uri parentDocumentUri,
             String mimeType, String displayName) throws FileNotFoundException {
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                parentDocumentUri.getAuthority());
         try {
-            return createDocument(client, parentDocumentUri, mimeType, displayName);
+            final Bundle in = new Bundle();
+            in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
+            in.putString(Document.COLUMN_MIME_TYPE, mimeType);
+            in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
+
+            final Bundle out = content.call(parentDocumentUri.getAuthority(),
+                    METHOD_CREATE_DOCUMENT, null, in);
+            return out.getParcelable(DocumentsContract.EXTRA_URI);
         } catch (Exception e) {
             Log.w(TAG, "Failed to create document", e);
-            rethrowIfNecessary(resolver, e);
+            rethrowIfNecessary(e);
             return null;
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
-    /** {@hide} */
-    public static Uri createDocument(ContentProviderClient client, Uri parentDocumentUri,
-            String mimeType, String displayName) throws RemoteException {
-        final Bundle in = new Bundle();
-        in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
-        in.putString(Document.COLUMN_MIME_TYPE, mimeType);
-        in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
-
-        final Bundle out = client.call(METHOD_CREATE_DOCUMENT, null, in);
-        return out.getParcelable(DocumentsContract.EXTRA_URI);
-    }
-
 
     /**
      * Test if a document is descendant (child, grandchild, etc) from the given
@@ -1326,39 +1295,29 @@
      * @return if given document is a descendant of the given parent.
      * @see Root#FLAG_SUPPORTS_IS_CHILD
      */
-    public static boolean isChildDocument(ContentResolver resolver, Uri parentDocumentUri,
+    public static boolean isChildDocument(ContentInterface content, Uri parentDocumentUri,
             Uri childDocumentUri) throws FileNotFoundException {
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                parentDocumentUri.getAuthority());
         try {
-            return isChildDocument(client, parentDocumentUri, childDocumentUri);
+            final Bundle in = new Bundle();
+            in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
+            in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, childDocumentUri);
+
+            final Bundle out = content.call(parentDocumentUri.getAuthority(),
+                    METHOD_IS_CHILD_DOCUMENT, null, in);
+            if (out == null) {
+                throw new RemoteException("Failed to get a reponse from isChildDocument query.");
+            }
+            if (!out.containsKey(DocumentsContract.EXTRA_RESULT)) {
+                throw new RemoteException("Response did not include result field..");
+            }
+            return out.getBoolean(DocumentsContract.EXTRA_RESULT);
         } catch (Exception e) {
-            Log.w(TAG, "Failed to query isChildDocument", e);
-            rethrowIfNecessary(resolver, e);
+            Log.w(TAG, "Failed to create document", e);
+            rethrowIfNecessary(e);
             return false;
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
-    /** {@hide} */
-    public static boolean isChildDocument(ContentProviderClient client, Uri parentDocumentUri,
-            Uri childDocumentUri) throws RemoteException {
-
-        final Bundle in = new Bundle();
-        in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
-        in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, childDocumentUri);
-
-        final Bundle out = client.call(METHOD_IS_CHILD_DOCUMENT, null, in);
-        if (out == null) {
-            throw new RemoteException("Failed to get a response from isChildDocument query.");
-        }
-        if (!out.containsKey(DocumentsContract.EXTRA_RESULT)) {
-            throw new RemoteException("Response did not include result field..");
-        }
-        return out.getBoolean(DocumentsContract.EXTRA_RESULT);
-    }
-
     /**
      * Change the display name of an existing document.
      * <p>
@@ -1372,64 +1331,46 @@
      * @return the existing or new document after the rename, or {@code null} if
      *         failed.
      */
-    public static Uri renameDocument(ContentResolver resolver, Uri documentUri,
+    public static Uri renameDocument(ContentInterface content, Uri documentUri,
             String displayName) throws FileNotFoundException {
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                documentUri.getAuthority());
         try {
-            return renameDocument(client, documentUri, displayName);
+            final Bundle in = new Bundle();
+            in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
+            in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
+
+            final Bundle out = content.call(documentUri.getAuthority(),
+                    METHOD_RENAME_DOCUMENT, null, in);
+            final Uri outUri = out.getParcelable(DocumentsContract.EXTRA_URI);
+            return (outUri != null) ? outUri : documentUri;
         } catch (Exception e) {
             Log.w(TAG, "Failed to rename document", e);
-            rethrowIfNecessary(resolver, e);
+            rethrowIfNecessary(e);
             return null;
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
-    /** {@hide} */
-    public static Uri renameDocument(ContentProviderClient client, Uri documentUri,
-            String displayName) throws RemoteException {
-        final Bundle in = new Bundle();
-        in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
-        in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
-
-        final Bundle out = client.call(METHOD_RENAME_DOCUMENT, null, in);
-        final Uri outUri = out.getParcelable(DocumentsContract.EXTRA_URI);
-        return (outUri != null) ? outUri : documentUri;
-    }
-
     /**
      * Delete the given document.
      *
      * @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE}
      * @return if the document was deleted successfully.
      */
-    public static boolean deleteDocument(ContentResolver resolver, Uri documentUri)
+    public static boolean deleteDocument(ContentInterface content, Uri documentUri)
             throws FileNotFoundException {
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                documentUri.getAuthority());
         try {
-            deleteDocument(client, documentUri);
+            final Bundle in = new Bundle();
+            in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
+
+            content.call(documentUri.getAuthority(),
+                    METHOD_DELETE_DOCUMENT, null, in);
             return true;
         } catch (Exception e) {
             Log.w(TAG, "Failed to delete document", e);
-            rethrowIfNecessary(resolver, e);
+            rethrowIfNecessary(e);
             return false;
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
-    /** {@hide} */
-    public static void deleteDocument(ContentProviderClient client, Uri documentUri)
-            throws RemoteException {
-        final Bundle in = new Bundle();
-        in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
-
-        client.call(METHOD_DELETE_DOCUMENT, null, in);
-    }
-
     /**
      * Copies the given document.
      *
@@ -1438,32 +1379,23 @@
      *         document's copy.
      * @return the copied document, or {@code null} if failed.
      */
-    public static Uri copyDocument(ContentResolver resolver, Uri sourceDocumentUri,
+    public static Uri copyDocument(ContentInterface content, Uri sourceDocumentUri,
             Uri targetParentDocumentUri) throws FileNotFoundException {
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                sourceDocumentUri.getAuthority());
         try {
-            return copyDocument(client, sourceDocumentUri, targetParentDocumentUri);
+            final Bundle in = new Bundle();
+            in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
+            in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
+
+            final Bundle out = content.call(sourceDocumentUri.getAuthority(),
+                    METHOD_COPY_DOCUMENT, null, in);
+            return out.getParcelable(DocumentsContract.EXTRA_URI);
         } catch (Exception e) {
             Log.w(TAG, "Failed to copy document", e);
-            rethrowIfNecessary(resolver, e);
+            rethrowIfNecessary(e);
             return null;
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
-    /** {@hide} */
-    public static Uri copyDocument(ContentProviderClient client, Uri sourceDocumentUri,
-            Uri targetParentDocumentUri) throws RemoteException {
-        final Bundle in = new Bundle();
-        in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
-        in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
-
-        final Bundle out = client.call(METHOD_COPY_DOCUMENT, null, in);
-        return out.getParcelable(DocumentsContract.EXTRA_URI);
-    }
-
     /**
      * Moves the given document under a new parent.
      *
@@ -1473,35 +1405,24 @@
      *         document.
      * @return the moved document, or {@code null} if failed.
      */
-    public static Uri moveDocument(ContentResolver resolver, Uri sourceDocumentUri,
+    public static Uri moveDocument(ContentInterface content, Uri sourceDocumentUri,
             Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException {
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                sourceDocumentUri.getAuthority());
         try {
-            return moveDocument(client, sourceDocumentUri, sourceParentDocumentUri,
-                    targetParentDocumentUri);
+            final Bundle in = new Bundle();
+            in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
+            in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, sourceParentDocumentUri);
+            in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
+
+            final Bundle out = content.call(sourceDocumentUri.getAuthority(),
+                    METHOD_MOVE_DOCUMENT, null, in);
+            return out.getParcelable(DocumentsContract.EXTRA_URI);
         } catch (Exception e) {
             Log.w(TAG, "Failed to move document", e);
-            rethrowIfNecessary(resolver, e);
+            rethrowIfNecessary(e);
             return null;
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
-    /** {@hide} */
-    @UnsupportedAppUsage
-    public static Uri moveDocument(ContentProviderClient client, Uri sourceDocumentUri,
-            Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws RemoteException {
-        final Bundle in = new Bundle();
-        in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
-        in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, sourceParentDocumentUri);
-        in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
-
-        final Bundle out = client.call(METHOD_MOVE_DOCUMENT, null, in);
-        return out.getParcelable(DocumentsContract.EXTRA_URI);
-    }
-
     /**
      * Removes the given document from a parent directory.
      *
@@ -1512,58 +1433,40 @@
      * @param parentDocumentUri parent document of the document to remove.
      * @return true if the document was removed successfully.
      */
-    public static boolean removeDocument(ContentResolver resolver, Uri documentUri,
+    public static boolean removeDocument(ContentInterface content, Uri documentUri,
             Uri parentDocumentUri) throws FileNotFoundException {
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                documentUri.getAuthority());
         try {
-            removeDocument(client, documentUri, parentDocumentUri);
+            final Bundle in = new Bundle();
+            in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
+            in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, parentDocumentUri);
+
+            content.call(documentUri.getAuthority(),
+                    METHOD_REMOVE_DOCUMENT, null, in);
             return true;
         } catch (Exception e) {
             Log.w(TAG, "Failed to remove document", e);
-            rethrowIfNecessary(resolver, e);
+            rethrowIfNecessary(e);
             return false;
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
-    /** {@hide} */
-    public static void removeDocument(ContentProviderClient client, Uri documentUri,
-            Uri parentDocumentUri) throws RemoteException {
-        final Bundle in = new Bundle();
-        in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
-        in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, parentDocumentUri);
-
-        client.call(METHOD_REMOVE_DOCUMENT, null, in);
-    }
-
     /**
      * Ejects the given root. It throws {@link IllegalStateException} when ejection failed.
      *
      * @param rootUri root with {@link Root#FLAG_SUPPORTS_EJECT} to be ejected
      */
-    public static void ejectRoot(ContentResolver resolver, Uri rootUri) {
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                rootUri.getAuthority());
+    public static void ejectRoot(ContentInterface content, Uri rootUri) {
         try {
-            ejectRoot(client, rootUri);
+            final Bundle in = new Bundle();
+            in.putParcelable(DocumentsContract.EXTRA_URI, rootUri);
+
+            content.call(rootUri.getAuthority(),
+                    METHOD_EJECT_ROOT, null, in);
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
-    /** {@hide} */
-    public static void ejectRoot(ContentProviderClient client, Uri rootUri)
-            throws RemoteException {
-        final Bundle in = new Bundle();
-        in.putParcelable(DocumentsContract.EXTRA_URI, rootUri);
-
-        client.call(METHOD_EJECT_ROOT, null, in);
-    }
-
     /**
      * Returns metadata associated with the document. The type of metadata returned
      * is specific to the document type. For example the data returned for an image
@@ -1594,67 +1497,22 @@
      * @param documentUri a Document URI
      * @return a Bundle of Bundles.
      */
-    public static Bundle getDocumentMetadata(ContentResolver resolver, Uri documentUri)
+    public static Bundle getDocumentMetadata(ContentInterface content, Uri documentUri)
             throws FileNotFoundException {
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                documentUri.getAuthority());
-
         try {
-            return getDocumentMetadata(client, documentUri);
+            final Bundle in = new Bundle();
+            in.putParcelable(EXTRA_URI, documentUri);
+
+            return content.call(documentUri.getAuthority(),
+                    METHOD_GET_DOCUMENT_METADATA, null, in);
         } catch (Exception e) {
             Log.w(TAG, "Failed to get document metadata");
-            rethrowIfNecessary(resolver, e);
+            rethrowIfNecessary(e);
             return null;
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
     /**
-     * Returns metadata associated with the document. The type of metadata returned
-     * is specific to the document type. For example the data returned for an image
-     * file will likely consist primarily or soley of EXIF metadata.
-     *
-     * <p>The returned {@link Bundle} will contain zero or more entries depending
-     * on the type of data supported by the document provider.
-     *
-     * <ol>
-     * <li>A {@link DocumentsContract.METADATA_TYPES} containing a {@code String[]} value.
-     *     The string array identifies the type or types of metadata returned. Each
-     *     value in the can be used to access a {@link Bundle} of data
-     *     containing that type of data.
-     * <li>An entry each for each type of returned metadata. Each set of metadata is
-     *     itself represented as a bundle and accessible via a string key naming
-     *     the type of data.
-     * </ol>
-     *
-     * <p>Example:
-     * <p><pre><code>
-     *     Bundle metadata = DocumentsContract.getDocumentMetadata(client, imageDocUri, tags);
-     *     if (metadata.containsKey(DocumentsContract.METADATA_EXIF)) {
-     *         Bundle exif = metadata.getBundle(DocumentsContract.METADATA_EXIF);
-     *         int imageLength = exif.getInt(ExifInterface.TAG_IMAGE_LENGTH);
-     *     }
-     * </code></pre>
-     *
-     * @param documentUri a Document URI
-     * @return a Bundle of Bundles.
-     * {@hide}
-     */
-    public static Bundle getDocumentMetadata(
-            ContentProviderClient client, Uri documentUri) throws RemoteException {
-        final Bundle in = new Bundle();
-        in.putParcelable(EXTRA_URI, documentUri);
-
-        final Bundle out = client.call(METHOD_GET_DOCUMENT_METADATA, null, in);
-
-        if (out == null) {
-            throw new RemoteException("Failed to get a response from getDocumentMetadata");
-        }
-        return out;
-    }
-
-    /**
      * Finds the canonical path from the top of the document tree.
      *
      * The {@link Path#getPath()} of the return value contains the document ID
@@ -1667,52 +1525,25 @@
      * @return the path of the document, or {@code null} if failed.
      * @see DocumentsProvider#findDocumentPath(String, String)
      */
-    public static Path findDocumentPath(ContentResolver resolver, Uri treeUri)
+    public static Path findDocumentPath(ContentInterface content, Uri treeUri)
             throws FileNotFoundException {
         checkArgument(isTreeUri(treeUri), treeUri + " is not a tree uri.");
 
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                treeUri.getAuthority());
         try {
-            return findDocumentPath(client, treeUri);
+            final Bundle in = new Bundle();
+            in.putParcelable(DocumentsContract.EXTRA_URI, treeUri);
+
+            final Bundle out = content.call(treeUri.getAuthority(),
+                    METHOD_FIND_DOCUMENT_PATH, null, in);
+            return out.getParcelable(DocumentsContract.EXTRA_RESULT);
         } catch (Exception e) {
             Log.w(TAG, "Failed to find path", e);
-            rethrowIfNecessary(resolver, e);
+            rethrowIfNecessary(e);
             return null;
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
     /**
-     * Finds the canonical path. If uri is a document uri returns path from a root and
-     * its associated root id. If uri is a tree uri returns the path from the top of
-     * the tree. The {@link Path#getPath()} of the return value contains document ID
-     * starts from the top of the tree or the root document to the requested document,
-     * both inclusive.
-     *
-     * Callers can expect the root ID returned from multiple calls to this method is
-     * consistent.
-     *
-     * @param uri uri of the document which path is requested. It can be either a
-     *          plain document uri or a tree uri.
-     * @return the path of the document.
-     * @see DocumentsProvider#findDocumentPath(String, String)
-     *
-     * {@hide}
-     */
-    public static Path findDocumentPath(ContentProviderClient client, Uri uri)
-            throws RemoteException {
-
-        final Bundle in = new Bundle();
-        in.putParcelable(DocumentsContract.EXTRA_URI, uri);
-
-        final Bundle out = client.call(METHOD_FIND_DOCUMENT_PATH, null, in);
-
-        return out.getParcelable(DocumentsContract.EXTRA_RESULT);
-    }
-
-    /**
      * Creates an intent for obtaining a web link for the specified document.
      *
      * <p>Note, that due to internal limitations, if there is already a web link
@@ -1763,40 +1594,29 @@
      * @see DocumentsProvider#createWebLinkIntent(String, Bundle)
      * @see Intent#EXTRA_EMAIL
      */
-    public static IntentSender createWebLinkIntent(ContentResolver resolver, Uri uri,
+    public static IntentSender createWebLinkIntent(ContentInterface content, Uri uri,
             Bundle options) throws FileNotFoundException {
-        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                uri.getAuthority());
         try {
-            return createWebLinkIntent(client, uri, options);
+            final Bundle in = new Bundle();
+            in.putParcelable(DocumentsContract.EXTRA_URI, uri);
+
+            // Options may be provider specific, so put them in a separate bundle to
+            // avoid overriding the Uri.
+            if (options != null) {
+                in.putBundle(EXTRA_OPTIONS, options);
+            }
+
+            final Bundle out = content.call(uri.getAuthority(),
+                    METHOD_CREATE_WEB_LINK_INTENT, null, in);
+            return out.getParcelable(DocumentsContract.EXTRA_RESULT);
         } catch (Exception e) {
             Log.w(TAG, "Failed to create a web link intent", e);
-            rethrowIfNecessary(resolver, e);
+            rethrowIfNecessary(e);
             return null;
-        } finally {
-            ContentProviderClient.releaseQuietly(client);
         }
     }
 
     /**
-     * {@hide}
-     */
-    public static IntentSender createWebLinkIntent(ContentProviderClient client, Uri uri,
-            Bundle options) throws RemoteException {
-        final Bundle in = new Bundle();
-        in.putParcelable(DocumentsContract.EXTRA_URI, uri);
-
-        // Options may be provider specific, so put them in a separate bundle to
-        // avoid overriding the Uri.
-        if (options != null) {
-            in.putBundle(EXTRA_OPTIONS, options);
-        }
-
-        final Bundle out = client.call(METHOD_CREATE_WEB_LINK_INTENT, null, in);
-        return out.getParcelable(DocumentsContract.EXTRA_RESULT);
-    }
-
-    /**
      * Open the given image for thumbnail purposes, using any embedded EXIF
      * thumbnail if available, and providing orientation hints from the parent
      * image.
@@ -1836,10 +1656,9 @@
         return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
     }
 
-    private static void rethrowIfNecessary(ContentResolver resolver, Exception e)
-            throws FileNotFoundException {
+    private static void rethrowIfNecessary(Exception e) throws FileNotFoundException {
         // We only want to throw applications targetting O and above
-        if (resolver.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
+        if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.O) {
             if (e instanceof ParcelableException) {
                 ((ParcelableException) e).maybeRethrow(FileNotFoundException.class);
             } else if (e instanceof RemoteException) {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 0299e41..1451165 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -16,11 +16,15 @@
 
 package android.provider;
 
+import android.annotation.BytesLong;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.AppGlobals;
@@ -102,6 +106,11 @@
     /** {@hide} */
     public static final String GET_MEDIA_URI_CALL = "get_media_uri";
 
+    /** {@hide} */
+    public static final String GET_CONTRIBUTED_MEDIA_CALL = "get_contributed_media";
+    /** {@hide} */
+    public static final String DELETE_CONTRIBUTED_MEDIA_CALL = "delete_contributed_media";
+
     /**
      * This is for internal use by the media scanner only.
      * Name of the (optional) Uri parameter that determines whether to skip deleting
@@ -2865,4 +2874,47 @@
             throw e.rethrowAsRuntimeException();
         }
     }
+
+    /**
+     * Calculate size of media contributed by given package under the calling
+     * user. The meaning of "contributed" means it won't automatically be
+     * deleted when the app is uninstalled.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA)
+    public static @BytesLong long getContributedMediaSize(Context context, String packageName) {
+        try (ContentProviderClient client = context.getContentResolver()
+                .acquireContentProviderClient(AUTHORITY)) {
+            final Bundle in = new Bundle();
+            in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+            final Bundle out = client.call(GET_CONTRIBUTED_MEDIA_CALL, null, in);
+            return out.getLong(Intent.EXTRA_INDEX);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Delete all media contributed by given package under the calling user. The
+     * meaning of "contributed" means it won't automatically be deleted when the
+     * app is uninstalled.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA)
+    public static void deleteContributedMedia(Context context, String packageName) {
+        try (ContentProviderClient client = context.getContentResolver()
+                .acquireContentProviderClient(AUTHORITY)) {
+            final Bundle in = new Bundle();
+            in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+            client.call(DELETE_CONTRIBUTED_MEDIA_CALL, null, in);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 64aa088..9380695 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1680,6 +1680,11 @@
      */
     public static final String CALL_METHOD_TAG_KEY = "_tag";
 
+    /**
+     * @hide - String argument extra to the fast-path call()-based requests
+     */
+    public static final String CALL_METHOD_PREFIX_KEY = "_prefix";
+
     /** @hide - Private call() method to write to 'system' table */
     public static final String CALL_METHOD_PUT_SYSTEM = "PUT_system";
 
@@ -1701,15 +1706,18 @@
     /** @hide - Private call() method to delete from the 'global' table */
     public static final String CALL_METHOD_DELETE_GLOBAL = "DELETE_global";
 
+    /** @hide - Private call() method to reset to defaults the 'configuration' table */
+    public static final String CALL_METHOD_DELETE_CONFIG = "DELETE_config";
+
+    /** @hide - Private call() method to reset to defaults the 'secure' table */
+    public static final String CALL_METHOD_RESET_SECURE = "RESET_secure";
+
     /** @hide - Private call() method to reset to defaults the 'global' table */
     public static final String CALL_METHOD_RESET_GLOBAL = "RESET_global";
 
     /** @hide - Private call() method to reset to defaults the 'configuration' table */
     public static final String CALL_METHOD_RESET_CONFIG = "RESET_config";
 
-    /** @hide - Private call() method to reset to defaults the 'secure' table */
-    public static final String CALL_METHOD_RESET_SECURE = "RESET_secure";
-
     /** @hide - Private call() method to query the 'system' table */
     public static final String CALL_METHOD_LIST_SYSTEM = "LIST_system";
 
@@ -1719,6 +1727,9 @@
     /** @hide - Private call() method to query the 'global' table */
     public static final String CALL_METHOD_LIST_GLOBAL = "LIST_global";
 
+    /** @hide - Private call() method to reset to defaults the 'configuration' table */
+    public static final String CALL_METHOD_LIST_CONFIG = "LIST_config";
+
     /**
      * Activity Extra: Limit available options in launched activity based on the given authority.
      * <p>
@@ -2044,7 +2055,8 @@
                     arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
                 }
                 IContentProvider cp = mProviderHolder.getProvider(cr);
-                cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
+                cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
+                        mCallSetCommand, name, arg);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
                 return false;
@@ -2117,12 +2129,14 @@
                     if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
                         final long token = Binder.clearCallingIdentity();
                         try {
-                            b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
+                            b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
+                                    mCallGetCommand, name, args);
                         } finally {
                             Binder.restoreCallingIdentity(token);
                         }
                     } else {
-                        b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
+                        b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
+                                mCallGetCommand, name, args);
                     }
                     if (b != null) {
                         String value = b.getString(Settings.NameValueTable.VALUE);
@@ -5112,7 +5126,8 @@
                 }
                 arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
-                cp.call(resolver.getPackageName(), CALL_METHOD_RESET_SECURE, null, arg);
+                cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
+                        CALL_METHOD_RESET_SECURE, null, arg);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
             }
@@ -11863,10 +11878,26 @@
         public static final String GPU_DEBUG_APP = "gpu_debug_app";
 
         /**
-         * App should try to use ANGLE
+         * Force all PKGs to use ANGLE, regardless of any other settings
+         * The value is a boolean (1 or 0).
          * @hide
          */
-        public static final String ANGLE_ENABLED_APP = "angle_enabled_app";
+        public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE =
+                "angle_gl_driver_all_angle";
+
+        /**
+         * List of PKGs that have an OpenGL driver selected
+         * @hide
+         */
+        public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS =
+                "angle_gl_driver_selection_pkgs";
+
+        /**
+         * List of selected OpenGL drivers, corresponding to the PKGs in GLOBAL_SETTINGS_DRIVER_PKGS
+         * @hide
+         */
+        public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES =
+                "angle_gl_driver_selection_values";
 
         /**
          * App that is selected to use updated graphics driver.
@@ -12821,6 +12852,13 @@
         public static final String HIDDEN_API_POLICY = "hidden_api_policy";
 
         /**
+         * Current version of signed configuration applied.
+         *
+         * @hide
+         */
+        public static final String SIGNED_CONFIG_VERSION = "signed_config_version";
+
+        /**
          * Timeout for a single {@link android.media.soundtrigger.SoundTriggerDetectionService}
          * operation (in ms).
          *
@@ -13147,7 +13185,8 @@
                 }
                 arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode);
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
-                cp.call(resolver.getPackageName(), CALL_METHOD_RESET_GLOBAL, null, arg);
+                cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
+                        CALL_METHOD_RESET_GLOBAL, null, arg);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e);
             }
@@ -13671,6 +13710,22 @@
                 "smart_replies_in_notifications_flags";
 
         /**
+         * Configuration flags for the automatic generation of smart replies and smart actions in
+         * notifications. This is encoded as a key=value list, separated by commas. Ex:
+         * "generate_replies=false,generate_actions=true".
+         *
+         * The following keys are supported:
+         *
+         * <pre>
+         * generate_replies                 (boolean)
+         * generate_actions                 (boolean)
+         * </pre>
+         * @hide
+         */
+        public static final String SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS =
+                "smart_suggestions_in_notifications_flags";
+
+        /**
          * If nonzero, crashes in foreground processes will bring up a dialog.
          * Otherwise, the process will be silently killed.
          * @hide
@@ -13747,6 +13802,14 @@
                 "backup_agent_timeout_parameters";
 
         /**
+         * Whether the backup system service supports multiple users (0 = disabled, 1 = enabled). If
+         * disabled, the service will only be active for the system user.
+         *
+         * @hide
+         */
+        public static final String BACKUP_MULTI_USER_ENABLED = "backup_multi_user_enabled";
+
+        /**
          * Blacklist of GNSS satellites.
          *
          * This is a list of integers separated by commas to represent pairs of (constellation,
@@ -13926,7 +13989,8 @@
                 }
                 arg.putInt(CALL_METHOD_RESET_MODE_KEY, RESET_MODE_PACKAGE_DEFAULTS);
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
-                cp.call(resolver.getPackageName(), CALL_METHOD_RESET_CONFIG, null, arg);
+                cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
+                        CALL_METHOD_RESET_CONFIG, null, arg);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't reset to defaults for " + CONTENT_URI, e);
             }
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 1cd76d2..bd3c3d3 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -175,7 +175,7 @@
     public float[][] onGetScores(@Nullable String algorithm,
             @Nullable Bundle algorithmOptions, @NonNull List<AutofillValue> actualValues,
             @NonNull List<String> userDataValues) {
-        Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()");
+        Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScores()");
         return null;
     }
 
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 7bf1f83..d408e9a 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -84,6 +84,7 @@
     private final @Nullable AutofillId[] mFieldClassificationIds;
     private final int mFlags;
     private int mRequestId;
+    private final @Nullable UserData mUserData;
 
     private FillResponse(@NonNull Builder builder) {
         mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -99,6 +100,7 @@
         mFieldClassificationIds = builder.mFieldClassificationIds;
         mFlags = builder.mFlags;
         mRequestId = INVALID_REQUEST_ID;
+        mUserData = builder.mUserData;
     }
 
     /** @hide */
@@ -157,6 +159,11 @@
     }
 
     /** @hide */
+    public @Nullable UserData getUserData() {
+        return mUserData;
+    }
+
+    /** @hide */
     @TestApi
     public int getFlags() {
         return mFlags;
@@ -198,6 +205,7 @@
         private AutofillId[] mFieldClassificationIds;
         private int mFlags;
         private boolean mDestroyed;
+        private UserData mUserData;
 
         /**
          * Triggers a custom UI before before autofilling the screen with any data set in this
@@ -506,6 +514,21 @@
         }
 
         /**
+         * Sets a specific {@link UserData} for field classification for this request only.
+         *
+         * @return this builder
+         * @throws IllegalStateException if the FillResponse
+         * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
+         * requires authentication}.
+         */
+        public Builder setUserData(@NonNull UserData userData) {
+            throwIfDestroyed();
+            throwIfAuthenticationCalled();
+            mUserData = Preconditions.checkNotNull(userData);
+            return this;
+        }
+
+        /**
          * Builds a new {@link FillResponse} instance.
          *
          * @throws IllegalStateException if any of the following conditions occur:
@@ -599,6 +622,9 @@
         if (mFieldClassificationIds != null) {
             builder.append(Arrays.toString(mFieldClassificationIds));
         }
+        if (mUserData != null) {
+            builder.append(", userData=").append(mUserData);
+        }
         return builder.append("]").toString();
     }
 
@@ -621,6 +647,7 @@
         parcel.writeParcelable(mPresentation, flags);
         parcel.writeParcelable(mHeader, flags);
         parcel.writeParcelable(mFooter, flags);
+        parcel.writeParcelable(mUserData, flags);
         parcel.writeParcelableArray(mIgnoredIds, flags);
         parcel.writeLong(mDisableDuration);
         parcel.writeParcelableArray(mFieldClassificationIds, flags);
@@ -661,6 +688,10 @@
             if (footer != null) {
                 builder.setFooter(footer);
             }
+            final UserData userData = parcel.readParcelable(null);
+            if (userData != null) {
+                builder.setUserData(userData);
+            }
 
             builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class));
             final long disableDuration = parcel.readLong();
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 9e3aba4..27df845 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -26,6 +26,7 @@
 import android.service.autofill.augmented.PresentationParams.Area;
 import android.util.Log;
 import android.view.Gravity;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
@@ -140,10 +141,24 @@
             // TODO(b/111330312): make sure all touch events are handled, window is always closed,
             // etc.
 
-            mDialog = new Dialog(rootView.getContext());
+            mDialog = new Dialog(rootView.getContext()) {
+                @Override
+                public boolean onTouchEvent(MotionEvent event) {
+                    if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                        FillWindow.this.destroy();
+                    }
+                    return false;
+                }
+            };
             mCloseGuard.open("destroy");
             final Window window = mDialog.getWindow();
             window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+            // Makes sure touch outside the dialog is received by the window behind the dialog.
+            window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+            // Makes sure the touch outside the dialog is received by the dialog to dismiss it.
+            window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+            // Makes sure keyboard shows up.
+            window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
 
             final int height = rect.bottom - rect.top;
             final int width = rect.right - rect.left;
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index dee6d90..ade7577 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -42,12 +42,15 @@
     static {
         DEFAULT_FLAGS = new HashMap<>();
         DEFAULT_FLAGS.put("settings_audio_switcher", "true");
-        DEFAULT_FLAGS.put("settings_systemui_theme", "true");
         DEFAULT_FLAGS.put("settings_dynamic_homepage", "true");
         DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
-        DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
-        DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
         DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
+        DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
+        DEFAULT_FLAGS.put("settings_systemui_theme", "true");
+        DEFAULT_FLAGS.put("settings_wifi_dpp", "false");
+        DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "false");
+        DEFAULT_FLAGS.put("settings_wifi_sharing", "false");
+        DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
         DEFAULT_FLAGS.put(SAFETY_HUB, "false");
         DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
     }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index bf1a005..34d076f 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -299,6 +299,8 @@
     private boolean mEnabled;
     private boolean mRequested = true;
 
+    private FrameDrawingCallback mNextRtFrameCallback;
+
     ThreadedRenderer(Context context, boolean translucent, String name) {
         super();
         setName(name);
@@ -432,6 +434,17 @@
     }
 
     /**
+     * Registers a callback to be executed when the next frame is being drawn on RenderThread. This
+     * callback will be executed on a RenderThread worker thread, and only used for the next frame
+     * and thus it will only fire once.
+     *
+     * @param callback The callback to register.
+     */
+    void registerRtFrameCallback(FrameDrawingCallback callback) {
+        mNextRtFrameCallback = callback;
+    }
+
+    /**
      * Destroys all hardware rendering resources associated with the specified
      * view hierarchy.
      *
@@ -562,6 +575,15 @@
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
         updateViewTreeDisplayList(view);
 
+        // Consume and set the frame callback after we dispatch draw to the view above, but before
+        // onPostDraw below which may reset the callback for the next frame.  This ensures that
+        // updates to the frame callback during scroll handling will also apply in this frame.
+        final FrameDrawingCallback callback = mNextRtFrameCallback;
+        mNextRtFrameCallback = null;
+        if (callback != null) {
+            setFrameCallback(callback);
+        }
+
         if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) {
             RecordingCanvas canvas = mRootNode.startRecording(mSurfaceWidth, mSurfaceHeight);
             try {
@@ -619,10 +641,8 @@
      *
      * @param view The view to draw.
      * @param attachInfo AttachInfo tied to the specified view.
-     * @param callbacks Callbacks invoked when drawing happens.
      */
-    void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks,
-            FrameDrawingCallback frameDrawingCallback) {
+    void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
         final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
         choreographer.mFrameInfo.markDrawStart();
 
@@ -642,9 +662,6 @@
             attachInfo.mPendingAnimatingRenderNodes = null;
         }
 
-        if (frameDrawingCallback != null) {
-            setFrameCallback(frameDrawingCallback);
-        }
         int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
         if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
             setEnabled(false);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cb47886..4b9a2b9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -200,8 +200,6 @@
     static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
     static boolean sFirstDrawComplete = false;
 
-    private FrameDrawingCallback mNextRtFrameCallback;
-
     /**
      * Callback for notifying about global configuration changes.
      */
@@ -1052,7 +1050,9 @@
      * @param callback The callback to register.
      */
     public void registerRtFrameCallback(FrameDrawingCallback callback) {
-        mNextRtFrameCallback = callback;
+        if (mAttachInfo.mThreadedRenderer != null) {
+            mAttachInfo.mThreadedRenderer.registerRtFrameCallback(callback);
+        }
     }
 
     @UnsupportedAppUsage
@@ -3534,10 +3534,7 @@
 
                 useAsyncReport = true;
 
-                // draw(...) might invoke post-draw, which might register the next callback already.
-                final FrameDrawingCallback callback = mNextRtFrameCallback;
-                mNextRtFrameCallback = null;
-                mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this, callback);
+                mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
             } else {
                 // If we get here with a disabled & requested hardware renderer, something went
                 // wrong (an invalidate posted right before we destroyed the hardware surface
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 1340955..e57fdff 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2354,17 +2354,19 @@
     }
 
     /**
-     * Shows the input method chooser dialog.
+     * Shows the input method chooser dialog from system.
      *
      * @param showAuxiliarySubtypes Set true to show auxiliary input methods.
+     * @param displayId The ID of the display where the chooser dialog should be shown.
      * @hide
      */
-    public void showInputMethodPicker(boolean showAuxiliarySubtypes) {
+    @RequiresPermission(WRITE_SECURE_SETTINGS)
+    public void showInputMethodPickerFromSystem(boolean showAuxiliarySubtypes, int displayId) {
         final int mode = showAuxiliarySubtypes
                 ? SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES
                 : SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES;
         try {
-            mService.showInputMethodPickerFromClient(mClient, mode);
+            mService.showInputMethodPickerFromSystem(mClient, mode, displayId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2520,16 +2522,6 @@
      */
     @Deprecated
     public boolean switchToLastInputMethod(IBinder imeToken) {
-        if (imeToken == null) {
-            // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
-            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
-            // TODO(Bug 114488811): Consider deprecating null token rule.
-            try {
-                return mService.switchToPreviousInputMethod(imeToken);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
         return InputMethodPrivilegedOperationsRegistry.get(imeToken).switchToPreviousInputMethod();
     }
 
@@ -2548,16 +2540,6 @@
      */
     @Deprecated
     public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
-        if (imeToken == null) {
-            // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
-            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
-            // TODO(Bug 114488811): Consider deprecating null token rule.
-            try {
-                return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
         return InputMethodPrivilegedOperationsRegistry.get(imeToken)
                 .switchToNextInputMethod(onlyCurrentIme);
     }
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 1a7b911..04b94b0 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -30,6 +30,7 @@
 import android.text.SpannedString;
 import android.util.ArraySet;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -654,6 +655,8 @@
         @NonNull
         @Hint
         private final List<String> mHints;
+        @Nullable
+        private String mCallingPackageName;
 
         private Request(
                 @NonNull List<Message> conversation,
@@ -666,15 +669,26 @@
             mHints = hints;
         }
 
-        private Request(Parcel in) {
+        private static Request readFromParcel(Parcel in) {
             List<Message> conversation = new ArrayList<>();
             in.readParcelableList(conversation, null);
-            mConversation = Collections.unmodifiableList(conversation);
-            mTypeConfig = in.readParcelable(null);
-            mMaxSuggestions = in.readInt();
+
+            TypeConfig typeConfig = in.readParcelable(null);
+
+            int maxSuggestions = in.readInt();
+
             List<String> hints = new ArrayList<>();
             in.readStringList(hints);
-            mHints = Collections.unmodifiableList(hints);
+
+            String callingPackageName = in.readString();
+
+            Request request = new Request(
+                    conversation,
+                    typeConfig,
+                    maxSuggestions,
+                    hints);
+            request.setCallingPackageName(callingPackageName);
+            return request;
         }
 
         @Override
@@ -683,6 +697,7 @@
             parcel.writeParcelable(mTypeConfig, flags);
             parcel.writeInt(mMaxSuggestions);
             parcel.writeStringList(mHints);
+            parcel.writeString(mCallingPackageName);
         }
 
         @Override
@@ -694,7 +709,7 @@
                 new Creator<Request>() {
                     @Override
                     public Request createFromParcel(Parcel in) {
-                        return new Request(in);
+                        return readFromParcel(in);
                     }
 
                     @Override
@@ -730,6 +745,26 @@
             return mHints;
         }
 
+        /**
+         * Sets the name of the package that is sending this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         * @hide
+         */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setCallingPackageName(@Nullable String callingPackageName) {
+            mCallingPackageName = callingPackageName;
+        }
+
+        /**
+         * Returns the name of the package that sent this request.
+         * This returns {@code null} if no calling package name is set.
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
         /** Builder object to construct the {@link Request} object. */
         public static final class Builder {
             @NonNull
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index f8fce62..c24489c 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -60,7 +60,7 @@
         mSettings = Preconditions.checkNotNull(settings);
         mFallback = context.getSystemService(TextClassificationManager.class)
                 .getTextClassifier(TextClassifier.LOCAL);
-        mPackageName = Preconditions.checkNotNull(context.getPackageName());
+        mPackageName = Preconditions.checkNotNull(context.getOpPackageName());
     }
 
     /**
@@ -72,6 +72,7 @@
         Preconditions.checkNotNull(request);
         Utils.checkMainThread();
         try {
+            request.setCallingPackageName(mPackageName);
             final TextSelectionCallback callback = new TextSelectionCallback();
             mManagerService.onSuggestSelection(mSessionId, request, callback);
             final TextSelection selection = callback.mReceiver.get();
@@ -93,6 +94,7 @@
         Preconditions.checkNotNull(request);
         Utils.checkMainThread();
         try {
+            request.setCallingPackageName(mPackageName);
             final TextClassificationCallback callback = new TextClassificationCallback();
             mManagerService.onClassifyText(mSessionId, request, callback);
             final TextClassification classification = callback.mReceiver.get();
@@ -150,6 +152,7 @@
         Utils.checkMainThread();
 
         try {
+            request.setCallingPackageName(mPackageName);
             final TextLanguageCallback callback = new TextLanguageCallback();
             mManagerService.onDetectLanguage(mSessionId, request, callback);
             final TextLanguage textLanguage = callback.mReceiver.get();
@@ -168,6 +171,7 @@
         Utils.checkMainThread();
 
         try {
+            request.setCallingPackageName(mPackageName);
             final ConversationActionsCallback callback = new ConversationActionsCallback();
             mManagerService.onSuggestConversationActions(mSessionId, request, callback);
             final ConversationActions conversationActions = callback.mReceiver.get();
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index e0910c0..d9f7965 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -37,11 +37,13 @@
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.SpannedString;
 import android.util.ArrayMap;
 import android.view.View.OnClickListener;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.view.textclassifier.TextClassifier.Utils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -518,6 +520,7 @@
         @Nullable private final LocaleList mDefaultLocales;
         @Nullable private final ZonedDateTime mReferenceTime;
         @NonNull private final Bundle mExtras;
+        @Nullable private String mCallingPackageName;
 
         private Request(
                 CharSequence text,
@@ -578,6 +581,26 @@
         }
 
         /**
+         * Sets the name of the package that is sending this request.
+         * <p>
+         * For SystemTextClassifier's use.
+         * @hide
+         */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setCallingPackageName(@Nullable String callingPackageName) {
+            mCallingPackageName = callingPackageName;
+        }
+
+        /**
+         * Returns the name of the package that sent this request.
+         * This returns {@code null} if no calling package name is set.
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -660,7 +683,8 @@
              */
             @NonNull
             public Request build() {
-                return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime,
+                return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
+                        mDefaultLocales, mReferenceTime,
                         mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
             }
         }
@@ -672,25 +696,37 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeString(mText.toString());
+            dest.writeCharSequence(mText);
             dest.writeInt(mStartIndex);
             dest.writeInt(mEndIndex);
-            dest.writeInt(mDefaultLocales != null ? 1 : 0);
-            if (mDefaultLocales != null) {
-                mDefaultLocales.writeToParcel(dest, flags);
-            }
-            dest.writeInt(mReferenceTime != null ? 1 : 0);
-            if (mReferenceTime != null) {
-                dest.writeString(mReferenceTime.toString());
-            }
+            dest.writeParcelable(mDefaultLocales, flags);
+            dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
+            dest.writeString(mCallingPackageName);
             dest.writeBundle(mExtras);
         }
 
+        private static Request readFromParcel(Parcel in) {
+            final CharSequence text = in.readCharSequence();
+            final int startIndex = in.readInt();
+            final int endIndex = in.readInt();
+            final LocaleList defaultLocales = in.readParcelable(null);
+            final String referenceTimeString = in.readString();
+            final ZonedDateTime referenceTime = referenceTimeString == null
+                    ? null : ZonedDateTime.parse(referenceTimeString);
+            final String callingPackageName = in.readString();
+            final Bundle extras = in.readBundle();
+
+            final Request request = new Request(text, startIndex, endIndex,
+                    defaultLocales, referenceTime, extras);
+            request.setCallingPackageName(callingPackageName);
+            return request;
+        }
+
         public static final Parcelable.Creator<Request> CREATOR =
                 new Parcelable.Creator<Request>() {
                     @Override
                     public Request createFromParcel(Parcel in) {
-                        return new Request(in);
+                        return readFromParcel(in);
                     }
 
                     @Override
@@ -698,15 +734,6 @@
                         return new Request[size];
                     }
                 };
-
-        private Request(Parcel in) {
-            mText = in.readString();
-            mStartIndex = in.readInt();
-            mEndIndex = in.readInt();
-            mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
-            mReferenceTime = in.readInt() == 0 ? null : ZonedDateTime.parse(in.readString());
-            mExtras = in.readBundle();
-        }
     }
 
     @Override
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index d28459e..b1609fc 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -26,6 +26,7 @@
 import android.os.Parcelable;
 import android.util.ArrayMap;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.util.Locale;
@@ -90,7 +91,7 @@
      * confidence to low confidence.
      *
      * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getLocaleCount() for the number of locales available.
+     * @see #getLocaleHypothesisCount() for the number of locales available.
      */
     @NonNull
     public ULocale getLocale(int index) {
@@ -222,11 +223,12 @@
         };
 
         private final CharSequence mText;
-        private final Bundle mBundle;
+        private final Bundle mExtra;
+        @Nullable private String mCallingPackageName;
 
         private Request(CharSequence text, Bundle bundle) {
             mText = text;
-            mBundle = bundle;
+            mExtra = bundle;
         }
 
         /**
@@ -238,6 +240,25 @@
         }
 
         /**
+         * Sets the name of the package that is sending this request.
+         * Package-private for SystemTextClassifier's use.
+         * @hide
+         */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setCallingPackageName(@Nullable String callingPackageName) {
+            mCallingPackageName = callingPackageName;
+        }
+
+        /**
+         * Returns the name of the package that sent this request.
+         * This returns null if no calling package name is set.
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
+        /**
          * 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
@@ -246,7 +267,7 @@
          */
         @NonNull
         public Bundle getExtras() {
-            return mBundle.deepCopy();
+            return mExtra.deepCopy();
         }
 
         @Override
@@ -257,13 +278,18 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeCharSequence(mText);
-            dest.writeBundle(mBundle);
+            dest.writeString(mCallingPackageName);
+            dest.writeBundle(mExtra);
         }
 
         private static Request readFromParcel(Parcel in) {
-            return new Request(
-                    in.readCharSequence(),
-                    in.readBundle());
+            final CharSequence text = in.readCharSequence();
+            final String callingPackageName = in.readString();
+            final Bundle extra = in.readBundle();
+
+            final Request request = new Request(text, extra);
+            request.setCallingPackageName(callingPackageName);
+            return request;
         }
 
         /**
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 1e42c41..ab34178 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -30,6 +30,7 @@
 import android.text.style.ClickableSpan;
 import android.text.style.URLSpan;
 import android.view.View;
+import android.view.textclassifier.TextClassifier.EntityConfig;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.widget.TextView;
 
@@ -205,27 +206,32 @@
         private final EntityConfidence mEntityScores;
         private final int mStart;
         private final int mEnd;
-        @Nullable final URLSpan mUrlSpan;
+        private final Bundle mExtras;
+        @Nullable private final URLSpan mUrlSpan;
 
         /**
          * Create a new TextLink.
          *
          * @param start The start index of the identified subsequence
          * @param end The end index of the identified subsequence
-         * @param entityScores A mapping of entity type to confidence score
+         * @param entityConfidence A mapping of entity type to confidence score
+         * @param extras A bundle containing custom data related to this TextLink
          * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled
          *
-         * @throws IllegalArgumentException if entityScores is null or empty
+         * @throws IllegalArgumentException if {@code entityConfidence} is null or empty
+         * @throws IllegalArgumentException if {@code start} is greater than {@code end}
          */
-        TextLink(int start, int end, Map<String, Float> entityScores,
-                @Nullable URLSpan urlSpan) {
-            Preconditions.checkNotNull(entityScores);
-            Preconditions.checkArgument(!entityScores.isEmpty());
+        private TextLink(int start, int end, @NonNull EntityConfidence entityConfidence,
+                @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
+            Preconditions.checkNotNull(entityConfidence);
+            Preconditions.checkArgument(!entityConfidence.getEntities().isEmpty());
             Preconditions.checkArgument(start <= end);
+            Preconditions.checkNotNull(extras);
             mStart = start;
             mEnd = end;
-            mEntityScores = new EntityConfidence(entityScores);
+            mEntityScores = entityConfidence;
             mUrlSpan = urlSpan;
+            mExtras = extras;
         }
 
         /**
@@ -274,6 +280,13 @@
             return mEntityScores.getConfidenceScore(entityType);
         }
 
+        /**
+         * Returns a bundle containing custom data related to this TextLink.
+         */
+        public Bundle getExtras() {
+            return mExtras;
+        }
+
         @Override
         public String toString() {
             return String.format(Locale.US,
@@ -291,13 +304,22 @@
             mEntityScores.writeToParcel(dest, flags);
             dest.writeInt(mStart);
             dest.writeInt(mEnd);
+            dest.writeBundle(mExtras);
+        }
+
+        private static TextLink readFromParcel(Parcel in) {
+            final EntityConfidence entityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+            final int start = in.readInt();
+            final int end = in.readInt();
+            final Bundle extras = in.readBundle();
+            return new TextLink(start, end, entityConfidence, extras, null /* urlSpan */);
         }
 
         public static final Parcelable.Creator<TextLink> CREATOR =
                 new Parcelable.Creator<TextLink>() {
                     @Override
                     public TextLink createFromParcel(Parcel in) {
-                        return new TextLink(in);
+                        return readFromParcel(in);
                     }
 
                     @Override
@@ -305,13 +327,6 @@
                         return new TextLink[size];
                     }
                 };
-
-        private TextLink(Parcel in) {
-            mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
-            mStart = in.readInt();
-            mEnd = in.readInt();
-            mUrlSpan = null;
-        }
     }
 
     /**
@@ -321,23 +336,21 @@
 
         private final CharSequence mText;
         @Nullable private final LocaleList mDefaultLocales;
-        @Nullable private final TextClassifier.EntityConfig mEntityConfig;
+        @Nullable private final EntityConfig mEntityConfig;
         private final boolean mLegacyFallback;
-        private String mCallingPackageName;
+        @Nullable private String mCallingPackageName;
         private final Bundle mExtras;
 
         private Request(
                 CharSequence text,
                 LocaleList defaultLocales,
-                TextClassifier.EntityConfig entityConfig,
+                EntityConfig entityConfig,
                 boolean legacyFallback,
-                String callingPackageName,
                 Bundle extras) {
             mText = text;
             mDefaultLocales = defaultLocales;
             mEntityConfig = entityConfig;
             mLegacyFallback = legacyFallback;
-            mCallingPackageName = callingPackageName;
             mExtras = extras;
         }
 
@@ -360,10 +373,10 @@
 
         /**
          * @return The config representing the set of entities to look for
-         * @see Builder#setEntityConfig(TextClassifier.EntityConfig)
+         * @see Builder#setEntityConfig(EntityConfig)
          */
         @Nullable
-        public TextClassifier.EntityConfig getEntityConfig() {
+        public EntityConfig getEntityConfig() {
             return mEntityConfig;
         }
 
@@ -378,13 +391,26 @@
         }
 
         /**
-         * Sets the name of the package that requested the links to get generated.
+         * Sets the name of the package that is sending this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         * @hide
          */
-        void setCallingPackageName(@Nullable String callingPackageName) {
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setCallingPackageName(@Nullable String callingPackageName) {
             mCallingPackageName = callingPackageName;
         }
 
         /**
+         * Returns the name of the package that sent this request.
+         * This returns {@code null} if no calling package name is set.
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -404,9 +430,8 @@
             private final CharSequence mText;
 
             @Nullable private LocaleList mDefaultLocales;
-            @Nullable private TextClassifier.EntityConfig mEntityConfig;
+            @Nullable private EntityConfig mEntityConfig;
             private boolean mLegacyFallback = true; // Use legacy fall back by default.
-            private String mCallingPackageName;
             @Nullable private Bundle mExtras;
 
             public Builder(@NonNull CharSequence text) {
@@ -434,7 +459,7 @@
              * @return this builder
              */
             @NonNull
-            public Builder setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) {
+            public Builder setEntityConfig(@Nullable EntityConfig entityConfig) {
                 mEntityConfig = entityConfig;
                 return this;
             }
@@ -455,18 +480,6 @@
             }
 
             /**
-             * Sets the name of the package that requested the links to get generated.
-             *
-             * @return this builder
-             * @hide
-             */
-            @NonNull
-            public Builder setCallingPackageName(@Nullable String callingPackageName) {
-                mCallingPackageName = callingPackageName;
-                return this;
-            }
-
-            /**
              * Sets the extended data.
              *
              * @return this builder
@@ -483,21 +496,11 @@
             public Request build() {
                 return new Request(
                         mText, mDefaultLocales, mEntityConfig,
-                        mLegacyFallback, mCallingPackageName,
+                        mLegacyFallback,
                         mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
             }
         }
 
-        /**
-         * @return the name of the package that requested the links to get generated.
-         * TODO: make available as system API
-         * @hide
-         */
-        @Nullable
-        public String getCallingPackageName() {
-            return mCallingPackageName;
-        }
-
         @Override
         public int describeContents() {
             return 0;
@@ -506,23 +509,30 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeString(mText.toString());
-            dest.writeInt(mDefaultLocales != null ? 1 : 0);
-            if (mDefaultLocales != null) {
-                mDefaultLocales.writeToParcel(dest, flags);
-            }
-            dest.writeInt(mEntityConfig != null ? 1 : 0);
-            if (mEntityConfig != null) {
-                mEntityConfig.writeToParcel(dest, flags);
-            }
+            dest.writeParcelable(mDefaultLocales, flags);
+            dest.writeParcelable(mEntityConfig, flags);
             dest.writeString(mCallingPackageName);
             dest.writeBundle(mExtras);
         }
 
+        private static Request readFromParcel(Parcel in) {
+            final String text = in.readString();
+            final LocaleList defaultLocales = in.readParcelable(null);
+            final EntityConfig entityConfig = in.readParcelable(null);
+            final String callingPackageName = in.readString();
+            final Bundle extras = in.readBundle();
+
+            final Request request = new Request(text, defaultLocales, entityConfig,
+                    /* legacyFallback= */ true, extras);
+            request.setCallingPackageName(callingPackageName);
+            return request;
+        }
+
         public static final Parcelable.Creator<Request> CREATOR =
                 new Parcelable.Creator<Request>() {
                     @Override
                     public Request createFromParcel(Parcel in) {
-                        return new Request(in);
+                        return readFromParcel(in);
                     }
 
                     @Override
@@ -530,16 +540,6 @@
                         return new Request[size];
                     }
                 };
-
-        private Request(Parcel in) {
-            mText = in.readString();
-            mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
-            mEntityConfig = in.readInt() == 0
-                    ? null : TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
-            mLegacyFallback = true;
-            mCallingPackageName = in.readString();
-            mExtras = in.readBundle();
-        }
     }
 
     /**
@@ -645,9 +645,20 @@
          * @throws IllegalArgumentException if entityScores is null or empty.
          */
         @NonNull
-        public Builder addLink(int start, int end, Map<String, Float> entityScores) {
-            mLinks.add(new TextLink(start, end, entityScores, null));
-            return this;
+        public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores) {
+            return addLink(start, end, entityScores, Bundle.EMPTY, null);
+        }
+
+        /**
+         * Adds a TextLink.
+         *
+         * @see #addLink(int, int, Map)
+         * @param extras An optional bundle containing custom data related to this TextLink
+         */
+        @NonNull
+        public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
+                @NonNull Bundle extras) {
+            return addLink(start, end, entityScores, extras, null);
         }
 
         /**
@@ -655,9 +666,15 @@
          * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
          */
         @NonNull
-        Builder addLink(int start, int end, Map<String, Float> entityScores,
+        Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
                 @Nullable URLSpan urlSpan) {
-            mLinks.add(new TextLink(start, end, entityScores, urlSpan));
+            return addLink(start, end, entityScores, Bundle.EMPTY, urlSpan);
+        }
+
+        private Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
+                @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
+            mLinks.add(new TextLink(
+                    start, end, new EntityConfidence(entityScores), extras, urlSpan));
             return this;
         }
 
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index f236915..4a6f3e5 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -24,10 +24,12 @@
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.SpannedString;
 import android.util.ArrayMap;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.view.textclassifier.TextClassifier.Utils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.util.Locale;
@@ -209,6 +211,7 @@
         @Nullable private final LocaleList mDefaultLocales;
         private final boolean mDarkLaunchAllowed;
         private final Bundle mExtras;
+        @Nullable private String mCallingPackageName;
 
         private Request(
                 CharSequence text,
@@ -270,6 +273,26 @@
         }
 
         /**
+         * Sets the name of the package that is sending this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         * @hide
+         */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setCallingPackageName(@Nullable String callingPackageName) {
+            mCallingPackageName = callingPackageName;
+        }
+
+        /**
+         * Returns the name of the package that sent this request.
+         * This returns {@code null} if no calling package name is set.
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -355,7 +378,7 @@
              */
             @NonNull
             public Request build() {
-                return new Request(mText, mStartIndex, mEndIndex,
+                return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
                         mDefaultLocales, mDarkLaunchAllowed,
                         mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
             }
@@ -368,21 +391,33 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeString(mText.toString());
+            dest.writeCharSequence(mText);
             dest.writeInt(mStartIndex);
             dest.writeInt(mEndIndex);
-            dest.writeInt(mDefaultLocales != null ? 1 : 0);
-            if (mDefaultLocales != null) {
-                mDefaultLocales.writeToParcel(dest, flags);
-            }
+            dest.writeParcelable(mDefaultLocales, flags);
+            dest.writeString(mCallingPackageName);
             dest.writeBundle(mExtras);
         }
 
+        private static Request readFromParcel(Parcel in) {
+            final CharSequence text = in.readCharSequence();
+            final int startIndex = in.readInt();
+            final int endIndex = in.readInt();
+            final LocaleList defaultLocales = in.readParcelable(null);
+            final String callingPackageName = in.readString();
+            final Bundle extras = in.readBundle();
+
+            final Request request = new Request(text, startIndex, endIndex, defaultLocales,
+                    /* darkLaunchAllowed= */ false, extras);
+            request.setCallingPackageName(callingPackageName);
+            return request;
+        }
+
         public static final Parcelable.Creator<Request> CREATOR =
                 new Parcelable.Creator<Request>() {
                     @Override
                     public Request createFromParcel(Parcel in) {
-                        return new Request(in);
+                        return readFromParcel(in);
                     }
 
                     @Override
@@ -390,15 +425,6 @@
                         return new Request[size];
                     }
                 };
-
-        private Request(Parcel in) {
-            mText = in.readString();
-            mStartIndex = in.readInt();
-            mEndIndex = in.readInt();
-            mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
-            mDarkLaunchAllowed = false;
-            mExtras = in.readBundle();
-        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 46c6fa4..ef5eb6c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -94,7 +94,7 @@
 public class ResolverActivity extends Activity {
 
     // Temporary flag for new chooser delegate behavior.
-    boolean mEnableChooserDelegate = false;
+    boolean mEnableChooserDelegate = true;
 
     protected ResolveListAdapter mAdapter;
     private boolean mSafeForwardingMode;
@@ -887,6 +887,8 @@
             chooserIntent.putExtra(ActivityTaskManager.EXTRA_IGNORE_TARGET_SECURITY,
                     ignoreTargetSecurity);
             chooserIntent.putExtra(Intent.EXTRA_USER_ID, userId);
+            chooserIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
+                    | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
             startActivity(chooserIntent);
         } catch (RemoteException e) {
             Log.e(TAG, e.toString());
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 1e71bd1..f62c440 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -64,6 +64,8 @@
 
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
+    void showInputMethodPickerFromSystem(in IInputMethodClient client, int auxiliarySubtypeMode,
+            int displayId);
     void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
     boolean isInputMethodPickerShownForTest();
     // TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
@@ -74,10 +76,6 @@
     boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
     InputMethodSubtype getCurrentInputMethodSubtype();
     boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype);
-    // TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
-    boolean switchToPreviousInputMethod(in IBinder token);
-    // TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
-    boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme);
     void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
     // This is kept due to @UnsupportedAppUsage.
     // TODO(Bug 113914148): Consider removing this.
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index c96bacd..d5dc703 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -840,6 +840,11 @@
                     + "of length " + MIN_LOCK_PASSWORD_SIZE);
         }
 
+        if (requestedQuality < PASSWORD_QUALITY_NUMERIC) {
+            throw new IllegalArgumentException("quality must be at least NUMERIC, but was "
+                    + requestedQuality);
+        }
+
         final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
         setKeyguardStoredPasswordQuality(
                 computePasswordQuality(CREDENTIAL_TYPE_PASSWORD, password, requestedQuality),
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index b97a9fa..b00e6fd 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -25,7 +25,6 @@
 import android.os.Build;
 import android.os.Environment;
 import android.os.Process;
-import android.os.SystemProperties;
 import android.os.storage.StorageManager;
 import android.permission.PermissionManager.SplitPermissionInfo;
 import android.text.TextUtils;
@@ -78,10 +77,23 @@
 
     final ArrayList<SplitPermissionInfo> mSplitPermissions = new ArrayList<>();
 
+    public static final class SharedLibraryEntry {
+        public final String name;
+        public final String filename;
+        public final String[] dependencies;
+
+        SharedLibraryEntry(String name, String filename, String[] dependencies) {
+            this.name = name;
+            this.filename = filename;
+            this.dependencies = dependencies;
+        }
+    }
+
     // These are the built-in shared libraries that were read from the
-    // system configuration files.  Keys are the library names; strings are the
-    // paths to the libraries.
-    final ArrayMap<String, String> mSharedLibraries  = new ArrayMap<>();
+    // system configuration files. Keys are the library names; values are
+    // the individual entries that contain information such as filename
+    // and dependencies.
+    final ArrayMap<String, SharedLibraryEntry> mSharedLibraries = new ArrayMap<>();
 
     // These are the features this devices supports that were read from the
     // system configuration files.
@@ -200,7 +212,7 @@
         return mSplitPermissions;
     }
 
-    public ArrayMap<String, String> getSharedLibraries() {
+    public ArrayMap<String, SharedLibraryEntry> getSharedLibraries() {
         return mSharedLibraries;
     }
 
@@ -497,6 +509,7 @@
                 } else if ("library".equals(name) && allowLibs) {
                     String lname = parser.getAttributeValue(null, "name");
                     String lfile = parser.getAttributeValue(null, "file");
+                    String ldependency = parser.getAttributeValue(null, "dependency");
                     if (lname == null) {
                         Slog.w(TAG, "<library> without name in " + permFile + " at "
                                 + parser.getPositionDescription());
@@ -505,11 +518,12 @@
                                 + parser.getPositionDescription());
                     } else {
                         //Log.i(TAG, "Got library " + lname + " in " + lfile);
-                        mSharedLibraries.put(lname, lfile);
+                        SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
+                                ldependency == null ? new String[0] : ldependency.split(":"));
+                        mSharedLibraries.put(lname, entry);
                     }
                     XmlUtils.skipCurrentTag(parser);
                     continue;
-
                 } else if ("feature".equals(name) && allowFeatures) {
                     String fname = parser.getAttributeValue(null, "name");
                     int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index d927972..516093e 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -23,8 +23,6 @@
 #include <nativehelper/JniConstants.h>
 #include "core_jni_helpers.h"
 
-#include <nativehelper/ScopedBytes.h>
-
 #include <utils/Log.h>
 #include <media/AudioSystem.h>
 #include <media/AudioTrack.h>
@@ -481,6 +479,24 @@
 }
 
 // ----------------------------------------------------------------------------
+static jboolean
+android_media_AudioTrack_is_direct_output_supported(JNIEnv *env, jobject thiz,
+                                             jint encoding, jint sampleRate,
+                                             jint channelMask, jint channelIndexMask,
+                                             jint contentType, jint usage, jint flags) {
+    audio_config_base_t config = {};
+    audio_attributes_t attributes = {};
+    config.format = static_cast<audio_format_t>(audioFormatToNative(encoding));
+    config.sample_rate = static_cast<uint32_t>(sampleRate);
+    config.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask);
+    attributes.content_type = static_cast<audio_content_type_t>(contentType);
+    attributes.usage = static_cast<audio_usage_t>(usage);
+    attributes.flags = static_cast<audio_flags_mask_t>(flags);
+    // ignore source and tags attributes as they don't affect querying whether output is supported
+    return AudioTrack::isDirectOutputSupported(config, attributes);
+}
+
+// ----------------------------------------------------------------------------
 static void
 android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
 {
@@ -712,7 +728,7 @@
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
-        jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
+        jobject javaByteBuffer, jint byteOffset, jint sizeInBytes,
         jint javaAudioFormat, jboolean isWriteBlocking) {
     //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
     //    offsetInBytes, sizeInBytes);
@@ -723,13 +739,14 @@
         return (jint)AUDIO_JAVA_INVALID_OPERATION;
     }
 
-    ScopedBytesRO bytes(env, javaBytes);
-    if (bytes.get() == NULL) {
+    const jbyte* bytes =
+            reinterpret_cast<const jbyte*>(env->GetDirectBufferAddress(javaByteBuffer));
+    if (bytes == NULL) {
         ALOGE("Error retrieving source of audio data to play, can't play");
         return (jint)AUDIO_JAVA_BAD_VALUE;
     }
 
-    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
+    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes, byteOffset,
             sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
 
     return written;
@@ -1298,6 +1315,9 @@
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
     // name,              signature,     funcPtr
+    {"native_is_direct_output_supported",
+                             "(IIIIIII)Z",
+                                         (void *)android_media_AudioTrack_is_direct_output_supported},
     {"native_start",         "()V",      (void *)android_media_AudioTrack_start},
     {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
     {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
@@ -1308,7 +1328,7 @@
     {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
     {"native_write_byte",    "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
     {"native_write_native_bytes",
-                             "(Ljava/lang/Object;IIIZ)I",
+                             "(Ljava/nio/ByteBuffer;IIIZ)I",
                                          (void *)android_media_AudioTrack_write_native_bytes},
     {"native_write_short",   "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
     {"native_write_float",   "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index b1a9866..06625b3 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -32,15 +32,16 @@
     android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
 }
 
-void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jboolean devOptIn,
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring devOptIn,
                          jobject rulesFd, jlong rulesOffset, jlong rulesLength) {
     ScopedUtfChars pathChars(env, path);
     ScopedUtfChars appNameChars(env, appName);
+    ScopedUtfChars devOptInChars(env, devOptIn);
 
     int rulesFd_native = jniGetFDFromFileDescriptor(env, rulesFd);
 
     android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
-            devOptIn, rulesFd_native, rulesOffset, rulesLength);
+            devOptInChars.c_str(), rulesFd_native, rulesOffset, rulesLength);
 }
 
 void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
@@ -67,7 +68,7 @@
 const JNINativeMethod g_methods[] = {
     { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) },
     { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
-    { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;ZLjava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
+    { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
     { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
     { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
     { "setDebugLayersGLES", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayersGLES_native) },
diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto
index 2290b2f..bba8328 100644
--- a/core/proto/android/app/job/enums.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -30,4 +30,5 @@
     STOP_REASON_PREEMPT = 2;
     STOP_REASON_TIMEOUT = 3;
     STOP_REASON_DEVICE_IDLE = 4;
+    STOP_REASON_DEVICE_THERMAL = 5;
 }
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 9620e4b..11bd43b 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -109,7 +109,15 @@
     }
     optional Autofill autofill = 140;
 
-    optional SettingProto backup_agent_timeout_parameters = 18;
+    reserved 18; // Used to be backup_agent_timeout_parameters
+
+    message Backup {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto backup_agent_timeout_parameters = 1;
+        optional SettingProto backup_multi_user_enabled = 2;
+    }
+    optional Backup backup = 146;
 
     message Battery {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -417,16 +425,20 @@
         // Ordered GPU debug layer list for Vulkan
         // i.e. <layer1>:<layer2>:...:<layerN>
         optional SettingProto debug_layers = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        // App will load ANGLE instead of native GLES drivers.
-        optional SettingProto angle_enabled_app = 3;
+        // ANGLE - Force all PKGs to use ANGLE, regardless of any other settings
+        optional SettingProto angle_gl_driver_all_angle = 3;
+        // ANGLE - List of PKGs that specify an OpenGL driver
+        optional SettingProto angle_gl_driver_selection_pkgs = 4;
+        // ANGLE - Corresponding OpenGL driver selection for the PKG
+        optional SettingProto angle_gl_driver_selection_values = 5;
         // App that can provide layer libraries.
-        optional SettingProto debug_layer_app = 4;
+        optional SettingProto debug_layer_app = 6;
         // Ordered GPU debug layer list for GLES
         // i.e. <layer1>:<layer2>:...:<layerN>
-        optional SettingProto debug_layers_gles = 5;
+        optional SettingProto debug_layers_gles = 7;
         // App opt in to load updated graphics driver instead of
         // native graphcis driver through developer options.
-        optional SettingProto updated_gfx_driver_dev_opt_in_app = 6;
+        optional SettingProto updated_gfx_driver_dev_opt_in_app = 8;
     }
     optional Gpu gpu = 59;
 
@@ -635,6 +647,9 @@
         // separated by commas.
         optional SettingProto snooze_options = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto smart_replies_in_notifications_flags = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Configuration options for smart replies and smart actions in notifications. This is
+        // encoded as a key=value list separated by commas.
+        optional SettingProto smart_suggestions_in_notifications_flags = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Notification notification = 82;
 
@@ -1000,5 +1015,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 146;
+    // Next tag = 147;
 }
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 231caab..c2bc7bf 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -41,6 +41,7 @@
     optional int64 last_heartbeat_time_millis = 16;
     optional int64 next_heartbeat_time_millis = 17;
     optional bool in_parole = 18;
+    optional bool in_thermal = 19;
 
     repeated int32 started_users = 2;
 
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 8fbea12..c7a6b68 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -44,14 +44,14 @@
   SET_PERMISSION_GRANT_STATE = 19;
   INSTALL_KEY_PAIR = 20;
   INSTALL_CA_CERT = 21;
-  ON_CHOOSE_KEY_ALIAS = 22;
+  CHOOSE_PRIVATE_KEY_ALIAS = 22;
   REMOVE_KEY_PAIR = 23;
   UNINSTALL_CA_CERTS = 24;
   SET_CERT_INSTALLER_PACKAGE = 25;
   SET_ALWAYS_ON_VPN_PACKAGE = 26;
   SET_PERMITTED_INPUT_METHODS = 27;
   SET_PERMITTED_ACCESSIBILITY_SERVICES = 28;
-  SET_SCREEN_CAPTURE_DISABLE = 29;
+  SET_SCREEN_CAPTURE_DISABLED = 29;
   SET_CAMERA_DISABLED = 30;
   QUERY_SUMMARY_FOR_USER = 31;
   QUERY_SUMMARY = 32;
@@ -64,16 +64,16 @@
   SET_ORGANIZATION_COLOR = 39;
   SET_PROFILE_NAME = 40;
   SET_USER_ICON = 41;
-  SET_DEVICE_OWNER_LOCKSCREEN_INFO = 42;
+  SET_DEVICE_OWNER_LOCK_SCREEN_INFO = 42;
   SET_SHORT_SUPPORT_MESSAGE = 43;
   SET_LONG_SUPPORT_MESSAGE = 44;
   SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED = 45;
-  SET_CROSS_PROFILE_CALLER_DISABLED = 46;
+  SET_CROSS_PROFILE_CALLER_ID_DISABLED = 46;
   SET_BLUETOOTH_CONTACT_SHARING_DISABLED = 47;
   ADD_CROSS_PROFILE_INTENT_FILTER = 48;
   ADD_CROSS_PROFILE_WIDGET_PROVIDER = 49;
   SET_SYSTEM_UPDATE_POLICY = 50;
-  SET_LOCKTASK_PACKAGES = 51;
+  SET_LOCKTASK_MODE_ENABLED = 51;
   ADD_PERSISTENT_PREFERRED_ACTIVITY = 52;
   REQUEST_BUGREPORT = 53;
   GET_WIFI_MAC_ADDRESS = 54;
@@ -99,13 +99,13 @@
   INSTALL_SYSTEM_UPDATE_ERROR = 74;
   IS_MANAGED_KIOSK = 75;
   IS_UNATTENDED_MANAGED_KIOSK = 76;
-  PROVISIONING_TO_COMP = 77;
-  PROVISIONING_FORCED_DO = 78;
+  PROVISIONING_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE = 77;
+  PROVISIONING_PERSISTENT_DEVICE_OWNER = 78;
 
   // existing Tron logs to be migrated to WestWorld
   PROVISIONING_ENTRY_POINT_NFC = 79;
   PROVISIONING_ENTRY_POINT_QR_CODE = 80;
-  PROVISIONING_ENTRY_POINT_ZERO_TOUCH = 81;
+  PROVISIONING_ENTRY_POINT_CLOUD_ENROLLMENT = 81;
   PROVISIONING_ENTRY_POINT_ADB = 82;
   PROVISIONING_ENTRY_POINT_TRUSTED_SOURCE = 83;
   PROVISIONING_DPC_PACKAGE_NAME = 84;
@@ -134,4 +134,11 @@
   PROVISIONING_TERMS_ACTIVITY_TIME_MS = 107;
   PROVISIONING_TERMS_COUNT = 108;
   PROVISIONING_TERMS_READ = 109;
+
+  SEPARATE_PROFILE_CHALLENGE_CHANGED = 110;
+  SET_GLOBAL_SETTING = 111;
+  PM_IS_INSTALLER_DEVICE_OWNER_OR_AFFILIATED_PROFILE_OWNER = 112;
+  PM_UNINSTALL = 113;
+  WIFI_SERVICE_ADD_NETWORK_SUGGESTIONS = 114;
+  WIFI_SERVICE_ADD_OR_UPDATE_NETWORK = 115;
 }
diff --git a/core/proto/android/stats/docsui/docsui_enums.proto b/core/proto/android/stats/docsui/docsui_enums.proto
new file mode 100644
index 0000000..6cb606a
--- /dev/null
+++ b/core/proto/android/stats/docsui/docsui_enums.proto
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.stats.docsui;
+option java_multiple_files = true;
+
+enum LaunchAction {
+    UNKNOWN = 0;
+    OPEN = 1;
+    CREATE = 2;
+    GET_CONTENT = 3;
+    OPEN_TREE = 4;
+    PICK_COPY_DEST = 5;
+    BROWSE = 6;
+    OTHER = 7;
+}
+
+enum MimeType {
+    MIME_UNKNOWN = 0;
+    MIME_NONE = 1;
+    MIME_ANY = 2;
+    MIME_AUDIO = 3;
+    MIME_IMAGE = 4;
+    MIME_MESSAGE = 5;
+    MIME_MULTIPART = 6;
+    MIME_TEXT = 7;
+    MIME_VIDEO = 8;
+    MIME_OTHER = 9;
+}
+
+enum Root {
+    ROOT_UNKNOWN = 0;
+    ROOT_NONE = 1;
+    ROOT_OTHER_DOCS_PROVIDER = 2;
+    ROOT_AUDIO = 3;
+    ROOT_DEVICE_STORAGE = 4;
+    ROOT_DOWNLOADS = 5;
+    ROOT_HOME = 6;
+    ROOT_IMAGES = 7;
+    ROOT_RECENTS = 8;
+    ROOT_VIDEOS = 9;
+    ROOT_MTP = 10;
+    ROOT_THIRD_PARTY_APP = 11;
+}
+
+enum ContextScope {
+    SCOPE_UNKNOWN = 0;
+    SCOPE_FILES = 1;
+    SCOPE_PICKER = 2;
+}
+
+enum Provider {
+    PROVIDER_UNKNOWN = 0;
+    PROVIDER_SYSTEM = 1;
+    PROVIDER_EXTERNAL = 2;
+}
+
+enum FileOperation {
+    OP_UNKNOWN = 0;
+    OP_OTHER = 1;
+    OP_COPY = 2;
+    OP_COPY_INTRA_PROVIDER = 3;
+    OP_COPY_SYSTEM_PROVIDER = 4;
+    OP_COPY_EXTERNAL_PROVIDER = 5;
+    OP_MOVE = 6;
+    OP_MOVE_INTRA_PROVIDER = 7;
+    OP_MOVE_SYSTEM_PROVIDER = 8;
+    OP_MOVE_EXTERNAL_PROVIDER = 9;
+    OP_DELETE = 10;
+    OP_RENAME = 11;
+    OP_CREATE_DIR = 12;
+    OP_OTHER_ERROR = 13;
+    OP_DELETE_ERROR = 14;
+    OP_MOVE_ERROR = 15;
+    OP_COPY_ERROR = 16;
+    OP_RENAME_ERROR = 17;
+    OP_CREATE_DIR_ERROR = 18;
+    OP_COMPRESS_INTRA_PROVIDER = 19;
+    OP_COMPRESS_SYSTEM_PROVIDER = 20;
+    OP_COMPRESS_EXTERNAL_PROVIDER = 21;
+    OP_EXTRACT_INTRA_PROVIDER = 22;
+    OP_EXTRACT_SYSTEM_PROVIDER = 23;
+    OP_EXTRACT_EXTERNAL_PROVIDER = 24;
+    OP_COMPRESS_ERROR = 25;
+    OP_EXTRACT_ERROR = 26;
+}
+
+enum SubFileOperation {
+    SUB_OP_UNKNOWN = 0;
+    SUB_OP_QUERY_DOC = 1;
+    SUB_OP_QUERY_CHILD = 2;
+    SUB_OP_OPEN_FILE = 3;
+    SUB_OP_READ_FILE = 4;
+    SUB_OP_CREATE_DOC = 5;
+    SUB_OP_WRITE_FILE = 6;
+    SUB_OP_DELETE_DOC = 7;
+    SUB_OP_OBTAIN_STREAM_TYPE = 8;
+    SUB_OP_QUICK_MOVE = 9;
+    SUB_OP_QUICK_COPY = 10;
+}
+
+enum CopyMoveOpMode {
+    MODE_UNKNOWN = 0;
+    MODE_PROVIDER = 1;
+    MODE_CONVERTED = 2;
+    MODE_CONVENTIONAL = 3;
+}
+
+enum Authority {
+    AUTH_UNKNOWN = 0;
+    AUTH_OTHER = 1;
+    AUTH_MEDIA = 2;
+    AUTH_STORAGE_INTERNAL = 3;
+    AUTH_STORAGE_EXTERNAL = 4;
+    AUTH_DOWNLOADS = 5;
+    AUTH_MTP = 6;
+}
+
+enum UserAction {
+    ACTION_UNKNOWN = 0;
+    ACTION_OTHER = 1;
+    ACTION_GRID = 2;
+    ACTION_LIST = 3;
+    ACTION_SORT_NAME = 4;
+    ACTION_SORT_DATE = 5;
+    ACTION_SORT_SIZE = 6;
+    ACTION_SORT_TYPE = 7;
+    ACTION_SEARCH = 8;
+    ACTION_SHOW_SIZE = 9;
+    ACTION_HIDE_SIZE = 10;
+    ACTION_SETTINGS = 11;
+    ACTION_COPY_TO = 12;
+    ACTION_MOVE_TO = 13;
+    ACTION_DELETE = 14;
+    ACTION_RENAME = 15;
+    ACTION_CREATE_DIR = 16;
+    ACTION_SELECT_ALL = 17;
+    ACTION_SHARE = 18;
+    ACTION_OPEN = 19;
+    ACTION_SHOW_ADVANCED = 20;
+    ACTION_HIDE_ADVANCED = 21;
+    ACTION_NEW_WINDOW = 22;
+    ACTION_PASTE_CLIPBOARD = 23;
+    ACTION_COPY_CLIPBOARD = 24;
+    ACTION_DRAG_N_DROP = 25;
+    ACTION_DRAG_N_DROP_MULTI_WINDOW = 26;
+    ACTION_CUT_CLIPBOARD = 27;
+    ACTION_COMPRESS = 28;
+    ACTION_EXTRACT_TO = 29;
+    ACTION_VIEW_IN_APPLICATION = 30;
+    ACTION_INSPECTOR = 31;
+}
+
+enum InvalidScopedAccess {
+    SCOPED_DIR_ACCESS_UNKNOWN = 0;
+    SCOPED_DIR_ACCESS_INVALID_ARGUMENTS = 1;
+    SCOPED_DIR_ACCESS_INVALID_DIRECTORY = 2;
+    SCOPED_DIR_ACCESS_ERROR = 3;
+    SCOPED_DIR_ACCESS_DEPRECATED = 4;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 594ae6b..988eac0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1595,7 +1595,7 @@
          @hide This should only be used by ManagedProvisioning app.
     -->
     <permission android:name="android.permission.NETWORK_MANAGED_PROVISIONING"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature" />
 
     <!-- #SystemApi @hide Allows applications to access information about LoWPAN interfaces.
          <p>Not for use by third-party applications. -->
@@ -1648,7 +1648,7 @@
          @hide
     -->
     <permission android:name="android.permission.SUSPEND_APPS"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|wellbeing" />
 
     <!-- Allows applications to discover and pair bluetooth devices.
          <p>Protection level: normal
@@ -2171,6 +2171,13 @@
     <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
         android:protectionLevel="signature|installer" />
 
+    <!-- @SystemApi Allows an application to start an activity within its managed profile from
+         the personal profile.
+         This permission is not available to third party applications.
+         @hide -->
+    <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
          users on the device. This permission is not available to
          third party applications. -->
@@ -4086,10 +4093,10 @@
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
 
-    <!-- Allows the holder to access and manage instant applications on the device.
-    @hide -->
+    <!-- @SystemApi Allows the holder to access and manage instant applications on the device.
+         @hide -->
     <permission android:name="android.permission.ACCESS_INSTANT_APPS"
-            android:protectionLevel="signature|installer|verifier" />
+            android:protectionLevel="signature|installer|verifier|wellbeing" />
     <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS"/>
 
     <!-- Allows the holder to view the instant applications on the device.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 089c59f..f8004ea 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -268,6 +268,9 @@
         <!-- Additional flag from base permission type: this permission can be automatically
             granted to the system default text classifier -->
         <flag name="textClassifier" value="0x10000" />
+        <!-- Additional flag from base permission type: this permission will be granted to the
+             wellbeing app, as defined by the OEM. -->
+        <flag name="wellbeing" value="0x20000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
@@ -1394,6 +1397,29 @@
 
     <attr name="usesNonSdkApi" format="boolean" />
 
+    <!-- Specify the type of foreground service. Apps targeting API
+     {@link android.os.Build.VERSION_CODES#Q} or later must specify foreground service type,
+     otherwise a SecurityException is thrown when
+     {@link android.app.Service#startForeground(int, Notification)} on this service is called.
+    -->
+    <attr name="foregroundServiceType">
+        <!-- Data (photo, file, account) upload/download, backup/restore, import/export, fetch,
+        transfer over network between device and cloud.  -->
+        <enum name="sync" value="1" />
+        <!-- Music, video, news or other media play. -->
+        <enum name="mediaPlay" value="2" />
+        <!-- Ongoing phone call or video conference. -->
+        <enum name="phoneCall" value="3" />
+        <!-- GPS, map, navigation location update. -->
+        <enum name="location" value="4" />
+        <!-- Auto, bluetooth, TV or other devices connection, monitoring and interaction. -->
+        <enum name="deviceCompanion" value="5" />
+        <!-- Process that should not be interrupted, including installation, setup, photo
+        compression etc. -->
+        <enum name="ongoingProcess" value="6" />
+    </attr>
+
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -2242,6 +2268,8 @@
              recommended to measure memory usage under typical workloads to determine
              whether it makes sense to use this flag. -->
         <attr name="useAppZygote" format="boolean" />
+        <!-- If this is a foreground service, specify its category. -->
+        <attr name="foregroundServiceType" />
     </declare-styleable>
 
     <!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 62ec5c4..101f92b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3349,6 +3349,13 @@
          See android.view.textclassifier.TextClassificationManager.
     -->
     <string name="config_defaultTextClassifierPackage" translatable="false"></string>
+    
+    <!-- The package name for the default wellbeing app.
+         This package must be trusted, as it has the permissions to control other applications
+         on the device.
+         Example: "com.android.wellbeing"
+     -->
+    <string name="config_defaultWellbeingPackage" translatable="false"></string>
 
     <!-- The package name for the system's content capture service.
          This service must be trusted, as it can be activated without explicit consent of the user.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5e8af62..d480121 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2929,6 +2929,7 @@
         <public name="dataUsedForMonetization" />
         <public name="dataRetentionTime" />
         <public name="selectionDividerHeight" />
+        <public name="foregroundServiceType" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 82a679e..b24cdba 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3265,6 +3265,7 @@
   <java-symbol type="string" name="notification_channel_do_not_disturb" />
   <java-symbol type="string" name="config_defaultAutofillService" />
   <java-symbol type="string" name="config_defaultTextClassifierPackage" />
+  <java-symbol type="string" name="config_defaultWellbeingPackage" />
   <java-symbol type="string" name="config_defaultContentCaptureService" />
   <java-symbol type="string" name="config_defaultAugmentedAutofillService" />
 
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index f1ed1c2..3a37fb6 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -248,6 +248,7 @@
                     Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
                     Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
                     Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
+                    Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
                     Settings.Global.ENHANCED_4G_MODE_ENABLED,
                     Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
                     Settings.Global.ERROR_LOGCAT_PREFIX,
@@ -414,6 +415,7 @@
                     Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
                     Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
                     Settings.Global.SHOW_TEMPERATURE_WARNING,
+                    Settings.Global.SIGNED_CONFIG_VERSION,
                     Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
                     Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
                     Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED,
@@ -469,7 +471,9 @@
                     Settings.Global.GPU_DEBUG_APP,
                     Settings.Global.GPU_DEBUG_LAYERS,
                     Settings.Global.GPU_DEBUG_LAYERS_GLES,
-                    Settings.Global.ANGLE_ENABLED_APP,
+                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
+                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
+                    Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
                     Settings.Global.UPDATED_GFX_DRIVER_DEV_OPT_IN_APP,
                     Settings.Global.GPU_DEBUG_LAYER_APP,
                     Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
@@ -538,7 +542,8 @@
                     Settings.Global.OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION,
                     Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
                     Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
-                    Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS);
+                    Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
+                    Settings.Global.BACKUP_MULTI_USER_ENABLED);
     private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
              newHashSet(
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index 108585d..04e8802 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -16,6 +16,10 @@
 
 package android.provider;
 
+import static org.hamcrest.Matchers.aMapWithSize;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.junit.Assert.assertThat;
+
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -26,6 +30,7 @@
 import android.content.pm.UserInfo;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.test.AndroidTestCase;
@@ -33,10 +38,19 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.test.suitebuilder.annotation.Suppress;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /** Unit test for SettingsProvider. */
 public class SettingsProviderTest extends AndroidTestCase {
+    /**
+     * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
+     *     API.
+     */
+    private static final Uri CONFIG_CONTENT_URI =
+            Uri.parse("content://" + Settings.AUTHORITY + "/config");
+
     @MediumTest
     public void testNameValueCache() {
         ContentResolver r = getContext().getContentResolver();
@@ -379,4 +393,109 @@
 
         assertTrue(ssaid.equals(ssaid2));
     }
+
+    @MediumTest
+    public void testCall_putAndGetConfig() {
+        ContentResolver r = getContext().getContentResolver();
+        String name = "key1";
+        String value = "value1";
+        String newValue = "value2";
+        Bundle args = new Bundle();
+        args.putString(Settings.NameValueTable.VALUE, value);
+
+        try {
+            // value is empty
+            Bundle results =
+                    r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+            assertNull(results.get(Settings.NameValueTable.VALUE));
+
+            // save value
+            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+            assertNull(results);
+
+            // value is no longer empty
+            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+            assertEquals(value, results.get(Settings.NameValueTable.VALUE));
+
+            // save new value
+            args.putString(Settings.NameValueTable.VALUE, newValue);
+            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+
+            // new value is returned
+            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+            assertEquals(newValue, results.get(Settings.NameValueTable.VALUE));
+        } finally {
+            // clean up
+            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+        }
+    }
+
+    @MediumTest
+    public void testCall_deleteConfig() {
+        ContentResolver r = getContext().getContentResolver();
+        String name = "key1";
+        String value = "value1";
+        Bundle args = new Bundle();
+        args.putString(Settings.NameValueTable.VALUE, value);
+
+        try {
+            // save value
+            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+
+            // get value
+            Bundle results =
+                    r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+            assertEquals(value, results.get(Settings.NameValueTable.VALUE));
+
+            // delete value
+            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+
+            // value is empty now
+            results = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, name, null);
+            assertNull(results.get(Settings.NameValueTable.VALUE));
+        } finally {
+            // clean up
+            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+        }
+    }
+
+    @MediumTest
+    public void testCall_listConfig() {
+        ContentResolver r = getContext().getContentResolver();
+        String prefix = "foo";
+        String newPrefix = "bar";
+        String name = prefix + "/" + "key1";
+        String newName = newPrefix + "/" + "key1";
+        String value = "value1";
+        String newValue = "value2";
+        Bundle args = new Bundle();
+        args.putString(Settings.NameValueTable.VALUE, value);
+
+        try {
+            // save both values
+            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, name, args);
+            args.putString(Settings.NameValueTable.VALUE, newValue);
+            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, newName, args);
+
+            // list all values
+            Bundle result = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_LIST_CONFIG,
+                    null, null);
+            Map<String, String> keyValueMap =
+                    (HashMap) result.getSerializable(Settings.NameValueTable.VALUE);
+            assertThat(keyValueMap.size(), greaterThanOrEqualTo(2));
+            assertEquals(value, keyValueMap.get(name));
+            assertEquals(newValue, keyValueMap.get(newName));
+
+            // list values for prefix
+            args.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
+            result = r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_LIST_CONFIG, null, args);
+            keyValueMap = (HashMap) result.getSerializable(Settings.NameValueTable.VALUE);
+            assertThat(keyValueMap, aMapWithSize(1));
+            assertEquals(value, keyValueMap.get(name));
+        } finally {
+            // clean up
+            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, name, null);
+            r.call(CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, newName, null);
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
index 8d6fbd5..61ab152 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
@@ -28,7 +28,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-// TODO(b/73862682): Add tests for RecoveryCertPath
+import java.security.cert.CertPath;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class KeyChainSnapshotTest {
@@ -43,35 +44,41 @@
     private static final int USER_SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
     private static final String KEY_ALIAS = "steph";
     private static final byte[] KEY_MATERIAL = new byte[] { 3, 5, 7, 9, 1 };
+    private static final CertPath CERT_PATH = TestData.getThmCertPath();
 
     @Test
-    public void build_setsCounterId() {
+    public void build_setsCounterId() throws Exception {
         assertEquals(COUNTER_ID, createKeyChainSnapshot().getCounterId());
     }
 
     @Test
-    public void build_setsSnapshotVersion() {
+    public void build_setsSnapshotVersion() throws Exception {
         assertEquals(SNAPSHOT_VERSION, createKeyChainSnapshot().getSnapshotVersion());
     }
 
     @Test
-    public void build_setsMaxAttempts() {
+    public void build_setsMaxAttempts() throws Exception {
         assertEquals(MAX_ATTEMPTS, createKeyChainSnapshot().getMaxAttempts());
     }
 
     @Test
-    public void build_setsServerParams() {
+    public void build_setsServerParams() throws Exception {
         assertArrayEquals(SERVER_PARAMS, createKeyChainSnapshot().getServerParams());
     }
 
     @Test
-    public void build_setsRecoveryKeyBlob() {
+    public void build_setsRecoveryKeyBlob() throws Exception {
         assertArrayEquals(RECOVERY_KEY_BLOB,
                 createKeyChainSnapshot().getEncryptedRecoveryKeyBlob());
     }
 
     @Test
-    public void build_setsKeyChainProtectionParams() {
+    public void build_setsCertPath() throws Exception {
+        assertEquals(CERT_PATH, createKeyChainSnapshot().getTrustedHardwareCertPath());
+    }
+
+    @Test
+    public void build_setsKeyChainProtectionParams() throws Exception {
         KeyChainSnapshot snapshot = createKeyChainSnapshot();
 
         assertEquals(1, snapshot.getKeyChainProtectionParams().size());
@@ -85,7 +92,7 @@
     }
 
     @Test
-    public void build_setsWrappedApplicationKeys() {
+    public void build_setsWrappedApplicationKeys() throws Exception {
         KeyChainSnapshot snapshot = createKeyChainSnapshot();
 
         assertEquals(1, snapshot.getWrappedApplicationKeys().size());
@@ -95,42 +102,49 @@
     }
 
     @Test
-    public void writeToParcel_writesCounterId() {
+    public void writeToParcel_writesCounterId() throws Exception {
         KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
 
         assertEquals(COUNTER_ID, snapshot.getCounterId());
     }
 
     @Test
-    public void writeToParcel_writesSnapshotVersion() {
+    public void writeToParcel_writesSnapshotVersion() throws Exception {
         KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
 
         assertEquals(SNAPSHOT_VERSION, snapshot.getSnapshotVersion());
     }
 
     @Test
-    public void writeToParcel_writesMaxAttempts() {
+    public void writeToParcel_writesMaxAttempts() throws Exception {
         KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
 
         assertEquals(MAX_ATTEMPTS, snapshot.getMaxAttempts());
     }
 
     @Test
-    public void writeToParcel_writesServerParams() {
+    public void writeToParcel_writesServerParams() throws Exception {
         KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
 
         assertArrayEquals(SERVER_PARAMS, snapshot.getServerParams());
     }
 
     @Test
-    public void writeToParcel_writesKeyRecoveryBlob() {
+    public void writeToParcel_writesKeyRecoveryBlob() throws Exception {
         KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
 
         assertArrayEquals(RECOVERY_KEY_BLOB, snapshot.getEncryptedRecoveryKeyBlob());
     }
 
     @Test
-    public void writeToParcel_writesKeyChainProtectionParams() {
+    public void writeToParcel_writesCertPath() throws Exception {
+        KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
+
+        assertEquals(CERT_PATH, snapshot.getTrustedHardwareCertPath());
+    }
+
+    @Test
+    public void writeToParcel_writesKeyChainProtectionParams() throws Exception {
         KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
 
         assertEquals(1, snapshot.getKeyChainProtectionParams().size());
@@ -144,7 +158,7 @@
     }
 
     @Test
-    public void writeToParcel_writesWrappedApplicationKeys() {
+    public void writeToParcel_writesWrappedApplicationKeys() throws Exception {
         KeyChainSnapshot snapshot = writeToThenReadFromParcel(createKeyChainSnapshot());
 
         assertEquals(1, snapshot.getWrappedApplicationKeys().size());
@@ -153,7 +167,7 @@
         assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial());
     }
 
-    private static KeyChainSnapshot createKeyChainSnapshot() {
+    private static KeyChainSnapshot createKeyChainSnapshot() throws Exception {
         return new KeyChainSnapshot.Builder()
                 .setCounterId(COUNTER_ID)
                 .setSnapshotVersion(SNAPSHOT_VERSION)
@@ -162,6 +176,7 @@
                 .setEncryptedRecoveryKeyBlob(RECOVERY_KEY_BLOB)
                 .setKeyChainProtectionParams(Lists.newArrayList(createKeyChainProtectionParams()))
                 .setWrappedApplicationKeys(Lists.newArrayList(createWrappedApplicationKey()))
+                .setTrustedHardwareCertPath(CERT_PATH)
                 .build();
     }
 
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
new file mode 100644
index 0000000..dd8cd8d
--- /dev/null
+++ b/core/tests/coretests/src/android/security/keystore/recovery/RecoveryCertPathTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.security.keystore.recovery;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+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;
+
+import java.security.cert.CertificateException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RecoveryCertPathTest {
+
+    @Test
+    public void createRecoveryCertPath_getCertPath_succeeds() throws Exception {
+        RecoveryCertPath recoveryCertPath = RecoveryCertPath.createRecoveryCertPath(
+                TestData.getThmCertPath());
+        assertEquals(TestData.getThmCertPath(), recoveryCertPath.getCertPath());
+    }
+
+    @Test
+    public void getCertPath_throwsIfCannnotDecode() {
+        Parcel parcel = Parcel.obtain();
+        parcel.writeByteArray(new byte[]{0, 1, 2, 3});
+        parcel.setDataPosition(0);
+        RecoveryCertPath recoveryCertPath = RecoveryCertPath.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        try {
+            recoveryCertPath.getCertPath();
+            fail("Did not throw when attempting to decode invalid cert path");
+        } catch (CertificateException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void writeToParcel_writesCertPath() throws Exception {
+        RecoveryCertPath recoveryCertPath =
+                writeToThenReadFromParcel(
+                        RecoveryCertPath.createRecoveryCertPath(TestData.getThmCertPath()));
+        assertEquals(TestData.getThmCertPath(), recoveryCertPath.getCertPath());
+    }
+
+    private RecoveryCertPath writeToThenReadFromParcel(RecoveryCertPath recoveryCertPath) {
+        Parcel parcel = Parcel.obtain();
+        recoveryCertPath.writeToParcel(parcel, /*flags=*/ 0);
+        parcel.setDataPosition(0);
+        RecoveryCertPath fromParcel = RecoveryCertPath.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+        return fromParcel;
+    }
+}
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/TestData.java b/core/tests/coretests/src/android/security/keystore/recovery/TestData.java
new file mode 100644
index 0000000..829a92a
--- /dev/null
+++ b/core/tests/coretests/src/android/security/keystore/recovery/TestData.java
@@ -0,0 +1,88 @@
+/*
+ * 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.security.keystore.recovery;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateFactory;
+import java.util.Base64;
+
+/** This class provides data for testing purposes. */
+class TestData {
+
+    private static final String THM_CERT_PATH_BASE64 = ""
+            + "MIIIXTCCBRowggMCoAMCAQICEB35ZwzVpI9ssXg9SAehnU0wDQYJKoZIhvcNAQEL"
+            + "BQAwMTEvMC0GA1UEAxMmR29vZ2xlIENsb3VkIEtleSBWYXVsdCBTZXJ2aWNlIFJv"
+            + "b3QgQ0EwHhcNMTgwNTA3MTg1ODEwWhcNMjgwNTA4MTg1ODEwWjA5MTcwNQYDVQQD"
+            + "Ey5Hb29nbGUgQ2xvdWQgS2V5IFZhdWx0IFNlcnZpY2UgSW50ZXJtZWRpYXRlIENB"
+            + "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA73TrvH3j6zEimpcc32tx"
+            + "2iupWwfyzdE5l4Ejc5EBYzx0aZH6b/KDuutwustk0IoyjlGySMBz/21YgWejIm+n"
+            + "duAlpk7WY5kYHp0XWtzdmxZknmWTqugPeNZeiKEjoDmpyIbY6N+f13hQ2RVh+WDT"
+            + "EowQ/i04WBL75chshlIG+3A42g5Qr7DZEKdT9oJQqkntzj0cGyJ5X8BwjeTiJrvY"
+            + "k2Kn/0555/Kpp65G3Rf29VPPU3i67kthAT3SavLBpH03S4WZ+QlfrAiGQziydtz9"
+            + "t7mSk1xefjax5ZWAuJAfCbKfI3VWAcaUr4P57BzmDcSi0jgs1aM3t2BrPfAMRxWv"
+            + "35yDZnrC+HipzkjyDGBfHmFgoglyhc9e/Kj3mSusO0Rq1wguVXKs2hKXRoaGJuHt"
+            + "e3YIwTC1pLznqvolhD1nPoXf8rMzgHRzlc9H8iXsgB1p7975nh5WCPrMDX2eAmYd"
+            + "a0xTMccTeBzIM2ohxQsxlh5rsjXVNU3ihbWkHquzIiwFcAtldP3dMksj0dn/DnYD"
+            + "yokjEgU/z2I216E93x9hmKkEk6Pp7o8t/z6lwMT9FJIuzp7NREnWCSi+e5s2E7FD"
+            + "j6S7xY2zEIUHrmwuuJc0jzJnwdZ+0myinaTmBDvBXR5cU1cmEAZoheCAoRv9Z/6o"
+            + "ASczLF0C4uuVfA5GXcAm14cCAwEAAaMmMCQwDgYDVR0PAQH/BAQDAgGGMBIGA1Ud"
+            + "EwEB/wQIMAYBAf8CAQEwDQYJKoZIhvcNAQELBQADggIBAEPht79yQm8woQbPB1Bs"
+            + "eotkzJtTWTO9fnIWwNiRfQ3vJFXf69ghE77wUS13Ez3FlgNPj0Qxmg5ouE0d2yYV"
+            + "4AUrXnEGZELcyN2XHRXyNK0zXgnr3x6eZyY7QfgGKJgkyja5TS6ZPWGyaLKhClS0"
+            + "AYZSzWJtz0+AkGCdTbmyy7ShdXJ+GfnmssbndZA62VhcjeQmHsDq7V3PKAsp4/B9"
+            + "PzcnTrgkUFNnP1F1pr7JpUUX3xyRFy6gjIrUx1fcOFRxFYPWGLLMZ6P41rafm+M/"
+            + "CbBNr5CY7NrZjr34jLqWycfYes49o9OK44X/wPrxj0Sjg+VrW21+AJ9vrM7DS5hE"
+            + "QX1lDbDtQGkk3N1vgCTo6xt9LXsEu4xUT5bk7YAfpJqM0ltDFPwYAGCbjSkVT/M5"
+            + "JVZkKiUW668Us67x8yZc/5bxbvTA+5xrYhak/VYIBY6qub4J+bKwadw6uBgxnq4P"
+            + "hwgwjfaoJy9YAXCswjCtaE9GwkVmRnJE9vFjJ33IGf37hFTYEHBFy4FomVmQwRFZ"
+            + "TIe7tkKDq9i18F7lzBPJPO6wEG8bxi4csatrjcVHR9erpY5u6ebtkKG8qsan9qzh"
+            + "iWAgSytiT++HejZeoQ+RRgQWjupjdDo5/0oSdQqvaN8Ah6C2J+ecCZ12Lu0FwF+t"
+            + "t9Ie3pF6W8TzxzuMdFWq+afvMIIDOzCCASOgAwIBAgIRAOTj/iNQb6/Qit7zAW9n"
+            + "cL0wDQYJKoZIhvcNAQELBQAwOTE3MDUGA1UEAxMuR29vZ2xlIENsb3VkIEtleSBW"
+            + "YXVsdCBTZXJ2aWNlIEludGVybWVkaWF0ZSBDQTAeFw0xODA1MDcyMjE4MTFaFw0y"
+            + "MzA1MDgyMjE4MTFaMDIxMDAuBgNVBAMTJ0dvb2dsZSBDbG91ZCBLZXkgVmF1bHQg"
+            + "U2VydmljZSBFbmRwb2ludDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABI4MEUp5"
+            + "IHwATNfpBuJYIUX6JMsHZt798YO0JlWYy6nVVa1lxf9c+xxONJh+T5aio370RlIE"
+            + "uiq5R7vCHt0VGsCjEDAOMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB"
+            + "AGf6QU58lU+gGzy8hnp0suR/ixC++CExtf39pDHkdfU/e3ui4ROR+pjQ5F7okDFW"
+            + "eKSCNcJZ7tyXMJ9g7/I0qVY8Bj/gRnlVokdl/wD5PiL9GIzqWfnHNe3T+xrAAAgO"
+            + "D0bEmjgwNYmekfUIYQczd04d7ZMGnmAkpVH/0O2mf9q5x9fMlbKuAygUqQ/gmnlg"
+            + "xKfl9DSRWi4oMBOqlKlCRP1XAh3anu92+M/EhsFbyc07CWZY0SByX5M/cHVMLhUX"
+            + "jZHvcYLyOmJWJmXznidgyNeIR6t9yDB55iCt7WSn3qMY+9vA9ELzt8jYpBNaKc0G"
+            + "bWQkRzYWegkf4kMis98eQ3SnAKbRz6669nmuAdxKs9/LK6BlFOFw1xvsTRQ96dBa"
+            + "oiX2XGhou+Im0Td/AMs0Aigz2N+Ujq/yW//35GZQfdGGIYtFbkcltStygjIJyAM1"
+            + "pBhyBBkJhOhRpO4fXh98aq8H5J7R9i5A9WpnDstAxPxcNCDWn0O/WxhPvVZkFTpi"
+            + "NXh9dnlJ/kZe+j+z5ZMaxW435drLPx2AQKjXA9GgGrFPltTUyGycmEGtuxLvSnm/"
+            + "zPlmk5FUk7x2wEr0+bZ3cx0JHHgAtgXpe0jkDi8Bw8O3X7mUOjxVhYU6auiYJezW"
+            + "9LGmweaKwYvS04UCWOReolUVexob9LI/VX1JrrwD3s7k";
+
+    static CertPath getThmCertPath() {
+        try {
+            return decodeCertPath(THM_CERT_PATH_BASE64);
+        } catch (Exception e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static CertPath decodeCertPath(String base64CertPath) throws Exception {
+        byte[] certPathBytes = Base64.getDecoder().decode(base64CertPath);
+        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        return certFactory.generateCertPath(new ByteArrayInputStream(certPathBytes), "PkiPath");
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index 91a5440..aaf7312 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -189,6 +189,7 @@
                 Instant.ofEpochMilli(946771200000L),  // 2000-01-02
                 ZoneId.of("UTC"));
         final String text = "text";
+        final String packageName = "packageName";
 
         final TextClassification.Request reference =
                 new TextClassification.Request.Builder(text, 0, text.length())
@@ -196,6 +197,7 @@
                         .setReferenceTime(referenceTime)
                         .setExtras(BUNDLE)
                         .build();
+        reference.setCallingPackageName(packageName);
 
         // Parcel and unparcel.
         final Parcel parcel = Parcel.obtain();
@@ -204,12 +206,13 @@
         final TextClassification.Request result =
                 TextClassification.Request.CREATOR.createFromParcel(parcel);
 
-        assertEquals(text, result.getText());
+        assertEquals(text, result.getText().toString());
         assertEquals(0, result.getStartIndex());
         assertEquals(text.length(), result.getEndIndex());
         assertEquals(referenceTime, result.getReferenceTime());
         assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
         assertEquals(referenceTime, result.getReferenceTime());
         assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+        assertEquals(packageName, result.getCallingPackageName());
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
index 75ca769..1dcaed6 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -69,10 +69,12 @@
         final String bundleKey = "experiment.str";
         final Bundle bundle = new Bundle();
         bundle.putString(bundleKey, "bundle");
+        final String packageName = "packageName";
 
         final TextLanguage.Request reference = new TextLanguage.Request.Builder(text)
                 .setExtras(bundle)
                 .build();
+        reference.setCallingPackageName(packageName);
 
         final Parcel parcel = Parcel.obtain();
         reference.writeToParcel(parcel, 0);
@@ -81,5 +83,6 @@
 
         assertEquals(text, result.getText());
         assertEquals("bundle", result.getExtras().getString(bundleKey));
+        assertEquals(packageName, result.getCallingPackageName());
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index f6ec0e6..f022d04 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -64,7 +64,7 @@
     public void testParcel() {
         final String fullText = "this is just a test";
         final TextLinks reference = new TextLinks.Builder(fullText)
-                .addLink(0, 4, getEntityScores(0.f, 0.f, 1.f))
+                .addLink(0, 4, getEntityScores(0.f, 0.f, 1.f), BUNDLE)
                 .addLink(5, 12, getEntityScores(.8f, .1f, .5f))
                 .setExtras(BUNDLE)
                 .build();
@@ -82,6 +82,7 @@
         assertEquals(1, resultList.get(0).getEntityCount());
         assertEquals(TextClassifier.TYPE_OTHER, resultList.get(0).getEntity(0));
         assertEquals(1.f, resultList.get(0).getConfidenceScore(TextClassifier.TYPE_OTHER), 1e-7f);
+        assertEquals(BUNDLE_VALUE, resultList.get(0).getExtras().getString(BUNDLE_KEY));
         assertEquals(5, resultList.get(1).getStart());
         assertEquals(12, resultList.get(1).getEnd());
         assertEquals(3, resultList.get(1).getEntityCount());
@@ -96,6 +97,7 @@
 
     @Test
     public void testParcelOptions() {
+        final String packageName = "packageName";
         final TextClassifier.EntityConfig entityConfig = TextClassifier.EntityConfig.create(
                 Arrays.asList(TextClassifier.HINT_TEXT_IS_EDITABLE),
                 Arrays.asList("a", "b", "c"),
@@ -105,6 +107,7 @@
                 .setEntityConfig(entityConfig)
                 .setExtras(BUNDLE)
                 .build();
+        reference.setCallingPackageName(packageName);
 
         // Parcel and unparcel.
         final Parcel parcel = Parcel.obtain();
@@ -119,5 +122,6 @@
         assertEquals(new HashSet<String>(Arrays.asList("a", "c")),
                 result.getEntityConfig().resolveEntityListModifications(Collections.emptyList()));
         assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+        assertEquals(packageName, result.getCallingPackageName());
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 7ea5108b..2ea49f7 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -75,11 +75,13 @@
     @Test
     public void testParcelRequest() {
         final String text = "text";
+        final String packageName = "packageName";
         final TextSelection.Request reference =
                 new TextSelection.Request.Builder(text, 0, text.length())
                         .setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
                         .setExtras(BUNDLE)
                         .build();
+        reference.setCallingPackageName(packageName);
 
         // Parcel and unparcel.
         final Parcel parcel = Parcel.obtain();
@@ -87,10 +89,11 @@
         parcel.setDataPosition(0);
         final TextSelection.Request result = TextSelection.Request.CREATOR.createFromParcel(parcel);
 
-        assertEquals(text, result.getText());
+        assertEquals(text, result.getText().toString());
         assertEquals(0, result.getStartIndex());
         assertEquals(text.length(), result.getEndIndex());
         assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
         assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+        assertEquals(packageName, result.getCallingPackageName());
     }
 }
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 141948f..4a2db0a 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -225,15 +225,18 @@
     <library name="android.test.base"
             file="/system/framework/android.test.base.impl.jar" />
     <library name="android.test.mock"
-            file="/system/framework/android.test.mock.impl.jar" />
+            file="/system/framework/android.test.mock.impl.jar"
+            dependency="android.test.base" />
     <library name="android.test.runner"
-            file="/system/framework/android.test.runner.impl.jar" />
+            file="/system/framework/android.test.runner.impl.jar"
+            dependency="android.test.base:android.test.mock" />
 
     <!-- In BOOT_JARS historically, and now added to legacy applications. -->
     <library name="android.hidl.base-V1.0-java"
             file="/system/framework/android.hidl.base-V1.0-java.jar" />
     <library name="android.hidl.manager-V1.0-java"
-            file="/system/framework/android.hidl.manager-V1.0-java.jar" />
+            file="/system/framework/android.hidl.manager-V1.0-java.jar"
+            dependency="android.hidl.base-V1.0-java" />
 
     <!-- These are the standard packages that are white-listed to always have internet
          access while in power save mode, even if they aren't in the foreground. -->
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ed7d5e5..d22eaf3 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -178,6 +178,7 @@
         "renderthread/CanvasContext.cpp",
         "renderthread/DrawFrameTask.cpp",
         "renderthread/EglManager.cpp",
+        "renderthread/ReliableSurface.cpp",
         "renderthread/VulkanManager.cpp",
         "renderthread/RenderProxy.cpp",
         "renderthread/RenderTask.cpp",
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index c63e449..0b847af 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -912,18 +912,6 @@
     fDL->drawAnnotation(rect, key, val);
 }
 
-void RecordingCanvas::onDrawText(const void* text, size_t bytes, SkScalar x, SkScalar y,
-                                 const SkPaint& paint) {
-    fDL->drawText(text, bytes, x, y, paint);
-}
-void RecordingCanvas::onDrawPosText(const void* text, size_t bytes, const SkPoint pos[],
-                                    const SkPaint& paint) {
-    fDL->drawPosText(text, bytes, pos, paint);
-}
-void RecordingCanvas::onDrawPosTextH(const void* text, size_t bytes, const SkScalar xs[],
-                                     SkScalar y, const SkPaint& paint) {
-    fDL->drawPosTextH(text, bytes, xs, y, paint);
-}
 void RecordingCanvas::onDrawTextRSXform(const void* text, size_t bytes, const SkRSXform xform[],
                                         const SkRect* cull, const SkPaint& paint) {
     fDL->drawTextRSXform(text, bytes, xform, cull, paint);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 08cfc62..35cf707 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -170,10 +170,6 @@
     void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
     void onDrawAnnotation(const SkRect&, const char[], SkData*) override;
 
-    void onDrawText(const void*, size_t, SkScalar x, SkScalar y, const SkPaint&) override;
-    void onDrawPosText(const void*, size_t, const SkPoint[], const SkPaint&) override;
-    void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&) override;
-
     void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
                            const SkPaint&) override;
     void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override;
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 2062194..2b5d580 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -82,18 +82,6 @@
         mOutput << mIdent << "drawDRRect" << std::endl;
     }
 
-    void onDrawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&) override {
-        mOutput << mIdent << "drawText" << std::endl;
-    }
-
-    void onDrawPosText(const void*, size_t, const SkPoint[], const SkPaint&) override {
-        mOutput << mIdent << "drawPosText" << std::endl;
-    }
-
-    void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&) override {
-        mOutput << mIdent << "drawPosTextH" << std::endl;
-    }
-
     void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
                            const SkPaint&) override {
         mOutput << mIdent << "drawTextRSXform" << std::endl;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 142bca9..07979a2 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -155,7 +155,7 @@
     }
 }
 
-bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
+bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
                                     ColorMode colorMode) {
     if (mEglSurface != EGL_NO_SURFACE) {
         mEglManager.destroySurface(mEglSurface);
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 4ab3541..47991069 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -42,7 +42,7 @@
     bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
     DeferredLayerUpdater* createTextureLayer() override;
-    bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
+    bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
                     renderthread::ColorMode colorMode) override;
     void onStop() override;
     bool isSurfaceReady() override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 3607b23..437b5dc 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -115,7 +115,7 @@
 
 void SkiaVulkanPipeline::onStop() {}
 
-bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
+bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
                                     ColorMode colorMode) {
     if (mVkSurface) {
         mVkManager.destroySurface(mVkSurface);
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 14c0d69..02874c7 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -38,7 +38,7 @@
     bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
     DeferredLayerUpdater* createTextureLayer() override;
-    bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
+    bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
                     renderthread::ColorMode colorMode) override;
     void onStop() override;
     bool isSurfaceReady() override;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 6869972..4e4262c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -142,7 +142,12 @@
 void CanvasContext::setSurface(sp<Surface>&& surface) {
     ATRACE_CALL();
 
-    mNativeSurface = std::move(surface);
+    if (surface) {
+        mNativeSurface = new ReliableSurface{std::move(surface)};
+        mNativeSurface->setDequeueTimeout(500_ms);
+    } else {
+        mNativeSurface = nullptr;
+    }
 
     ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
     bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode);
@@ -285,10 +290,11 @@
 
     info.damageAccumulator = &mDamageAccumulator;
     info.layerUpdateQueue = &mLayerUpdateQueue;
+    info.out.canDrawThisFrame = true;
 
     mAnimationContext->startFrame(info.mode);
     mRenderPipeline->onPrepareTree();
-    for (const sp<RenderNode>& node : mRenderNodes) {
+    for (const sp<RenderNode> &node : mRenderNodes) {
         // Only the primary target node will be drawn full - all other nodes would get drawn in
         // real time mode. In case of a window, the primary node is the window content and the other
         // node(s) are non client / filler nodes.
@@ -304,7 +310,7 @@
 
     mIsDirty = true;
 
-    if (CC_UNLIKELY(!mNativeSurface.get())) {
+    if (CC_UNLIKELY(!hasSurface())) {
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
         info.out.canDrawThisFrame = false;
         return;
@@ -312,7 +318,7 @@
 
     if (CC_LIKELY(mSwapHistory.size() && !Properties::forceDrawFrame)) {
         nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
-        SwapHistory& lastSwap = mSwapHistory.back();
+        SwapHistory &lastSwap = mSwapHistory.back();
         nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
         // The slight fudge-factor is to deal with cases where
         // the vsync was estimated due to being slow handling the signal.
@@ -333,7 +339,19 @@
         info.out.canDrawThisFrame = false;
     }
 
-    if (!info.out.canDrawThisFrame) {
+    if (info.out.canDrawThisFrame) {
+        int err = mNativeSurface->reserveNext();
+        if (err != OK) {
+            mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+            info.out.canDrawThisFrame = false;
+            ALOGW("reserveNext failed, error = %d (%s)", err, strerror(-err));
+            if (err != TIMED_OUT) {
+                // A timed out surface can still recover, but assume others are permanently dead.
+                setSurface(nullptr);
+                return;
+            }
+        }
+    } else {
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
     }
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 70be4a6..9e7abf4 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -25,6 +25,7 @@
 #include "IRenderPipeline.h"
 #include "LayerUpdateQueue.h"
 #include "RenderNode.h"
+#include "ReliableSurface.h"
 #include "renderthread/RenderTask.h"
 #include "renderthread/RenderThread.h"
 #include "thread/Task.h"
@@ -219,7 +220,7 @@
     EGLint mLastFrameHeight = 0;
 
     RenderThread& mRenderThread;
-    sp<Surface> mNativeSurface;
+    sp<ReliableSurface> mNativeSurface;
     // stopped indicates the CanvasContext will reject actual redraw operations,
     // and defer repaint until it is un-stopped
     bool mStopped = false;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 65ced6a..8230dfd 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -31,6 +31,8 @@
 
 #include <string>
 #include <vector>
+#include <system/window.h>
+#include <gui/Surface.h>
 
 #define GLES_VERSION 2
 
@@ -106,7 +108,7 @@
     LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
                         "Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());
 
-    ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
+    ALOGV("Initialized EGL, version %d.%d", (int)major, (int)minor);
 
     initExtensions();
 
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 4972554..42e17b273 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -28,9 +28,9 @@
 
 class GrContext;
 
-namespace android {
+struct ANativeWindow;
 
-class Surface;
+namespace android {
 
 namespace uirenderer {
 
@@ -67,7 +67,7 @@
     virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
                              FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
     virtual DeferredLayerUpdater* createTextureLayer() = 0;
-    virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
+    virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
     virtual void onStop() = 0;
     virtual bool isSurfaceReady() = 0;
     virtual bool isContextReady() = 0;
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
new file mode 100644
index 0000000..6f2b9df
--- /dev/null
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReliableSurface.h"
+
+#include <private/android/AHardwareBufferHelpers.h>
+
+namespace android::uirenderer::renderthread {
+
+// TODO: Re-enable after addressing more of the TODO's
+// With this disabled we won't have a good up-front signal that the surface is no longer valid,
+// however we can at least handle that reactively post-draw. There's just not a good mechanism
+// to propagate this error back to the caller
+constexpr bool DISABLE_BUFFER_PREFETCH = true;
+
+// TODO: Make surface less protected
+// This exists because perform is a varargs, and ANativeWindow has no va_list perform.
+// So wrapping/chaining that is hard. Telling the compiler to ignore protected is easy, so we do
+// that instead
+struct SurfaceExposer : Surface {
+    // Make warnings happy
+    SurfaceExposer() = delete;
+
+    using Surface::setBufferCount;
+    using Surface::setSwapInterval;
+    using Surface::dequeueBuffer;
+    using Surface::queueBuffer;
+    using Surface::cancelBuffer;
+    using Surface::lockBuffer_DEPRECATED;
+    using Surface::perform;
+};
+
+#define callProtected(surface, func, ...) ((*surface).*&SurfaceExposer::func)(__VA_ARGS__)
+
+ReliableSurface::ReliableSurface(sp<Surface>&& surface) : mSurface(std::move(surface)) {
+    LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr");
+
+    ANativeWindow::setSwapInterval = hook_setSwapInterval;
+    ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
+    ANativeWindow::cancelBuffer = hook_cancelBuffer;
+    ANativeWindow::queueBuffer = hook_queueBuffer;
+    ANativeWindow::query = hook_query;
+    ANativeWindow::perform = hook_perform;
+
+    ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
+    ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
+    ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
+    ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
+}
+
+ReliableSurface::~ReliableSurface() {
+    clearReservedBuffer();
+}
+
+void ReliableSurface::perform(int operation, va_list args) {
+    std::lock_guard _lock{mMutex};
+
+    switch (operation) {
+        case NATIVE_WINDOW_SET_USAGE:
+            mUsage = va_arg(args, uint32_t);
+            break;
+        case NATIVE_WINDOW_SET_USAGE64:
+            mUsage = va_arg(args, uint64_t);
+            break;
+        case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
+            /* width */ va_arg(args, uint32_t);
+            /* height */ va_arg(args, uint32_t);
+            mFormat = va_arg(args, PixelFormat);
+            break;
+        case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
+            mFormat = va_arg(args, PixelFormat);
+            break;
+    }
+}
+
+int ReliableSurface::reserveNext() {
+    {
+        std::lock_guard _lock{mMutex};
+        if (mReservedBuffer) {
+            ALOGW("reserveNext called but there was already a buffer reserved?");
+            return OK;
+        }
+        if (mInErrorState) {
+            return UNKNOWN_ERROR;
+        }
+        if (mHasDequeuedBuffer) {
+            return OK;
+        }
+        if constexpr (DISABLE_BUFFER_PREFETCH) {
+            return OK;
+        }
+    }
+
+    // TODO: Update this to better handle when requested dimensions have changed
+    // Currently the driver does this via query + perform but that's after we've already
+    // reserved a buffer. Should we do that logic instead? Or should we drop
+    // the backing Surface to the ground and go full manual on the IGraphicBufferProducer instead?
+
+    int fenceFd = -1;
+    ANativeWindowBuffer* buffer = nullptr;
+    int result = callProtected(mSurface, dequeueBuffer, &buffer, &fenceFd);
+
+    {
+        std::lock_guard _lock{mMutex};
+        LOG_ALWAYS_FATAL_IF(mReservedBuffer, "race condition in reserveNext");
+        mReservedBuffer = buffer;
+        mReservedFenceFd.reset(fenceFd);
+    }
+
+    return result;
+}
+
+void ReliableSurface::clearReservedBuffer() {
+    ANativeWindowBuffer* buffer = nullptr;
+    int releaseFd = -1;
+    {
+        std::lock_guard _lock{mMutex};
+        if (mReservedBuffer) {
+            ALOGW("Reserved buffer %p was never used", mReservedBuffer);
+            buffer = mReservedBuffer;
+            releaseFd = mReservedFenceFd.release();
+        }
+        mReservedBuffer = nullptr;
+        mReservedFenceFd.reset();
+        mHasDequeuedBuffer = false;
+    }
+    if (buffer) {
+        callProtected(mSurface, cancelBuffer, buffer, releaseFd);
+    }
+}
+
+int ReliableSurface::cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
+    clearReservedBuffer();
+    if (isFallbackBuffer(buffer)) {
+        if (fenceFd > 0) {
+            close(fenceFd);
+        }
+        return OK;
+    }
+    int result = callProtected(mSurface, cancelBuffer, buffer, fenceFd);
+    return result;
+}
+
+int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) {
+    {
+        std::lock_guard _lock{mMutex};
+        if (mReservedBuffer) {
+            *buffer = mReservedBuffer;
+            *fenceFd = mReservedFenceFd.release();
+            mReservedBuffer = nullptr;
+            return OK;
+        }
+    }
+
+    int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd);
+    if (result != OK) {
+        ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
+        *buffer = acquireFallbackBuffer();
+        *fenceFd = -1;
+        return *buffer ? OK : INVALID_OPERATION;
+    } else {
+        std::lock_guard _lock{mMutex};
+        mHasDequeuedBuffer = true;
+    }
+    return OK;
+}
+
+int ReliableSurface::queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
+    clearReservedBuffer();
+
+    if (isFallbackBuffer(buffer)) {
+        if (fenceFd > 0) {
+            close(fenceFd);
+        }
+        return OK;
+    }
+
+    int result = callProtected(mSurface, queueBuffer, buffer, fenceFd);
+    return result;
+}
+
+bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const {
+    if (!mScratchBuffer || !windowBuffer) {
+        return false;
+    }
+    ANativeWindowBuffer* scratchBuffer =
+            AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get());
+    return windowBuffer == scratchBuffer;
+}
+
+ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer() {
+    std::lock_guard _lock{mMutex};
+    mInErrorState = true;
+
+    if (mScratchBuffer) {
+        return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get());
+    }
+
+    AHardwareBuffer_Desc desc;
+    desc.usage = mUsage;
+    desc.format = mFormat;
+    desc.width = 1;
+    desc.height = 1;
+    desc.layers = 1;
+    desc.rfu0 = 0;
+    desc.rfu1 = 0;
+    AHardwareBuffer* newBuffer = nullptr;
+    int err = AHardwareBuffer_allocate(&desc, &newBuffer);
+    if (err) {
+        // Allocate failed, that sucks
+        ALOGW("Failed to allocate scratch buffer, error=%d", err);
+        return nullptr;
+    }
+    mScratchBuffer.reset(newBuffer);
+    return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer);
+}
+
+Surface* ReliableSurface::getWrapped(const ANativeWindow* window) {
+    return getSelf(window)->mSurface.get();
+}
+
+int ReliableSurface::hook_setSwapInterval(ANativeWindow* window, int interval) {
+    return callProtected(getWrapped(window), setSwapInterval, interval);
+}
+
+int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                                        int* fenceFd) {
+    return getSelf(window)->dequeueBuffer(buffer, fenceFd);
+}
+
+int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                                       int fenceFd) {
+    return getSelf(window)->cancelBuffer(buffer, fenceFd);
+}
+
+int ReliableSurface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                                      int fenceFd) {
+    return getSelf(window)->queueBuffer(buffer, fenceFd);
+}
+
+int ReliableSurface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
+                                                   ANativeWindowBuffer** buffer) {
+    ANativeWindowBuffer* buf;
+    int fenceFd = -1;
+    int result = window->dequeueBuffer(window, &buf, &fenceFd);
+    if (result != OK) {
+        return result;
+    }
+    sp<Fence> fence(new Fence(fenceFd));
+    int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
+    if (waitResult != OK) {
+        ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", waitResult);
+        window->cancelBuffer(window, buf, -1);
+        return waitResult;
+    }
+    *buffer = buf;
+    return result;
+}
+
+int ReliableSurface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
+                                                  ANativeWindowBuffer* buffer) {
+    return window->cancelBuffer(window, buffer, -1);
+}
+
+int ReliableSurface::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
+                                                ANativeWindowBuffer* buffer) {
+    // This method is a no-op in Surface as well
+    return OK;
+}
+
+int ReliableSurface::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
+                                                 ANativeWindowBuffer* buffer) {
+    return window->queueBuffer(window, buffer, -1);
+}
+
+int ReliableSurface::hook_query(const ANativeWindow* window, int what, int* value) {
+    return getWrapped(window)->query(what, value);
+}
+
+int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) {
+    // Drop the reserved buffer if there is one since this (probably) mutated buffer dimensions
+    // TODO: Filter to things that only affect the reserved buffer
+    // TODO: Can we mutate the reserved buffer in some cases?
+    getSelf(window)->clearReservedBuffer();
+    va_list args;
+    va_start(args, operation);
+    int result = callProtected(getWrapped(window), perform, operation, args);
+    va_end(args);
+
+    switch (operation) {
+        case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
+        case NATIVE_WINDOW_SET_USAGE:
+        case NATIVE_WINDOW_SET_USAGE64:
+            va_start(args, operation);
+            getSelf(window)->perform(operation, args);
+            va_end(args);
+            break;
+        default:
+            break;
+    }
+
+    return result;
+}
+
+};  // namespace android::uirenderer::renderthread
\ No newline at end of file
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
new file mode 100644
index 0000000..0bfc72e
--- /dev/null
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/Surface.h>
+#include <utils/Macros.h>
+#include <utils/StrongPointer.h>
+
+#include <memory>
+
+namespace android::uirenderer::renderthread {
+
+class ReliableSurface : public ANativeObjectBase<ANativeWindow, ReliableSurface, RefBase> {
+    PREVENT_COPY_AND_ASSIGN(ReliableSurface);
+
+public:
+    ReliableSurface(sp<Surface>&& surface);
+    ~ReliableSurface();
+
+    void setDequeueTimeout(nsecs_t timeout) { mSurface->setDequeueTimeout(timeout); }
+
+    int reserveNext();
+
+    void allocateBuffers() { mSurface->allocateBuffers(); }
+
+    int query(int what, int* value) const { return mSurface->query(what, value); }
+
+    nsecs_t getLastDequeueStartTime() const { return mSurface->getLastDequeueStartTime(); }
+
+    uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); }
+
+private:
+    const sp<Surface> mSurface;
+
+    mutable std::mutex mMutex;
+
+    uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
+    PixelFormat mFormat = PIXEL_FORMAT_RGBA_8888;
+    std::unique_ptr<AHardwareBuffer, void (*)(AHardwareBuffer*)> mScratchBuffer{
+            nullptr, AHardwareBuffer_release};
+    ANativeWindowBuffer* mReservedBuffer = nullptr;
+    base::unique_fd mReservedFenceFd;
+    bool mHasDequeuedBuffer = false;
+    bool mInErrorState = false;
+
+    bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const;
+    ANativeWindowBuffer* acquireFallbackBuffer();
+    void clearReservedBuffer();
+
+    void perform(int operation, va_list args);
+    int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+    int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
+    int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+
+    static Surface* getWrapped(const ANativeWindow*);
+
+    // ANativeWindow hooks
+    static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+    static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                                  int* fenceFd);
+    static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+
+    static int hook_perform(ANativeWindow* window, int operation, ...);
+    static int hook_query(const ANativeWindow* window, int what, int* value);
+    static int hook_setSwapInterval(ANativeWindow* window, int interval);
+
+    static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
+    static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer);
+    static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
+    static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
+};
+
+};  // namespace android::uirenderer::renderthread
\ No newline at end of file
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 2abb3d5..4be8bd9 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -831,6 +831,11 @@
 
     createBuffers(surface, surfaceFormat, extent);
 
+    // The window content is not updated (frozen) until a buffer of the window size is received.
+    // This prevents temporary stretching of the window after it is resized, but before the first
+    // buffer with new size is enqueued.
+    native_window_set_scaling_mode(surface->mNativeWindow, NATIVE_WINDOW_SCALING_MODE_FREEZE);
+
     return true;
 }
 
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 89f0c52..146662b 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -30,18 +30,6 @@
     void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) {
         ADD_FAILURE() << "onDrawDRRect not expected in this test";
     }
-    void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
-                    const SkPaint& paint) {
-        ADD_FAILURE() << "onDrawText not expected in this test";
-    }
-    void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
-                       const SkPaint& paint) {
-        ADD_FAILURE() << "onDrawPosText not expected in this test";
-    }
-    void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY,
-                        const SkPaint& paint) {
-        ADD_FAILURE() << "onDrawPosTextH not expected in this test";
-    }
     void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
                            const SkRect* cullRect, const SkPaint& paint) {
         ADD_FAILURE() << "onDrawTextRSXform not expected in this test";
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index f1d9397..a2e8672 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -14,7 +14,7 @@
 
 cc_library_shared {
     name: "libinputservice",
-
+    cpp_std: "c++17",
     srcs: [
         "PointerController.cpp",
         "SpriteController.cpp",
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 0a90f85..80d8e72 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -89,10 +89,6 @@
 
     mLocked.animationPending = false;
 
-    mLocked.displayWidth = -1;
-    mLocked.displayHeight = -1;
-    mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
-
     mLocked.presentation = PRESENTATION_POINTER;
     mLocked.presentationChanged = false;
 
@@ -110,15 +106,6 @@
     mLocked.lastFrameUpdatedTime = 0;
 
     mLocked.buttonState = 0;
-
-    mPolicy->loadPointerIcon(&mLocked.pointerIcon);
-
-    loadResources();
-
-    if (mLocked.pointerIcon.isValid()) {
-        mLocked.pointerIconChanged = true;
-        updatePointerLocked();
-    }
 }
 
 PointerController::~PointerController() {
@@ -144,23 +131,15 @@
 
 bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
         float* outMaxX, float* outMaxY) const {
-    if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
+
+    if (!mLocked.viewport.isValid()) {
         return false;
     }
 
-    *outMinX = 0;
-    *outMinY = 0;
-    switch (mLocked.displayOrientation) {
-    case DISPLAY_ORIENTATION_90:
-    case DISPLAY_ORIENTATION_270:
-        *outMaxX = mLocked.displayHeight - 1;
-        *outMaxY = mLocked.displayWidth - 1;
-        break;
-    default:
-        *outMaxX = mLocked.displayWidth - 1;
-        *outMaxY = mLocked.displayHeight - 1;
-        break;
-    }
+    *outMinX = mLocked.viewport.logicalLeft;
+    *outMinY = mLocked.viewport.logicalTop;
+    *outMaxX = mLocked.viewport.logicalRight - 1;
+    *outMaxY = mLocked.viewport.logicalBottom - 1;
     return true;
 }
 
@@ -231,6 +210,12 @@
     *outY = mLocked.pointerY;
 }
 
+int32_t PointerController::getDisplayId() const {
+    AutoMutex _l(mLock);
+
+    return mLocked.viewport.displayId;
+}
+
 void PointerController::fade(Transition transition) {
     AutoMutex _l(mLock);
 
@@ -355,48 +340,57 @@
 void PointerController::reloadPointerResources() {
     AutoMutex _l(mLock);
 
-    loadResources();
-
-    if (mLocked.presentation == PRESENTATION_POINTER) {
-        mLocked.additionalMouseResources.clear();
-        mLocked.animationResources.clear();
-        mPolicy->loadPointerIcon(&mLocked.pointerIcon);
-        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                                              &mLocked.animationResources);
-    }
-
-    mLocked.presentationChanged = true;
+    loadResourcesLocked();
     updatePointerLocked();
 }
 
-void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
-    AutoMutex _l(mLock);
+/**
+ * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
+ * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
+ */
+static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
+    if (viewport.orientation == DISPLAY_ORIENTATION_90
+            || viewport.orientation == DISPLAY_ORIENTATION_270) {
+        width = viewport.deviceHeight;
+        height = viewport.deviceWidth;
+    } else {
+        width = viewport.deviceWidth;
+        height = viewport.deviceHeight;
+    }
+}
 
-    // Adjust to use the display's unrotated coordinate frame.
-    if (orientation == DISPLAY_ORIENTATION_90
-            || orientation == DISPLAY_ORIENTATION_270) {
-        int32_t temp = height;
-        height = width;
-        width = temp;
+void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
+    AutoMutex _l(mLock);
+    if (viewport == mLocked.viewport) {
+        return;
     }
 
-    if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
-        mLocked.displayWidth = width;
-        mLocked.displayHeight = height;
+    const DisplayViewport oldViewport = mLocked.viewport;
+    mLocked.viewport = viewport;
+
+    int32_t oldDisplayWidth, oldDisplayHeight;
+    getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
+    int32_t newDisplayWidth, newDisplayHeight;
+    getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
+
+    // Reset cursor position to center if size or display changed.
+    if (oldViewport.displayId != viewport.displayId
+            || oldDisplayWidth != newDisplayWidth
+            || oldDisplayHeight != newDisplayHeight) {
 
         float minX, minY, maxX, maxY;
         if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
             mLocked.pointerX = (minX + maxX) * 0.5f;
             mLocked.pointerY = (minY + maxY) * 0.5f;
+            // Reload icon resources for density may be changed.
+            loadResourcesLocked();
         } else {
             mLocked.pointerX = 0;
             mLocked.pointerY = 0;
         }
 
         fadeOutAndReleaseAllSpotsLocked();
-    }
-
-    if (mLocked.displayOrientation != orientation) {
+    } else if (oldViewport.orientation != viewport.orientation) {
         // Apply offsets to convert from the pixel top-left corner position to the pixel center.
         // This creates an invariant frame of reference that we can easily rotate when
         // taking into account that the pointer may be located at fractional pixel offsets.
@@ -405,37 +399,37 @@
         float temp;
 
         // Undo the previous rotation.
-        switch (mLocked.displayOrientation) {
+        switch (oldViewport.orientation) {
         case DISPLAY_ORIENTATION_90:
             temp = x;
-            x = mLocked.displayWidth - y;
+            x =  oldViewport.deviceHeight - y;
             y = temp;
             break;
         case DISPLAY_ORIENTATION_180:
-            x = mLocked.displayWidth - x;
-            y = mLocked.displayHeight - y;
+            x = oldViewport.deviceWidth - x;
+            y = oldViewport.deviceHeight - y;
             break;
         case DISPLAY_ORIENTATION_270:
             temp = x;
             x = y;
-            y = mLocked.displayHeight - temp;
+            y = oldViewport.deviceWidth - temp;
             break;
         }
 
         // Perform the new rotation.
-        switch (orientation) {
+        switch (viewport.orientation) {
         case DISPLAY_ORIENTATION_90:
             temp = x;
             x = y;
-            y = mLocked.displayWidth - temp;
+            y = viewport.deviceHeight - temp;
             break;
         case DISPLAY_ORIENTATION_180:
-            x = mLocked.displayWidth - x;
-            y = mLocked.displayHeight - y;
+            x = viewport.deviceWidth - x;
+            y = viewport.deviceHeight - y;
             break;
         case DISPLAY_ORIENTATION_270:
             temp = x;
-            x = mLocked.displayHeight - y;
+            x = viewport.deviceWidth - y;
             y = temp;
             break;
         }
@@ -444,7 +438,6 @@
         // and save the results.
         mLocked.pointerX = x - 0.5f;
         mLocked.pointerY = y - 0.5f;
-        mLocked.displayOrientation = orientation;
     }
 
     updatePointerLocked();
@@ -614,11 +607,16 @@
     mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
 }
 
-void PointerController::updatePointerLocked() {
+void PointerController::updatePointerLocked() REQUIRES(mLock) {
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+
     mSpriteController->openTransaction();
 
     mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
     mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+    mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
 
     if (mLocked.pointerAlpha > 0) {
         mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
@@ -729,8 +727,18 @@
     }
 }
 
-void PointerController::loadResources() {
+void PointerController::loadResourcesLocked() REQUIRES(mLock) {
     mPolicy->loadPointerResources(&mResources);
+
+    if (mLocked.presentation == PRESENTATION_POINTER) {
+        mLocked.additionalMouseResources.clear();
+        mLocked.animationResources.clear();
+        mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+                                              &mLocked.animationResources);
+    }
+
+    mLocked.pointerIconChanged = true;
 }
 
 
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 7f4e5a5..a32cc42 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -23,6 +23,7 @@
 #include <vector>
 
 #include <ui/DisplayInfo.h>
+#include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <PointerControllerInterface.h>
 #include <utils/BitSet.h>
@@ -96,6 +97,7 @@
     virtual int32_t getButtonState() const;
     virtual void setPosition(float x, float y);
     virtual void getPosition(float* outX, float* outY) const;
+    virtual int32_t getDisplayId() const;
     virtual void fade(Transition transition);
     virtual void unfade(Transition transition);
 
@@ -106,7 +108,7 @@
 
     void updatePointerIcon(int32_t iconId);
     void setCustomPointerIcon(const SpriteIcon& icon);
-    void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
+    void setDisplayViewport(const DisplayViewport& viewport);
     void setInactivityTimeout(InactivityTimeout inactivityTimeout);
     void reloadPointerResources();
 
@@ -156,9 +158,7 @@
         size_t animationFrameIndex;
         nsecs_t lastFrameUpdatedTime;
 
-        int32_t displayWidth;
-        int32_t displayHeight;
-        int32_t displayOrientation;
+        DisplayViewport viewport;
 
         InactivityTimeout inactivityTimeout;
 
@@ -182,7 +182,7 @@
 
         Vector<Spot*> spots;
         Vector<sp<Sprite> > recycledSprites;
-    } mLocked;
+    } mLocked GUARDED_BY(mLock);
 
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     void setPositionLocked(float x, float y);
@@ -207,7 +207,7 @@
     void fadeOutAndReleaseSpotLocked(Spot* spot);
     void fadeOutAndReleaseAllSpotsLocked();
 
-    void loadResources();
+    void loadResourcesLocked();
 };
 
 } // namespace android
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index eb2bc98..c1868d3 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -144,13 +144,16 @@
         }
     }
 
-    // Resize sprites if needed.
+    // Resize and/or reparent sprites if needed.
     SurfaceComposerClient::Transaction t;
     bool needApplyTransaction = false;
     for (size_t i = 0; i < numSprites; i++) {
         SpriteUpdate& update = updates.editItemAt(i);
+        if (update.state.surfaceControl == nullptr) {
+            continue;
+        }
 
-        if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
+        if (update.state.wantSurfaceVisible()) {
             int32_t desiredWidth = update.state.icon.bitmap.width();
             int32_t desiredHeight = update.state.icon.bitmap.height();
             if (update.state.surfaceWidth < desiredWidth
@@ -170,6 +173,12 @@
                 }
             }
         }
+
+        // If surface is a new one, we have to set right layer stack.
+        if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
+            t.setLayerStack(update.state.surfaceControl, update.state.displayId);
+            needApplyTransaction = true;
+        }
     }
     if (needApplyTransaction) {
         t.apply();
@@ -236,7 +245,7 @@
         if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
                 || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
                         | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
-                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
+                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
             needApplyTransaction = true;
 
             if (wantSurfaceVisibleAndDrawn
@@ -445,6 +454,15 @@
     }
 }
 
+void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
+    AutoMutex _l(mController->mLock);
+
+    if (mLocked.state.displayId != displayId) {
+        mLocked.state.displayId = displayId;
+        invalidateLocked(DIRTY_DISPLAY_ID);
+    }
+}
+
 void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
     bool wasDirty = mLocked.state.dirty;
     mLocked.state.dirty |= dirty;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 31e43e9..5b216f5 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -125,6 +125,9 @@
 
     /* Sets the sprite transformation matrix. */
     virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
+
+    /* Sets the id of the display where the sprite should be shown. */
+    virtual void setDisplayId(int32_t displayId) = 0;
 };
 
 /*
@@ -170,6 +173,7 @@
         DIRTY_LAYER = 1 << 4,
         DIRTY_VISIBILITY = 1 << 5,
         DIRTY_HOTSPOT = 1 << 6,
+        DIRTY_DISPLAY_ID = 1 << 7,
     };
 
     /* Describes the state of a sprite.
@@ -180,7 +184,7 @@
     struct SpriteState {
         inline SpriteState() :
                 dirty(0), visible(false),
-                positionX(0), positionY(0), layer(0), alpha(1.0f),
+                positionX(0), positionY(0), layer(0), alpha(1.0f), displayId(ADISPLAY_ID_DEFAULT),
                 surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
         }
 
@@ -193,6 +197,7 @@
         int32_t layer;
         float alpha;
         SpriteTransformationMatrix transformationMatrix;
+        int32_t displayId;
 
         sp<SurfaceControl> surfaceControl;
         int32_t surfaceWidth;
@@ -225,6 +230,7 @@
         virtual void setLayer(int32_t layer);
         virtual void setAlpha(float alpha);
         virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
+        virtual void setDisplayId(int32_t displayId);
 
         inline const SpriteState& getStateLocked() const {
             return mLocked.state;
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index ae87998..32c7520 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -96,9 +96,7 @@
     void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
     void removeTestProvider(String provider, String opPackageName);
     void setTestProviderLocation(String provider, in Location loc, String opPackageName);
-    void clearTestProviderLocation(String provider, String opPackageName);
     void setTestProviderEnabled(String provider, boolean enabled, String opPackageName);
-    void clearTestProviderEnabled(String provider, String opPackageName);
 
     // --- deprecated ---
     void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime,
@@ -108,12 +106,8 @@
 
     // --- internal ---
 
-    // Used by location providers to tell the location manager when it has a new location.
-    // Passive is true if the location is coming from the passive provider, in which case
-    // it need not be shared with other providers.
+    // --- deprecated ---
     void reportLocation(in Location location, boolean passive);
-
-    // Used when a (initially Gnss) Location batch arrives
     void reportLocationBatch(in List<Location> locations);
 
     // for reporting callback completion
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 3bf98b3..334170e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1537,14 +1537,11 @@
      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
      * allowed} for your app.
      * @throws IllegalArgumentException if no provider with the given name exists
+     *
+     * @deprecated This function has always been a no-op, and may be removed in the future.
      */
-    public void clearTestProviderLocation(String provider) {
-        try {
-            mService.clearTestProviderLocation(provider, mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
+    @Deprecated
+    public void clearTestProviderLocation(String provider) {}
 
     /**
      * Sets a mock enabled value for the given provider.  This value will be used in place
@@ -1575,13 +1572,12 @@
      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
      * allowed} for your app.
      * @throws IllegalArgumentException if no provider with the given name exists
+     *
+     * @deprecated Use {@link #setTestProviderEnabled(String, boolean)} instead.
      */
+    @Deprecated
     public void clearTestProviderEnabled(String provider) {
-        try {
-            mService.clearTestProviderEnabled(provider, mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        setTestProviderEnabled(provider, true);
     }
 
     /**
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index 39c2d92..71b54fb 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -16,29 +16,26 @@
 
 package com.android.internal.location;
 
-import android.location.Location;
-import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.os.WorkSource;
 
-import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ILocationProviderManager;
 import com.android.internal.location.ProviderRequest;
 
 /**
- * Binder interface for services that implement location providers.
- * <p>Use {@link LocationProviderBase} as a helper to implement this
- * interface.
+ * Binder interface for services that implement location providers. Do not implement this directly,
+ * extend {@link LocationProviderBase} instead.
  * @hide
  */
 interface ILocationProvider {
-    void enable();
-    void disable();
 
-    void setRequest(in ProviderRequest request, in WorkSource ws);
+    oneway void setLocationProviderManager(in ILocationProviderManager manager);
 
-    // --- deprecated (but still supported) ---
-    ProviderProperties getProperties();
+    oneway void setRequest(in ProviderRequest request, in WorkSource ws);
+
+    oneway void sendExtraCommand(String command, in Bundle extras);
+
+    // --- deprecated and will be removed the future ---
     int getStatus(out Bundle extras);
     long getStatusUpdateTime();
-    boolean sendExtraCommand(String command, inout Bundle extras);
 }
diff --git a/location/java/android/location/IGnssStatusProvider.aidl b/location/java/com/android/internal/location/ILocationProviderManager.aidl
similarity index 61%
rename from location/java/android/location/IGnssStatusProvider.aidl
rename to location/java/com/android/internal/location/ILocationProviderManager.aidl
index 006b5d3..b1b8f0c 100644
--- a/location/java/android/location/IGnssStatusProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProviderManager.aidl
@@ -14,16 +14,21 @@
  * limitations under the License.
  */
 
-package android.location;
+package com.android.internal.location;
 
-import android.location.IGnssStatusListener;
+import android.location.Location;
+
+import com.android.internal.location.ProviderProperties;
 
 /**
- * An interface for location providers that provide GNSS status information.
- *
- * {@hide}
+ * Binder interface for manager of all location providers.
+ * @hide
  */
-interface IGnssStatusProvider {
-    void registerGnssStatusCallback(IGnssStatusListener callback);
-    void unregisterGnssStatusCallback(IGnssStatusListener callback);
+interface ILocationProviderManager {
+
+    void onSetEnabled(boolean enabled);
+
+    void onSetProperties(in ProviderProperties properties);
+
+    void onReportLocation(in Location location);
 }
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index 88919f6..a45c20d 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -16,15 +16,15 @@
 
 package com.android.internal.location;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import android.annotation.UnsupportedAppUsage;
 import android.location.LocationRequest;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.TimeUtils;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /** @hide */
 public final class ProviderRequest implements Parcelable {
     /** Location reporting is requested (true) */
@@ -36,6 +36,13 @@
     public long interval = Long.MAX_VALUE;
 
     /**
+     * When this flag is true, providers should ignore all location settings, user consents, power
+     * restrictions or any other restricting factors and always satisfy this request to the best of
+     * their ability. This flag should only be used in event of an emergency.
+     */
+    public boolean forceLocation = false;
+
+    /**
      * Whether provider shall make stronger than normal tradeoffs to substantially restrict power
      * use.
      */
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index d19559e..10c3447 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -8,14 +8,18 @@
   public abstract class LocationProviderBase {
     ctor public LocationProviderBase(java.lang.String, com.android.location.provider.ProviderPropertiesUnbundled);
     method public android.os.IBinder getBinder();
-    method public abstract void onDisable();
-    method public void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
-    method public abstract void onEnable();
-    method public deprecated int onGetStatus(android.os.Bundle);
-    method public deprecated long onGetStatusUpdateTime();
-    method public boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
-    method public abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
-    method public final void reportLocation(android.location.Location);
+    method public boolean isEnabled();
+    method protected deprecated void onDisable();
+    method protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method protected deprecated void onEnable();
+    method protected deprecated int onGetStatus(android.os.Bundle);
+    method protected deprecated long onGetStatusUpdateTime();
+    method protected void onInit();
+    method protected boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
+    method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
+    method public void reportLocation(android.location.Location);
+    method public void setEnabled(boolean);
+    method public void setProperties(com.android.location.provider.ProviderPropertiesUnbundled);
     field public static final java.lang.String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
     field public static final java.lang.String FUSED_PROVIDER = "fused";
   }
@@ -38,6 +42,7 @@
   }
 
   public final class ProviderRequestUnbundled {
+    method public boolean getForceLocation();
     method public long getInterval();
     method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
     method public boolean getReportLocation();
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index d45a4ba..5bcec92 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,6 +16,7 @@
 
 package com.android.location.provider;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.location.ILocationManager;
 import android.location.Location;
@@ -29,12 +30,11 @@
 import android.util.Log;
 
 import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ILocationProviderManager;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
-import com.android.internal.util.FastPrintWriter;
 
 import java.io.FileDescriptor;
-import java.io.FileOutputStream;
 import java.io.PrintWriter;
 
 /**
@@ -55,12 +55,6 @@
  * of this package for more information.
  */
 public abstract class LocationProviderBase {
-    private final String TAG;
-
-    /** @hide */
-    protected final ILocationManager mLocationManager;
-    private final ProviderProperties mProperties;
-    private final IBinder mBinder;
 
     /**
      * Bundle key for a version of the location containing no GPS data.
@@ -77,49 +71,34 @@
      */
     public static final String FUSED_PROVIDER = LocationManager.FUSED_PROVIDER;
 
-    private final class Service extends ILocationProvider.Stub {
-        @Override
-        public void enable() {
-            onEnable();
-        }
-        @Override
-        public void disable() {
-            onDisable();
-        }
-        @Override
-        public void setRequest(ProviderRequest request, WorkSource ws) {
-            onSetRequest(new ProviderRequestUnbundled(request), ws);
-        }
-        @Override
-        public ProviderProperties getProperties() {
-            return mProperties;
-        }
-        @Override
-        public int getStatus(Bundle extras) {
-            return onGetStatus(extras);
-        }
-        @Override
-        public long getStatusUpdateTime() {
-            return onGetStatusUpdateTime();
-        }
-        @Override
-        public boolean sendExtraCommand(String command, Bundle extras) {
-            return onSendExtraCommand(command, extras);
-        }
-        @Override
-        public void dump(FileDescriptor fd, String[] args) {
-            PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
-            onDump(fd, pw, args);
-            pw.flush();
-        }
-    }
+    private final String mTag;
+    private final IBinder mBinder;
+
+    /**
+     * This field may be removed in the future, do not rely on it.
+     *
+     * @deprecated Do not use this field! Use LocationManager APIs instead. If you use this field
+     * you may be broken in the future.
+     * @hide
+     */
+    @Deprecated
+    protected final ILocationManager mLocationManager;
+
+    // write locked on mBinder, read lock is optional depending on atomicity requirements
+    @Nullable private volatile ILocationProviderManager mManager;
+    private volatile ProviderProperties mProperties;
+    private volatile boolean mEnabled;
 
     public LocationProviderBase(String tag, ProviderPropertiesUnbundled properties) {
-        TAG = tag;
-        IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
-        mLocationManager = ILocationManager.Stub.asInterface(b);
-        mProperties = properties.getProviderProperties();
+        mTag = tag;
         mBinder = new Service();
+
+        mLocationManager = ILocationManager.Stub.asInterface(
+                ServiceManager.getService(Context.LOCATION_SERVICE));
+
+        mManager = null;
+        mProperties = properties.getProviderProperties();
+        mEnabled = true;
     }
 
     public IBinder getBinder() {
@@ -127,51 +106,116 @@
     }
 
     /**
-     * Used by the location provider to report new locations.
+     * Sets whether this provider is currently enabled or not. Note that this is specific to the
+     * provider only, and is not related to global location settings. This is a hint to the Location
+     * Manager that this provider will generally be unable to fulfill incoming requests. This
+     * provider may still receive callbacks to onSetRequest while not enabled, and must decide
+     * whether to attempt to satisfy those requests or not.
      *
-     * @param location new Location to report
-     *
-     * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
+     * Some guidelines: providers should set their own enabled/disabled status based only on state
+     * "owned" by that provider. For instance, providers should not take into account the state of
+     * the location master setting when setting themselves enabled or disabled, as this state is not
+     * owned by a particular provider. If a provider requires some additional user consent that is
+     * particular to the provider, this should be use to set the enabled/disabled state. If the
+     * provider proxies to another provider, the child provider's enabled/disabled state should be
+     * taken into account in the parent's enabled/disabled state. For most providers, it is expected
+     * that they will be always enabled.
      */
-    public final void reportLocation(Location location) {
-        try {
-            mLocationManager.reportLocation(location, false);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException", e);
-        } catch (Exception e) {
-            // never crash provider, might be running in a system process
-            Log.e(TAG, "Exception", e);
+    public void setEnabled(boolean enabled) {
+        synchronized (mBinder) {
+            if (mEnabled == enabled) {
+                return;
+            }
+
+            mEnabled = enabled;
+        }
+
+        ILocationProviderManager manager = mManager;
+        if (manager != null) {
+            try {
+                manager.onSetEnabled(mEnabled);
+            } catch (RemoteException | RuntimeException e) {
+                Log.w(mTag, e);
+            }
         }
     }
 
     /**
-     * Enable the location provider.
-     * <p>The provider may initialize resources, but does
-     * not yet need to report locations.
+     * Sets the provider properties that may be queried by clients. Generally speaking, providers
+     * should try to avoid changing their properties after construction.
      */
-    public abstract void onEnable();
+    public void setProperties(ProviderPropertiesUnbundled properties) {
+        synchronized (mBinder) {
+            mProperties = properties.getProviderProperties();
+        }
+
+        ILocationProviderManager manager = mManager;
+        if (manager != null) {
+            try {
+                manager.onSetProperties(mProperties);
+            } catch (RemoteException | RuntimeException e) {
+                Log.w(mTag, e);
+            }
+        }
+    }
 
     /**
-     * Disable the location provider.
-     * <p>The provider must release resources, and stop
-     * performing work. It may no longer report locations.
+     * Returns true if this provider has been set as enabled. This will be true unless explicitly
+     * set otherwise.
      */
-    public abstract void onDisable();
+    public boolean isEnabled() {
+        return mEnabled;
+    }
 
     /**
-     * Set the {@link ProviderRequest} requirements for this provider.
-     * <p>Each call to this method overrides all previous requests.
-     * <p>This method might trigger the provider to start returning
-     * locations, or to stop returning locations, depending on the
-     * parameters in the request.
+     * Reports a new location from this provider.
      */
-    public abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
+    public void reportLocation(Location location) {
+        ILocationProviderManager manager = mManager;
+        if (manager != null) {
+            try {
+                manager.onReportLocation(location);
+            } catch (RemoteException | RuntimeException e) {
+                Log.w(mTag, e);
+            }
+        }
+    }
+
+    protected void onInit() {
+        // call once so that providers designed for APIs pre-Q are not broken
+        onEnable();
+    }
+
+    /**
+     * @deprecated This callback will be invoked once when the provider is created to maintain
+     * backwards compatibility with providers not designed for Android Q and above. This method
+     * should only be implemented in location providers that need to support SDKs below Android Q.
+     * Even in this case, it is usually unnecessary to implement this callback with the correct
+     * design. This method may be removed in the future.
+     */
+    @Deprecated
+    protected void onEnable() {}
+
+    /**
+     * @deprecated This callback will be never be invoked on Android Q and above. This method should
+     * only be implemented in location providers that need to support SDKs below Android Q. Even in
+     * this case, it is usually unnecessary to implement this callback with the correct design. This
+     * method may be removed in the future.
+     */
+    @Deprecated
+    protected void onDisable() {}
+
+    /**
+     * Set the {@link ProviderRequest} requirements for this provider. Each call to this method
+     * overrides all previous requests. This method might trigger the provider to start returning
+     * locations, or to stop returning locations, depending on the parameters in the request.
+     */
+    protected abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
 
     /**
      * Dump debug information.
      */
-    public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
-    }
+    protected void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {}
 
     /**
      * This method will no longer be invoked.
@@ -187,10 +231,12 @@
      * <p>If extras is non-null, additional status information may be
      * added to it in the form of provider-specific key/value pairs.
      *
-     * @deprecated This method will no longer be invoked.
+     * @deprecated This callback will be never be invoked on Android Q and above. This method should
+     * only be implemented in location providers that need to support SDKs below Android Q. This
+     * method may be removed in the future.
      */
     @Deprecated
-    public int onGetStatus(Bundle extras) {
+    protected int onGetStatus(Bundle extras) {
         return LocationProvider.AVAILABLE;
     }
 
@@ -206,24 +252,64 @@
      *
      * @return time of last status update in millis since last reboot
      *
-     * @deprecated This method will no longer be invoked.
+     * @deprecated This callback will be never be invoked on Android Q and above. This method should
+     * only be implemented in location providers that need to support SDKs below Android Q. This
+     * method may be removed in the future.
      */
     @Deprecated
-    public long onGetStatusUpdateTime() {
+    protected long onGetStatusUpdateTime() {
         return 0;
     }
 
     /**
-     * Implements addditional location provider specific additional commands.
-     *
-     * @param command name of the command to send to the provider.
-     * @param extras optional arguments for the command (or null).
-     * The provider may optionally fill the extras Bundle with results from the command.
-     *
-     * @return true if the command succeeds.
+     * Implements location provider specific custom commands. The return value will be ignored on
+     * Android Q and above.
      */
-    public boolean onSendExtraCommand(String command, Bundle extras) {
-        // default implementation
+    protected boolean onSendExtraCommand(@Nullable String command, @Nullable Bundle extras) {
         return false;
     }
+
+    private final class Service extends ILocationProvider.Stub {
+
+        @Override
+        public void setLocationProviderManager(ILocationProviderManager manager) {
+            synchronized (mBinder) {
+                try {
+                    manager.onSetProperties(mProperties);
+                    manager.onSetEnabled(mEnabled);
+                } catch (RemoteException e) {
+                    Log.w(mTag, e);
+                }
+
+                mManager = manager;
+            }
+
+            onInit();
+        }
+
+        @Override
+        public void setRequest(ProviderRequest request, WorkSource ws) {
+            onSetRequest(new ProviderRequestUnbundled(request), ws);
+        }
+
+        @Override
+        public int getStatus(Bundle extras) {
+            return onGetStatus(extras);
+        }
+
+        @Override
+        public long getStatusUpdateTime() {
+            return onGetStatusUpdateTime();
+        }
+
+        @Override
+        public void sendExtraCommand(String command, Bundle extras) {
+            onSendExtraCommand(command, extras);
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            onDump(fd, pw, args);
+        }
+    }
 }
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index 6a8e618..b825b58 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -16,13 +16,13 @@
 
 package com.android.location.provider;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import android.location.LocationRequest;
 
 import com.android.internal.location.ProviderRequest;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * This class is an interface to Provider Requests for unbundled applications.
  *
@@ -46,6 +46,10 @@
         return mRequest.interval;
     }
 
+    public boolean getForceLocation() {
+        return mRequest.forceLocation;
+    }
+
     /**
      * Never null.
      */
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 3e3e651..793aa27 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -213,6 +213,8 @@
  * (e.g.{@link AudioTrack#getPlaybackHeadPosition()
  * AudioTrack.getPlaybackHeadPosition()}),
  * depending on the context where audio frame is used.
+ * For the purposes of {@link AudioFormat#getFrameSizeInBytes()}, a compressed data format
+ * returns a frame size of 1 byte.
  */
 public final class AudioFormat implements Parcelable {
 
@@ -670,27 +672,53 @@
     }
 
     /**
-     * Private constructor with an ignored argument to differentiate from the removed default ctor
-     * @param ignoredArgument
-     */
-    private AudioFormat(int ignoredArgument) {
-    }
-
-    /**
      * Constructor used by the JNI.  Parameters are not checked for validity.
      */
     // Update sound trigger JNI in core/jni/android_hardware_SoundTrigger.cpp when modifying this
     // constructor
     @UnsupportedAppUsage
     private AudioFormat(int encoding, int sampleRate, int channelMask, int channelIndexMask) {
-        mEncoding = encoding;
-        mSampleRate = sampleRate;
-        mChannelMask = channelMask;
-        mChannelIndexMask = channelIndexMask;
-        mPropertySetMask = AUDIO_FORMAT_HAS_PROPERTY_ENCODING |
-                AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE |
-                AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK |
-                AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK;
+        this(
+             AUDIO_FORMAT_HAS_PROPERTY_ENCODING
+             | AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE
+             | AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK
+             | AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK,
+             encoding, sampleRate, channelMask, channelIndexMask
+             );
+    }
+
+    private AudioFormat(int propertySetMask,
+            int encoding, int sampleRate, int channelMask, int channelIndexMask) {
+        mPropertySetMask = propertySetMask;
+        mEncoding = (propertySetMask & AUDIO_FORMAT_HAS_PROPERTY_ENCODING) != 0
+                ? encoding : ENCODING_INVALID;
+        mSampleRate = (propertySetMask & AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0
+                ? sampleRate : SAMPLE_RATE_UNSPECIFIED;
+        mChannelMask = (propertySetMask & AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0
+                ? channelMask : CHANNEL_INVALID;
+        mChannelIndexMask = (propertySetMask & AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0
+                ? channelIndexMask : CHANNEL_INVALID;
+
+        // Compute derived values.
+
+        final int channelIndexCount = Integer.bitCount(getChannelIndexMask());
+        int channelCount = channelCountFromOutChannelMask(getChannelMask());
+        if (channelCount == 0) {
+            channelCount = channelIndexCount;
+        } else if (channelCount != channelIndexCount && channelIndexCount != 0) {
+            channelCount = 0; // position and index channel count mismatch
+        }
+        mChannelCount = channelCount;
+
+        int frameSizeInBytes = 1;
+        try {
+            frameSizeInBytes = getBytesPerSample(mEncoding) * channelCount;
+        } catch (IllegalArgumentException iae) {
+            // ignored
+        }
+        // it is possible that channel count is 0, so ensure we return 1 for
+        // mFrameSizeInBytes for consistency.
+        mFrameSizeInBytes = frameSizeInBytes != 0 ? frameSizeInBytes : 1;
     }
 
     /** @hide */
@@ -704,14 +732,21 @@
     /** @hide */
     public final static int AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK = 0x1 << 3;
 
+    // This is an immutable class, all member variables are final.
+
+    // Essential values.
     @UnsupportedAppUsage
-    private int mEncoding;
+    private final int mEncoding;
     @UnsupportedAppUsage
-    private int mSampleRate;
+    private final int mSampleRate;
     @UnsupportedAppUsage
-    private int mChannelMask;
-    private int mChannelIndexMask;
-    private int mPropertySetMask;
+    private final int mChannelMask;
+    private final int mChannelIndexMask;
+    private final int mPropertySetMask;
+
+    // Derived values computed in the constructor, cached here.
+    private final int mChannelCount;
+    private final int mFrameSizeInBytes;
 
     /**
      * Return the encoding.
@@ -721,9 +756,6 @@
      * {@link AudioFormat#ENCODING_INVALID} if not set.
      */
     public int getEncoding() {
-        if ((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_ENCODING) == 0) {
-            return ENCODING_INVALID;
-        }
         return mEncoding;
     }
 
@@ -745,9 +777,6 @@
      * {@link AudioFormat#CHANNEL_INVALID} if not set.
      */
     public int getChannelMask() {
-        if ((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) == 0) {
-            return CHANNEL_INVALID;
-        }
         return mChannelMask;
     }
 
@@ -760,9 +789,6 @@
      * {@link AudioFormat#CHANNEL_INVALID} if not set or an invalid mask was used.
      */
     public int getChannelIndexMask() {
-        if ((mPropertySetMask & AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) == 0) {
-            return CHANNEL_INVALID;
-        }
         return mChannelIndexMask;
     }
 
@@ -772,14 +798,26 @@
      * Zero is returned if both the channel position mask and the channel index mask are not set.
      */
     public int getChannelCount() {
-        final int channelIndexCount = Integer.bitCount(getChannelIndexMask());
-        int channelCount = channelCountFromOutChannelMask(getChannelMask());
-        if (channelCount == 0) {
-            channelCount = channelIndexCount;
-        } else if (channelCount != channelIndexCount && channelIndexCount != 0) {
-            channelCount = 0; // position and index channel count mismatch
-        }
-        return channelCount;
+        return mChannelCount;
+    }
+
+    /**
+     * Return the frame size in bytes.
+     *
+     * For PCM or PCM packed compressed data this is the size of a sample multiplied
+     * by the channel count. For all other cases, including invalid/unset channel masks,
+     * this will return 1 byte.
+     * As an example, a stereo 16-bit PCM format would have a frame size of 4 bytes,
+     * an 8 channel float PCM format would have a frame size of 32 bytes,
+     * and a compressed data format (not packed in PCM) would have a frame size of 1 byte.
+     *
+     * Both {@link AudioRecord} or {@link AudioTrack} process data in multiples of
+     * this frame size.
+     *
+     * @return The audio frame size in bytes corresponding to the encoding and the channel mask.
+     */
+    public int getFrameSizeInBytes() {
+        return mFrameSizeInBytes;
     }
 
     /** @hide */
@@ -790,7 +828,7 @@
     /** @hide */
     public String toLogFriendlyString() {
         return String.format("%dch %dHz %s",
-                getChannelCount(), mSampleRate, toLogFriendlyEncoding(mEncoding));
+                mChannelCount, mSampleRate, toLogFriendlyEncoding(mEncoding));
     }
 
     /**
@@ -839,14 +877,13 @@
          * @return a new {@link AudioFormat} object
          */
         public AudioFormat build() {
-            AudioFormat af = new AudioFormat(1980/*ignored*/);
-            af.mEncoding = mEncoding;
-            // not calling setSampleRate is equivalent to calling
-            // setSampleRate(SAMPLE_RATE_UNSPECIFIED)
-            af.mSampleRate = mSampleRate;
-            af.mChannelMask = mChannelMask;
-            af.mChannelIndexMask = mChannelIndexMask;
-            af.mPropertySetMask = mPropertySetMask;
+            AudioFormat af = new AudioFormat(
+                    mPropertySetMask,
+                    mEncoding,
+                    mSampleRate,
+                    mChannelMask,
+                    mChannelIndexMask
+                    );
             return af;
         }
 
@@ -1049,11 +1086,13 @@
     }
 
     private AudioFormat(Parcel in) {
-        mPropertySetMask = in.readInt();
-        mEncoding = in.readInt();
-        mSampleRate = in.readInt();
-        mChannelMask = in.readInt();
-        mChannelIndexMask = in.readInt();
+        this(
+             in.readInt(), // propertySetMask
+             in.readInt(), // encoding
+             in.readInt(), // sampleRate
+             in.readInt(), // channelMask
+             in.readInt()  // channelIndexMask
+            );
     }
 
     public static final Parcelable.Creator<AudioFormat> CREATOR =
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 2c4ec3a..5e77fdf 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -995,6 +995,35 @@
         }
     }
 
+    /**
+     * Returns whether direct playback of an audio format with the provided attributes is
+     * currently supported on the system.
+     * <p>Direct playback means that the audio stream is not resampled or downmixed
+     * by the framework. Checking for direct support can help the app select the representation
+     * of audio content that most closely matches the capabilities of the device and peripherials
+     * (e.g. A/V receiver) connected to it. Note that the provided stream can still be re-encoded
+     * or mixed with other streams, if needed.
+     * <p>Also note that this query only provides information about the support of an audio format.
+     * It does not indicate whether the resources necessary for the playback are available
+     * at that instant.
+     * @param format a non-null {@link AudioFormat} instance describing the format of
+     *   the audio data.
+     * @param attributes a non-null {@link AudioAttributes} instance.
+     * @return true if the given audio format can be played directly.
+     */
+    public static boolean isDirectPlaybackSupported(@NonNull AudioFormat format,
+            @NonNull AudioAttributes attributes) {
+        if (format == null) {
+            throw new IllegalArgumentException("Illegal null AudioFormat argument");
+        }
+        if (attributes == null) {
+            throw new IllegalArgumentException("Illegal null AudioAttributes argument");
+        }
+        return native_is_direct_output_supported(format.getEncoding(), format.getSampleRate(),
+                format.getChannelMask(), format.getChannelIndexMask(),
+                attributes.getContentType(), attributes.getUsage(), attributes.getFlags());
+    }
+
     // mask of all the positional channels supported, however the allowed combinations
     // are further restricted by the matching left/right rule and
     // AudioSystem.OUT_CHANNEL_COUNT_MAX
@@ -3328,6 +3357,9 @@
     // Native methods called from the Java side
     //--------------------
 
+    private static native boolean native_is_direct_output_supported(int encoding, int sampleRate,
+            int channelMask, int channelIndexMask, int contentType, int usage, int flags);
+
     // post-condition: mStreamType is overwritten with a value
     //     that reflects the audio attributes (e.g. an AudioAttributes object with a usage of
     //     AudioAttributes.USAGE_MEDIA will map to AudioManager.STREAM_MUSIC
@@ -3365,7 +3397,7 @@
                                                 int offsetInFloats, int sizeInFloats, int format,
                                                 boolean isBlocking);
 
-    private native final int native_write_native_bytes(Object audioData,
+    private native final int native_write_native_bytes(ByteBuffer audioData,
             int positionInBytes, int sizeInBytes, int format, boolean blocking);
 
     private native final int native_reload_static();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 1d27c03..242ae46 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -3308,6 +3308,55 @@
     public static final String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
 
     /**
+     * Set the HDR10+ metadata on the next queued input frame.
+     *
+     * Provide a byte array of data that's conforming to the
+     * user_data_registered_itu_t_t35() syntax of SEI message for ST 2094-40.
+     *<p>
+     * For decoders:
+     *<p>
+     * When a decoder is configured for one of the HDR10+ profiles that uses
+     * out-of-band metadata (such as {@link
+     * MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus} or {@link
+     * MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus}), this
+     * parameter sets the HDR10+ metadata on the next input buffer queued
+     * to the decoder. A decoder supporting these profiles must propagate
+     * the metadata to the format of the output buffer corresponding to this
+     * particular input buffer (under key {@link MediaFormat#KEY_HDR10_PLUS_INFO}).
+     * The metadata should be applied to that output buffer and the buffers
+     * following it (in display order), until the next output buffer (in
+     * display order) upon which an HDR10+ metadata is set.
+     *<p>
+     * This parameter shouldn't be set if the decoder is not configured for
+     * an HDR10+ profile that uses out-of-band metadata. In particular,
+     * it shouldn't be set for HDR10+ profiles that uses in-band metadata
+     * where the metadata is embedded in the input buffers, for example
+     * {@link MediaCodecInfo.CodecProfileLevel#HEVCProfileMain10HDR10Plus}.
+     *<p>
+     * For encoders:
+     *<p>
+     * When an encoder is configured for one of the HDR10+ profiles and the
+     * operates in byte buffer input mode (instead of surface input mode),
+     * this parameter sets the HDR10+ metadata on the next input buffer queued
+     * to the encoder. For the HDR10+ profiles that uses out-of-band metadata
+     * (such as {@link MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus},
+     * or {@link MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus}),
+     * the metadata must be propagated to the format of the output buffer
+     * corresponding to this particular input buffer (under key {@link
+     * MediaFormat#KEY_HDR10_PLUS_INFO}). For the HDR10+ profiles that uses
+     * in-band metadata (such as {@link
+     * MediaCodecInfo.CodecProfileLevel#HEVCProfileMain10HDR10Plus}), the
+     * metadata info must be embedded in the corresponding output buffer itself.
+     *<p>
+     * This parameter shouldn't be set if the encoder is not configured for
+     * an HDR10+ profile, or if it's operating in surface input mode.
+     *<p>
+     *
+     * @see MediaFormat#KEY_HDR10_PLUS_INFO
+     */
+    public static final String PARAMETER_KEY_HDR10_PLUS_INFO = MediaFormat.KEY_HDR10_PLUS_INFO;
+
+    /**
      * Communicate additional parameter changes to the component instance.
      * <b>Note:</b> Some of these parameter changes may silently fail to apply.
      *
@@ -3325,7 +3374,14 @@
         int i = 0;
         for (final String key: params.keySet()) {
             keys[i] = key;
-            values[i] = params.get(key);
+            Object value = params.get(key);
+
+            // Bundle's byte array is a byte[], JNI layer only takes ByteBuffer
+            if (value instanceof byte[]) {
+                values[i] = ByteBuffer.wrap((byte[])value);
+            } else {
+                values[i] = value;
+            }
             ++i;
         }
 
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 6301993..9025829 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1135,6 +1135,10 @@
                 maxChannels = 6;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) {
                 maxChannels = 16;
+            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3_JOC)) {
+                sampleRates = new int[] { 48000 };
+                bitRates = Range.create(32000, 6144000);
+                maxChannels = 16;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC4)) {
                 sampleRates = new int[] { 44100, 48000, 96000, 192000 };
                 bitRates = Range.create(16000, 2688000);
@@ -2516,6 +2520,8 @@
                         case CodecProfileLevel.VP9Profile3:
                         case CodecProfileLevel.VP9Profile2HDR:
                         case CodecProfileLevel.VP9Profile3HDR:
+                        case CodecProfileLevel.VP9Profile2HDR10Plus:
+                        case CodecProfileLevel.VP9Profile3HDR10Plus:
                             break;
                         default:
                             Log.w(TAG, "Unrecognized profile "
@@ -2608,7 +2614,9 @@
                     switch (profileLevel.profile) {
                         case CodecProfileLevel.HEVCProfileMain:
                         case CodecProfileLevel.HEVCProfileMain10:
+                        case CodecProfileLevel.HEVCProfileMainStill:
                         case CodecProfileLevel.HEVCProfileMain10HDR10:
+                        case CodecProfileLevel.HEVCProfileMain10HDR10Plus:
                             break;
                         default:
                             Log.w(TAG, "Unrecognized profile "
@@ -2999,6 +3007,8 @@
         // HDR profiles also support passing HDR metadata
         public static final int VP9Profile2HDR = 0x1000;
         public static final int VP9Profile3HDR = 0x2000;
+        public static final int VP9Profile2HDR10Plus = 0x4000;
+        public static final int VP9Profile3HDR10Plus = 0x8000;
 
         // from OMX_VIDEO_VP9LEVELTYPE
         public static final int VP9Level1  = 0x1;
@@ -3021,6 +3031,7 @@
         public static final int HEVCProfileMain10      = 0x02;
         public static final int HEVCProfileMainStill   = 0x04;
         public static final int HEVCProfileMain10HDR10 = 0x1000;
+        public static final int HEVCProfileMain10HDR10Plus = 0x2000;
 
         // from OMX_VIDEO_HEVCLEVELTYPE
         public static final int HEVCMainTierLevel1  = 0x1;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index b7743c9..594a224 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -147,6 +147,7 @@
     public static final String MIMETYPE_AUDIO_MSGSM = "audio/gsm";
     public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3";
     public static final String MIMETYPE_AUDIO_EAC3 = "audio/eac3";
+    public static final String MIMETYPE_AUDIO_EAC3_JOC = "audio/eac3-joc";
     public static final String MIMETYPE_AUDIO_AC4 = "audio/ac4";
     public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
 
@@ -910,6 +911,27 @@
     public static final String KEY_HDR_STATIC_INFO = "hdr-static-info";
 
     /**
+     * An optional key describing the HDR10+ metadata of the video content.
+     *
+     * The associated value is a ByteBuffer containing HDR10+ metadata conforming to the
+     * user_data_registered_itu_t_t35() syntax of SEI message for ST 2094-40. This key will
+     * be present on:
+     *<p>
+     * - The formats of output buffers of a decoder configured for HDR10+ profiles (such as
+     *   {@link MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus}, {@link
+     *   MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus} or {@link
+     *   MediaCodecInfo.CodecProfileLevel#HEVCProfileMain10HDR10Plus}), or
+     *<p>
+     * - The formats of output buffers of an encoder configured for an HDR10+ profiles that
+     *   uses out-of-band metadata (such as {@link
+     *   MediaCodecInfo.CodecProfileLevel#VP9Profile2HDR10Plus} or {@link
+     *   MediaCodecInfo.CodecProfileLevel#VP9Profile3HDR10Plus}).
+     *
+     * @see MediaCodec#PARAMETER_KEY_HDR10_PLUS_INFO
+     */
+    public static final String KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
+
+    /**
      * A key describing a unique ID for the content of a media track.
      *
      * <p>This key is used by {@link MediaExtractor}. Some extractors provide multiple encodings
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 4805780..9038f72 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1711,10 +1711,12 @@
     public MediaTimestamp getTimestamp() {
         try {
             // TODO: get the timestamp from native side
-            return new MediaTimestamp(
-                    getCurrentPosition() * 1000L,
-                    System.nanoTime(),
-                    getState() == PLAYER_STATE_PLAYING ? getPlaybackParams().getSpeed() : 0.f);
+            return new MediaTimestamp.Builder()
+                    .setMediaTimestamp(
+                        getCurrentPosition() * 1000L,
+                        System.nanoTime(),
+                        getState() == PLAYER_STATE_PLAYING ? getPlaybackParams().getSpeed() : 0.f)
+                    .build();
         } catch (IllegalStateException e) {
             return null;
         }
@@ -2398,11 +2400,13 @@
                             return;
                         }
                         Iterator<Value> in = playerMsg.getValuesList().iterator();
-                        SubtitleData data = new SubtitleData(
-                                in.next().getInt32Value(),  // trackIndex
-                                in.next().getInt64Value(),  // startTimeUs
-                                in.next().getInt64Value(),  // durationUs
-                                in.next().getBytesValue().toByteArray());  // data
+                        SubtitleData data = new SubtitleData.Builder()
+                                .setSubtitleData(
+                                    in.next().getInt32Value(),  // trackIndex
+                                    in.next().getInt64Value(),  // startTimeUs
+                                    in.next().getInt64Value(),  // durationUs
+                                    in.next().getBytesValue().toByteArray())  // data
+                                .build();
                         sendEvent(new EventNotifier() {
                             @Override
                             public void notify(EventCallback callback) {
@@ -2426,9 +2430,11 @@
                             return;
                         }
                         Iterator<Value> in = playerMsg.getValuesList().iterator();
-                        data = new TimedMetaData(
-                                in.next().getInt64Value(),  // timestampUs
-                                in.next().getBytesValue().toByteArray());  // metaData
+                        data = new TimedMetaData.Builder()
+                                .setTimedMetaData(
+                                    in.next().getInt64Value(),  // timestampUs
+                                    in.next().getBytesValue().toByteArray())  // metaData
+                                .build();
                     } else {
                         data = null;
                     }
diff --git a/media/java/android/media/MediaTimestamp.java b/media/java/android/media/MediaTimestamp.java
index e079a8e..03e454c 100644
--- a/media/java/android/media/MediaTimestamp.java
+++ b/media/java/android/media/MediaTimestamp.java
@@ -16,6 +16,9 @@
 
 package android.media;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
 /**
  * An immutable object that represents the linear correlation between the media time
  * and the system time. It contains the media clock rate, together with the media timestamp
@@ -117,4 +120,71 @@
                 + " clockRate=" + clockRate
                 + "}";
     }
+
+    /**
+     * Builder class for {@link MediaTimestamp} objects.
+     * <p> Here is an example where <code>Builder</code> is used to define the
+     * {@link MediaTimestamp}:
+     *
+     * <pre class="prettyprint">
+     * MediaTimestamp mts = new MediaTimestamp.Builder()
+     *         .setMediaTimestamp(mediaTime, systemTime, rate)
+     *         .build();
+     * </pre>
+     * @hide
+     */
+    @SystemApi
+    public static class Builder {
+        long mMediaTimeUs;
+        long mNanoTime;
+        float mClockRate = 1.0f;
+
+        /**
+         * Constructs a new Builder with the defaults.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a new Builder from a given {@link MediaTimestamp} instance
+         * @param mts the {@link MediaTimestamp} object whose data will be reused
+         * in the new Builder.
+         */
+        public Builder(@NonNull MediaTimestamp mts) {
+            if (mts == null) {
+                throw new IllegalArgumentException("null MediaTimestamp is not allowed");
+            }
+            mMediaTimeUs = mts.mediaTimeUs;
+            mNanoTime = mts.nanoTime;
+            mClockRate = mts.clockRate;
+        }
+
+        /**
+         * Combines all of the fields that have been set and return a new
+         * {@link MediaTimestamp} object.
+         *
+         * @return a new {@link MediaTimestamp} object
+         */
+        public @NonNull MediaTimestamp build() {
+            return new MediaTimestamp(mMediaTimeUs, mNanoTime, mClockRate);
+        }
+
+        /**
+         * Sets the info of media timestamp.
+         *
+         * @param mediaTimeUs the media time of the anchor in microseconds
+         * @param nanoTime the {@link java.lang.System#nanoTime system time} corresponding to
+         *     the media time in nanoseconds.
+         * @param clockRate the rate of the media clock in relation to the system time.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setMediaTimestamp(
+                long mediaTimeUs, long nanoTime, float clockRate) {
+            mMediaTimeUs = mediaTimeUs;
+            mNanoTime = nanoTime;
+            mClockRate = clockRate;
+
+            return this;
+        }
+    }
 }
diff --git a/media/java/android/media/TimedMetaData.java b/media/java/android/media/TimedMetaData.java
index bcc18ef..2a89888 100644
--- a/media/java/android/media/TimedMetaData.java
+++ b/media/java/android/media/TimedMetaData.java
@@ -148,7 +148,7 @@
          *     It should not be null.
          * @return the same Builder instance.
          */
-        public @NonNull Builder setTimedMetaData(int timestamp, @NonNull byte[] metaData) {
+        public @NonNull Builder setTimedMetaData(long timestamp, @NonNull byte[] metaData) {
             if (metaData == null) {
                 throw new IllegalArgumentException("null metaData is not allowed");
             }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 5a9a7c8..6a06dd0 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -96,15 +96,9 @@
      * @return The binder object from the system
      * @hide
      */
-    @SystemApi
     public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub,
-            @NonNull String tag, int userId) {
-        try {
-            return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-        return null;
+            @NonNull String tag, int userId) throws RemoteException {
+        return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
     }
 
     /**
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index d087176..1737b64 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -26,7 +26,6 @@
 import android.car.Car;
 import android.car.CarNotConnectedException;
 import android.car.media.CarAudioManager;
-import android.car.media.ICarVolumeCallback;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -106,7 +105,8 @@
     private ListItemAdapter mPagedListAdapter;
     private Car mCar;
     private CarAudioManager mCarAudioManager;
-    private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
+    private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback =
+            new CarAudioManager.CarVolumeCallback() {
         @Override
         public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
             // TODO: Include zoneId into consideration.
@@ -162,7 +162,7 @@
                 if (mPagedListAdapter != null) {
                     mPagedListAdapter.notifyDataSetChanged();
                 }
-                mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder());
+                mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
             } catch (CarNotConnectedException e) {
                 Log.e(TAG, "Car is not connected!", e);
             }
@@ -440,11 +440,7 @@
     }
 
     private void cleanupAudioManager() {
-        try {
-            mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
-        } catch (CarNotConnectedException e) {
-            Log.e(TAG, "Car is not connected!", e);
-        }
+        mCarAudioManager.unregisterCarVolumeCallback(mVolumeChangeCallback);
         mVolumeLineItems.clear();
         mCarAudioManager = null;
     }
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 0cad5af..133d8ba 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -27,20 +27,15 @@
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.IPackageManager;
-import android.database.ContentObserver;
 import android.ext.services.notification.AgingHelper.Callback;
-import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.Handler;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
-import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.NotificationAssistantService;
 import android.service.notification.NotificationStats;
@@ -92,8 +87,6 @@
         PREJUDICAL_DISMISSALS.add(REASON_LISTENER_CANCEL);
     }
 
-    private float mDismissToViewRatioLimit;
-    private int mStreakLimit;
     private SmartActionsHelper mSmartActionsHelper;
     private NotificationCategorizer mNotificationCategorizer;
     private AgingHelper mAgingHelper;
@@ -107,7 +100,11 @@
     private Ranking mFakeRanking = null;
     private AtomicFile mFile = null;
     private IPackageManager mPackageManager;
-    protected SettingsObserver mSettingsObserver;
+
+    @VisibleForTesting
+    protected AssistantSettings.Factory mSettingsFactory = AssistantSettings.FACTORY;
+    @VisibleForTesting
+    protected AssistantSettings mSettings;
 
     public Assistant() {
     }
@@ -118,7 +115,8 @@
         // Contexts are correctly hooked up by the creation step, which is required for the observer
         // to be hooked up/initialized.
         mPackageManager = ActivityThread.getPackageManager();
-        mSettingsObserver = new SettingsObserver(mHandler);
+        mSettings = mSettingsFactory.createAndRegister(mHandler,
+                getApplicationContext().getContentResolver(), getUserId(), this::updateThresholds);
         mSmartActionsHelper = new SmartActionsHelper();
         mNotificationCategorizer = new NotificationCategorizer();
         mAgingHelper = new AgingHelper(getContext(),
@@ -216,11 +214,11 @@
         if (!isForCurrentUser(sbn)) {
             return null;
         }
-        NotificationEntry entry = new NotificationEntry(
-                ActivityThread.getPackageManager(), sbn, channel);
+        NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel);
         ArrayList<Notification.Action> actions =
-                mSmartActionsHelper.suggestActions(this, entry);
-        ArrayList<CharSequence> replies = mSmartActionsHelper.suggestReplies(this, entry);
+                mSmartActionsHelper.suggestActions(this, entry, mSettings);
+        ArrayList<CharSequence> replies =
+                mSmartActionsHelper.suggestReplies(this, entry, mSettings);
         return createEnqueuedNotificationAdjustment(entry, actions, replies);
     }
 
@@ -239,8 +237,7 @@
         if (!smartReplies.isEmpty()) {
             signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
         }
-        if (Settings.Secure.getInt(getContentResolver(),
-                Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
+        if (mSettings.mNewInterruptionModel) {
             if (mNotificationCategorizer.shouldSilence(entry)) {
                 final int importance = entry.getImportance() < IMPORTANCE_LOW
                         ? entry.getImportance() : IMPORTANCE_LOW;
@@ -460,6 +457,11 @@
     }
 
     @VisibleForTesting
+    public void setSmartActionsHelper(SmartActionsHelper smartActionsHelper) {
+        mSmartActionsHelper = smartActionsHelper;
+    }
+
+    @VisibleForTesting
     public ChannelImpressions getImpressions(String key) {
         synchronized (mkeyToImpressions) {
             return mkeyToImpressions.get(key);
@@ -475,10 +477,20 @@
 
     private ChannelImpressions createChannelImpressionsWithThresholds() {
         ChannelImpressions impressions = new ChannelImpressions();
-        impressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit);
+        impressions.updateThresholds(mSettings.mDismissToViewRatioLimit, mSettings.mStreakLimit);
         return impressions;
     }
 
+    private void updateThresholds() {
+        // Update all existing channel impression objects with any new limits/thresholds.
+        synchronized (mkeyToImpressions) {
+            for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) {
+                channelImpressions.updateThresholds(
+                        mSettings.mDismissToViewRatioLimit, mSettings.mStreakLimit);
+            }
+        }
+    }
+
     protected final class AgingCallback implements Callback {
         @Override
         public void sendAdjustment(String key, int newImportance) {
@@ -495,51 +507,4 @@
         }
     }
 
-    /**
-     * Observer for updates on blocking helper threshold values.
-     */
-    protected final class SettingsObserver extends ContentObserver {
-        private final Uri STREAK_LIMIT_URI =
-                Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT);
-        private final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI =
-                Settings.Global.getUriFor(
-                        Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT);
-
-        public SettingsObserver(Handler handler) {
-            super(handler);
-            ContentResolver resolver = getApplicationContext().getContentResolver();
-            resolver.registerContentObserver(
-                    DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, getUserId());
-            resolver.registerContentObserver(STREAK_LIMIT_URI, false, this, getUserId());
-
-            // Update all uris on creation.
-            update(null);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            update(uri);
-        }
-
-        private void update(Uri uri) {
-            ContentResolver resolver = getApplicationContext().getContentResolver();
-            if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) {
-                mDismissToViewRatioLimit = Settings.Global.getFloat(
-                        resolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
-                        ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT);
-            }
-            if (uri == null || STREAK_LIMIT_URI.equals(uri)) {
-                mStreakLimit = Settings.Global.getInt(
-                        resolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
-                        ChannelImpressions.DEFAULT_STREAK_LIMIT);
-            }
-
-            // Update all existing channel impression objects with any new limits/thresholds.
-            synchronized (mkeyToImpressions) {
-                for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) {
-                    channelImpressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit);
-                }
-            }
-        }
-    }
 }
diff --git a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
new file mode 100644
index 0000000..39a1676
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
@@ -0,0 +1,140 @@
+/**
+ * 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.ext.services.notification;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Observes the settings for {@link Assistant}.
+ */
+final class AssistantSettings extends ContentObserver {
+    public static Factory FACTORY = AssistantSettings::createAndRegister;
+    private static final boolean DEFAULT_GENERATE_REPLIES = true;
+    private static final boolean DEFAULT_GENERATE_ACTIONS = true;
+    private static final int DEFAULT_NEW_INTERRUPTION_MODEL_INT = 1;
+
+    private static final Uri STREAK_LIMIT_URI =
+            Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT);
+    private static final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI =
+            Settings.Global.getUriFor(
+                    Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT);
+    private static final Uri SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI =
+            Settings.Global.getUriFor(
+                    Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS);
+    private static final Uri NOTIFICATION_NEW_INTERRUPTION_MODEL_URI =
+            Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL);
+
+    private static final String KEY_GENERATE_REPLIES = "generate_replies";
+    private static final String KEY_GENERATE_ACTIONS = "generate_actions";
+
+    private final KeyValueListParser mParser = new KeyValueListParser(',');
+    private final ContentResolver mResolver;
+    private final int mUserId;
+
+    @VisibleForTesting
+    protected final Runnable mOnUpdateRunnable;
+
+    // Actuall configuration settings.
+    float mDismissToViewRatioLimit;
+    int mStreakLimit;
+    boolean mGenerateReplies = DEFAULT_GENERATE_REPLIES;
+    boolean mGenerateActions = DEFAULT_GENERATE_ACTIONS;
+    boolean mNewInterruptionModel;
+
+    private AssistantSettings(Handler handler, ContentResolver resolver, int userId,
+            Runnable onUpdateRunnable) {
+        super(handler);
+        mResolver = resolver;
+        mUserId = userId;
+        mOnUpdateRunnable = onUpdateRunnable;
+    }
+
+    private static AssistantSettings createAndRegister(
+            Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
+        AssistantSettings assistantSettings =
+                new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
+        assistantSettings.register();
+        return assistantSettings;
+    }
+
+    /**
+     * Creates an instance but doesn't register it as an observer.
+     */
+    @VisibleForTesting
+    protected static AssistantSettings createForTesting(
+            Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
+        return new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
+    }
+
+    private void register() {
+        mResolver.registerContentObserver(
+                DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, mUserId);
+        mResolver.registerContentObserver(STREAK_LIMIT_URI, false, this, mUserId);
+        mResolver.registerContentObserver(
+                SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI, false, this, mUserId);
+
+        // Update all uris on creation.
+        update(null);
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        update(uri);
+    }
+
+    private void update(Uri uri) {
+        if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) {
+            mDismissToViewRatioLimit = Settings.Global.getFloat(
+                    mResolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
+                    ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT);
+        }
+        if (uri == null || STREAK_LIMIT_URI.equals(uri)) {
+            mStreakLimit = Settings.Global.getInt(
+                    mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
+                    ChannelImpressions.DEFAULT_STREAK_LIMIT);
+        }
+        if (uri == null || SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS_URI.equals(uri)) {
+            mParser.setString(
+                    Settings.Global.getString(mResolver,
+                            Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+            mGenerateReplies =
+                    mParser.getBoolean(KEY_GENERATE_REPLIES, DEFAULT_GENERATE_REPLIES);
+            mGenerateActions =
+                    mParser.getBoolean(KEY_GENERATE_ACTIONS, DEFAULT_GENERATE_ACTIONS);
+        }
+        if (uri == null || NOTIFICATION_NEW_INTERRUPTION_MODEL_URI.equals(uri)) {
+            int mNewInterruptionModelInt = Settings.Secure.getInt(
+                    mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL,
+                    DEFAULT_NEW_INTERRUPTION_MODEL_INT);
+            mNewInterruptionModel = mNewInterruptionModelInt == 1;
+        }
+
+        mOnUpdateRunnable.run();
+    }
+
+    public interface Factory {
+        AssistantSettings createAndRegister(Handler handler, ContentResolver resolver, int userId,
+                Runnable onUpdateRunnable);
+    }
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index 892267b..6f2b6c9 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -69,8 +69,11 @@
      * from notification text / message, we can replace most of the code here by consuming that API.
      */
     @NonNull
-    ArrayList<Notification.Action> suggestActions(
-            @Nullable Context context, @NonNull NotificationEntry entry) {
+    ArrayList<Notification.Action> suggestActions(@Nullable Context context,
+            @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) {
+        if (!settings.mGenerateActions) {
+            return EMPTY_ACTION_LIST;
+        }
         if (!isEligibleForActionAdjustment(entry)) {
             return EMPTY_ACTION_LIST;
         }
@@ -86,8 +89,11 @@
                 getMostSalientActionText(entry.getNotification()), MAX_SMART_ACTIONS);
     }
 
-    ArrayList<CharSequence> suggestReplies(
-            @Nullable Context context, @NonNull NotificationEntry entry) {
+    ArrayList<CharSequence> suggestReplies(@Nullable Context context,
+            @NonNull NotificationEntry entry, @NonNull AssistantSettings settings) {
+        if (!settings.mGenerateReplies) {
+            return EMPTY_REPLY_LIST;
+        }
         if (!isEligibleForReplyAdjustment(entry)) {
             return EMPTY_REPLY_LIST;
         }
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
new file mode 100644
index 0000000..fd23f2b
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
@@ -0,0 +1,162 @@
+/**
+ * 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.ext.services.notification;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.testing.TestableContext;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class AssistantSettingsTest {
+    private static final int USER_ID = 5;
+
+    @Rule
+    public final TestableContext mContext =
+            new TestableContext(InstrumentationRegistry.getContext(), null);
+
+    @Mock Runnable mOnUpdateRunnable;
+
+    private ContentResolver mResolver;
+    private AssistantSettings mAssistantSettings;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mResolver = mContext.getContentResolver();
+        Handler handler = new Handler(Looper.getMainLooper());
+
+        // To bypass real calls to global settings values, set the Settings values here.
+        Settings.Global.putFloat(mResolver,
+                Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, 0.8f);
+        Settings.Global.putInt(mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, 2);
+        Settings.Global.putString(mResolver,
+                Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+                "generate_replies=true,generate_actions=true");
+        Settings.Secure.putInt(mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
+
+        mAssistantSettings = AssistantSettings.createForTesting(
+                handler, mResolver, USER_ID, mOnUpdateRunnable);
+    }
+
+    @Test
+    public void testGenerateRepliesDisabled() {
+        Settings.Global.putString(mResolver,
+                Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+                "generate_replies=false");
+
+        // Notify for the settings values we updated.
+        mAssistantSettings.onChange(false,
+                Settings.Global.getUriFor(
+                        Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+
+        assertFalse(mAssistantSettings.mGenerateReplies);
+    }
+
+    @Test
+    public void testGenerateRepliesEnabled() {
+        Settings.Global.putString(mResolver,
+                Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_replies=true");
+
+        // Notify for the settings values we updated.
+        mAssistantSettings.onChange(false,
+                Settings.Global.getUriFor(
+                        Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+        assertTrue(mAssistantSettings.mGenerateReplies);
+    }
+
+    @Test
+    public void testGenerateActionsDisabled() {
+        Settings.Global.putString(mResolver,
+                Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_actions=false");
+
+        // Notify for the settings values we updated.
+        mAssistantSettings.onChange(false,
+                Settings.Global.getUriFor(
+                        Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+        assertTrue(mAssistantSettings.mGenerateReplies);
+    }
+
+    @Test
+    public void testGenerateActionsEnabled() {
+        Settings.Global.putString(mResolver,
+                Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS, "generate_actions=true");
+
+        // Notify for the settings values we updated.
+        mAssistantSettings.onChange(false,
+                Settings.Global.getUriFor(
+                        Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS));
+
+        assertTrue(mAssistantSettings.mGenerateReplies);
+    }
+
+    @Test
+    public void testStreakLimit() {
+        verify(mOnUpdateRunnable, never()).run();
+
+        // Update settings value.
+        int newStreakLimit = 4;
+        Settings.Global.putInt(mResolver,
+                Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, newStreakLimit);
+
+        // Notify for the settings value we updated.
+        mAssistantSettings.onChange(false, Settings.Global.getUriFor(
+                Settings.Global.BLOCKING_HELPER_STREAK_LIMIT));
+
+        assertEquals(newStreakLimit, mAssistantSettings.mStreakLimit);
+        verify(mOnUpdateRunnable).run();
+    }
+
+    @Test
+    public void testDismissToViewRatioLimit() {
+        verify(mOnUpdateRunnable, never()).run();
+
+        // Update settings value.
+        float newDismissToViewRatioLimit = 3f;
+        Settings.Global.putFloat(mResolver,
+                Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
+                newDismissToViewRatioLimit);
+
+        // Notify for the settings value we updated.
+        mAssistantSettings.onChange(false, Settings.Global.getUriFor(
+                Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT));
+
+        assertEquals(newDismissToViewRatioLimit, mAssistantSettings.mDismissToViewRatioLimit, 1e-6);
+        verify(mOnUpdateRunnable).run();
+    }
+}
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 2eb005a..0a95b83 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -33,13 +33,11 @@
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
-import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.os.Build;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
@@ -86,8 +84,7 @@
 
     @Mock INotificationManager mNoMan;
     @Mock AtomicFile mFile;
-    @Mock
-    IPackageManager mPackageManager;
+    @Mock IPackageManager mPackageManager;
 
     Assistant mAssistant;
     Application mApplication;
@@ -108,20 +105,26 @@
                 new Intent("android.service.notification.NotificationAssistantService");
         startIntent.setPackage("android.ext.services");
 
-        // To bypass real calls to global settings values, set the Settings values here.
-        Settings.Global.putFloat(mContext.getContentResolver(),
-                Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, 0.8f);
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, 2);
         mApplication = (Application) InstrumentationRegistry.getInstrumentation().
                 getTargetContext().getApplicationContext();
         // Force the test to use the correct application instead of trying to use a mock application
         setApplication(mApplication);
-        bindService(startIntent);
+
+        setupService();
         mAssistant = getService();
+
+        // Override the AssistantSettings factory.
+        mAssistant.mSettingsFactory = AssistantSettings::createForTesting;
+
+        bindService(startIntent);
+
+        mAssistant.mSettings.mDismissToViewRatioLimit = 0.8f;
+        mAssistant.mSettings.mStreakLimit = 2;
+        mAssistant.mSettings.mNewInterruptionModel = true;
         mAssistant.setNoMan(mNoMan);
         mAssistant.setFile(mFile);
         mAssistant.setPackageManager(mPackageManager);
+
         ApplicationInfo info = mock(ApplicationInfo.class);
         when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
                 .thenReturn(info);
@@ -408,6 +411,8 @@
         mAssistant.writeXml(serializer);
 
         Assistant assistant = new Assistant();
+        // onCreate is not invoked, so settings won't be initialised, unless we do it here.
+        assistant.mSettings = mAssistant.mSettings;
         assistant.readXml(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())));
 
         assertEquals(ci1, assistant.getImpressions(key1));
@@ -417,8 +422,6 @@
 
     @Test
     public void testSettingsProviderUpdate() {
-        ContentResolver resolver = mApplication.getContentResolver();
-
         // Set up channels
         String key = mAssistant.getKey("pkg1", 1, "channel1");
         ChannelImpressions ci = new ChannelImpressions();
@@ -435,19 +438,11 @@
         assertEquals(false, ci.shouldTriggerBlock());
 
         // Update settings values.
-        float newDismissToViewRatioLimit = 0f;
-        int newStreakLimit = 0;
-        Settings.Global.putFloat(resolver,
-                Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
-                newDismissToViewRatioLimit);
-        Settings.Global.putInt(resolver,
-                Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, newStreakLimit);
+        mAssistant.mSettings.mDismissToViewRatioLimit = 0f;
+        mAssistant.mSettings.mStreakLimit = 0;
 
         // Notify for the settings values we updated.
-        mAssistant.mSettingsObserver.onChange(false, Settings.Global.getUriFor(
-                Settings.Global.BLOCKING_HELPER_STREAK_LIMIT));
-        mAssistant.mSettingsObserver.onChange(false, Settings.Global.getUriFor(
-                Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT));
+        mAssistant.mSettings.mOnUpdateRunnable.run();
 
         // With the new threshold, the blocking helper should be triggered.
         assertEquals(true, ci.shouldTriggerBlock());
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 87d6e4a..be817d6 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -23,7 +23,6 @@
 import android.location.Criteria;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.os.UserHandle;
 import android.os.WorkSource;
 
@@ -34,87 +33,53 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
+class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
     private static final String TAG = "FusedLocationProvider";
 
     private static ProviderPropertiesUnbundled PROPERTIES = ProviderPropertiesUnbundled.create(
             false, false, false, false, true, true, true, Criteria.POWER_LOW,
             Criteria.ACCURACY_FINE);
 
-    private static final int MSG_ENABLE = 1;
-    private static final int MSG_DISABLE = 2;
-    private static final int MSG_SET_REQUEST = 3;
-
+    private final Context mContext;
+    private final Handler mHandler;
     private final FusionEngine mEngine;
 
-    private static class RequestWrapper {
-        public ProviderRequestUnbundled request;
-        public WorkSource source;
-        public RequestWrapper(ProviderRequestUnbundled request, WorkSource source) {
-            this.request = request;
-            this.source = source;
-        }
-    }
-
-    public FusedLocationProvider(Context context) {
-        super(TAG, PROPERTIES);
-        mEngine = new FusionEngine(context, Looper.myLooper());
-
-        // listen for user change
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        context.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                    mEngine.switchUser();
-                }
-            }
-        }, UserHandle.ALL, intentFilter, null, mHandler);
-    }
-
-    /**
-     * For serializing requests to mEngine.
-     */
-    private Handler mHandler = new Handler() {
+    private final BroadcastReceiver mUserSwitchReceiver = new BroadcastReceiver() {
         @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_ENABLE:
-                    mEngine.init(FusedLocationProvider.this);
-                    break;
-                case MSG_DISABLE:
-                    mEngine.deinit();
-                    break;
-                case MSG_SET_REQUEST:
-                    {
-                        RequestWrapper wrapper = (RequestWrapper) msg.obj;
-                        mEngine.setRequest(wrapper.request, wrapper.source);
-                        break;
-                    }
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                mEngine.switchUser();
             }
         }
     };
 
-    @Override
-    public void onEnable() {
-        mHandler.sendEmptyMessage(MSG_ENABLE);
+    FusedLocationProvider(Context context) {
+        super(TAG, PROPERTIES);
+
+        mContext = context;
+        mHandler = new Handler(Looper.myLooper());
+        mEngine = new FusionEngine(context, Looper.myLooper(), this);
     }
 
-    @Override
-    public void onDisable() {
-        mHandler.sendEmptyMessage(MSG_DISABLE);
+    void init() {
+        // listen for user change
+        mContext.registerReceiverAsUser(mUserSwitchReceiver, UserHandle.ALL,
+                new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
+    }
+
+    void destroy() {
+        mContext.unregisterReceiver(mUserSwitchReceiver);
+        mHandler.post(() -> mEngine.setRequest(null));
     }
 
     @Override
     public void onSetRequest(ProviderRequestUnbundled request, WorkSource source) {
-        mHandler.obtainMessage(MSG_SET_REQUEST, new RequestWrapper(request, source)).sendToTarget();
+        mHandler.post(() -> mEngine.setRequest(request));
     }
 
     @Override
     public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        // perform synchronously
         mEngine.dump(fd, pw, args);
     }
 }
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
index 12966cf..75bb5ec 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
@@ -21,27 +21,24 @@
 import android.os.IBinder;
 
 public class FusedLocationService extends Service {
+
     private FusedLocationProvider mProvider;
 
     @Override
     public IBinder onBind(Intent intent) {
         if (mProvider == null) {
-            mProvider = new FusedLocationProvider(getApplicationContext());
+            mProvider = new FusedLocationProvider(this);
+            mProvider.init();
         }
+
         return mProvider.getBinder();
     }
 
     @Override
-    public boolean onUnbind(Intent intent) {
-        // make sure to stop performing work
-        if (mProvider != null) {
-            mProvider.onDisable();
-        }
-      return false;
-    }
-
-    @Override
     public void onDestroy() {
-        mProvider = null;
+        if (mProvider != null) {
+            mProvider.destroy();
+            mProvider = null;
+        }
     }
 }
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
index 7a49524..e4610cf 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
@@ -16,14 +16,6 @@
 
 package com.android.location.fused;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashMap;
-
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.LocationRequestUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
-
 import android.content.Context;
 import android.location.Location;
 import android.location.LocationListener;
@@ -32,9 +24,16 @@
 import android.os.Looper;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import android.os.WorkSource;
 import android.util.Log;
 
+import com.android.location.provider.LocationProviderBase;
+import com.android.location.provider.LocationRequestUnbundled;
+import com.android.location.provider.ProviderRequestUnbundled;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+
 public class FusionEngine implements LocationListener {
     public interface Callback {
         void reportLocation(Location location);
@@ -47,72 +46,35 @@
 
     public static final long SWITCH_ON_FRESHNESS_CLIFF_NS = 11 * 1000000000L; // 11 seconds
 
-    private final Context mContext;
     private final LocationManager mLocationManager;
     private final Looper mLooper;
+    private final Callback mCallback;
 
     // all fields are only used on mLooper thread. except for in dump() which is not thread-safe
-    private Callback mCallback;
     private Location mFusedLocation;
     private Location mGpsLocation;
     private Location mNetworkLocation;
 
-    private boolean mEnabled;
     private ProviderRequestUnbundled mRequest;
 
     private final HashMap<String, ProviderStats> mStats = new HashMap<>();
 
-    public FusionEngine(Context context, Looper looper) {
-        mContext = context;
+    FusionEngine(Context context, Looper looper, Callback callback) {
         mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
         mNetworkLocation = new Location("");
         mNetworkLocation.setAccuracy(Float.MAX_VALUE);
         mGpsLocation = new Location("");
         mGpsLocation.setAccuracy(Float.MAX_VALUE);
         mLooper = looper;
+        mCallback = callback;
 
         mStats.put(GPS, new ProviderStats());
         mStats.put(NETWORK, new ProviderStats());
-
-    }
-
-    public void init(Callback callback) {
-        Log.i(TAG, "engine started (" + mContext.getPackageName() + ")");
-        mCallback = callback;
-    }
-
-    /**
-     * Called to stop doing any work, and release all resources
-     * This can happen when a better fusion engine is installed
-     * in a different package, and this one is no longer needed.
-     * Called on mLooper thread
-     */
-    public void deinit() {
-        mRequest = null;
-        disable();
-        Log.i(TAG, "engine stopped (" + mContext.getPackageName() + ")");
     }
 
     /** Called on mLooper thread */
-    public void enable() {
-        if (!mEnabled) {
-            mEnabled = true;
-            updateRequirements();
-        }
-    }
-
-    /** Called on mLooper thread */
-    public void disable() {
-        if (mEnabled) {
-            mEnabled = false;
-            updateRequirements();
-        }
-    }
-
-    /** Called on mLooper thread */
-    public void setRequest(ProviderRequestUnbundled request, WorkSource source) {
+    public void setRequest(ProviderRequestUnbundled request) {
         mRequest = request;
-        mEnabled = request.getReportLocation();
         updateRequirements();
     }
 
@@ -120,6 +82,7 @@
         public boolean requested;
         public long requestTime;
         public long minTime;
+
         @Override
         public String toString() {
             return (requested ? " REQUESTED" : " ---");
@@ -154,7 +117,7 @@
     }
 
     private void updateRequirements() {
-        if (!mEnabled || mRequest == null) {
+        if (mRequest == null || !mRequest.getReportLocation()) {
             mRequest = null;
             disableProvider(NETWORK);
             disableProvider(GPS);
@@ -200,29 +163,30 @@
      * Test whether one location (a) is better to use than another (b).
      */
     private static boolean isBetterThan(Location locationA, Location locationB) {
-      if (locationA == null) {
-        return false;
-      }
-      if (locationB == null) {
-        return true;
-      }
-      // A provider is better if the reading is sufficiently newer.  Heading
-      // underground can cause GPS to stop reporting fixes.  In this case it's
-      // appropriate to revert to cell, even when its accuracy is less.
-      if (locationA.getElapsedRealtimeNanos() > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
-        return true;
-      }
+        if (locationA == null) {
+            return false;
+        }
+        if (locationB == null) {
+            return true;
+        }
+        // A provider is better if the reading is sufficiently newer.  Heading
+        // underground can cause GPS to stop reporting fixes.  In this case it's
+        // appropriate to revert to cell, even when its accuracy is less.
+        if (locationA.getElapsedRealtimeNanos()
+                > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
+            return true;
+        }
 
-      // A provider is better if it has better accuracy.  Assuming both readings
-      // are fresh (and by that accurate), choose the one with the smaller
-      // accuracy circle.
-      if (!locationA.hasAccuracy()) {
-        return false;
-      }
-      if (!locationB.hasAccuracy()) {
-        return true;
-      }
-      return locationA.getAccuracy() < locationB.getAccuracy();
+        // A provider is better if it has better accuracy.  Assuming both readings
+        // are fresh (and by that accurate), choose the one with the smaller
+        // accuracy circle.
+        if (!locationA.hasAccuracy()) {
+            return false;
+        }
+        if (!locationB.hasAccuracy()) {
+            return true;
+        }
+        return locationA.getAccuracy() < locationB.getAccuracy();
     }
 
     private void updateFusedLocation() {
@@ -252,9 +216,9 @@
         }
 
         if (mCallback != null) {
-          mCallback.reportLocation(mFusedLocation);
+            mCallback.reportLocation(mFusedLocation);
         } else {
-          Log.w(TAG, "Location updates received while fusion engine not started");
+            Log.w(TAG, "Location updates received while fusion engine not started");
         }
     }
 
@@ -272,19 +236,22 @@
 
     /** Called on mLooper thread */
     @Override
-    public void onStatusChanged(String provider, int status, Bundle extras) {  }
+    public void onStatusChanged(String provider, int status, Bundle extras) {
+    }
 
     /** Called on mLooper thread */
     @Override
-    public void onProviderEnabled(String provider) {  }
+    public void onProviderEnabled(String provider) {
+    }
 
     /** Called on mLooper thread */
     @Override
-    public void onProviderDisabled(String provider) {  }
+    public void onProviderDisabled(String provider) {
+    }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         StringBuilder s = new StringBuilder();
-        s.append("mEnabled=").append(mEnabled).append(' ').append(mRequest).append('\n');
+        s.append(mRequest).append('\n');
         s.append("fused=").append(mFusedLocation).append('\n');
         s.append(String.format("gps %s\n", mGpsLocation));
         s.append("    ").append(mStats.get(GPS)).append('\n');
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 8f254e9..a7de631 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -200,7 +200,7 @@
             }
             if (objectInfo.getFormat() != MtpConstants.FORMAT_ASSOCIATION) {
                 if (!device.sendObject(sendObjectInfoResult.getObjectHandle(),
-                        sendObjectInfoResult.getCompressedSize(), source)) {
+                        sendObjectInfoResult.getCompressedSizeLong(), source)) {
                     throw new IOException("Failed to send contents of a document");
                 }
             }
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 03eafc4..478ee54 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -51,6 +51,7 @@
     <application
         android:allowClearUserData="true"
         android:label="@string/app_label"
+        android:icon="@drawable/ic_app_icon"
         android:supportsRtl="true">
 
         <service
diff --git a/packages/PrintSpooler/res/drawable/app_icon_foreground.xml b/packages/PrintSpooler/res/drawable/app_icon_foreground.xml
new file mode 100644
index 0000000..249e387
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/app_icon_foreground.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<inset
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetTop="25%"
+    android:insetRight="25%"
+    android:insetBottom="25%"
+    android:insetLeft="25%">
+
+    <vector
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+
+        <path
+            android:fillColor="#FFFFFF"
+            android:pathData="M19,8L5,8c-1.66,0 -3,1.34 -3,3v6h4v4h12v-4h4v-6c0,-1.66 -1.34,-3 -3,-3zM16,19L8,19v-5h8v5zM19,12c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM18,3L6,3v4h12L18,3z" />
+    </vector>
+</inset>
diff --git a/packages/PrintSpooler/res/drawable/ic_app_icon.xml b/packages/PrintSpooler/res/drawable/ic_app_icon.xml
new file mode 100644
index 0000000..82c18e0
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/ic_app_icon.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@*android:color/accent_device_default_light"/>
+    <foreground android:drawable="@drawable/app_icon_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index cc17b25..0126e7e5 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -18,6 +18,7 @@
         "SettingsLibSettingsSpinner",
         "SettingsLayoutPreference",
         "ActionButtonsPreference",
+        "SettingsLibEntityHeaderWidgets",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/EntityHeaderWidgets/Android.bp b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
new file mode 100644
index 0000000..3ca4ecd
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
@@ -0,0 +1,14 @@
+android_library {
+    name: "SettingsLibEntityHeaderWidgets",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+          "androidx.annotation_annotation",
+          "SettingsLibAppPreference"
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml b/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml
new file mode 100644
index 0000000..9f30eda
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/app_entities_header"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="24dp"
+    android:paddingEnd="8dp"
+    android:gravity="center"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/header_title"
+        android:layout_width="wrap_content"
+        android:layout_height="48dp"
+        android:gravity="center"
+        android:textAppearance="@style/AppEntitiesHeader.Text.HeaderTitle"/>
+
+    <LinearLayout
+        android:id="@+id/all_apps_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:gravity="center">
+
+        <include
+            android:id="@+id/app1_view"
+            layout="@layout/app_view"/>
+
+        <include
+            android:id="@+id/app2_view"
+            layout="@layout/app_view"/>
+
+        <include
+            android:id="@+id/app3_view"
+            layout="@layout/app_view"/>
+
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/header_details"
+        style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+        android:layout_width="wrap_content"
+        android:layout_height="48dp"
+        android:gravity="center"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
new file mode 100644
index 0000000..fcafa31
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:layout_marginEnd="16dp"
+    android:gravity="center"
+    android:orientation="vertical">
+
+    <ImageView
+        android:id="@+id/app_icon"
+        android:layout_width="@dimen/secondary_app_icon_size"
+        android:layout_height="@dimen/secondary_app_icon_size"
+        android:layout_marginBottom="12dp"/>
+
+    <TextView
+        android:id="@+id/app_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="2dp"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:textAppearance="@style/AppEntitiesHeader.Text.Title"/>
+
+    <TextView
+        android:id="@+id/app_summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:textAppearance="@style/AppEntitiesHeader.Text.Summary"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/values/styles.xml b/packages/SettingsLib/EntityHeaderWidgets/res/values/styles.xml
new file mode 100644
index 0000000..0eefd4b
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/values/styles.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <style name="AppEntitiesHeader.Text"
+           parent="@android:style/TextAppearance.Material.Subhead">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="AppEntitiesHeader.Text.HeaderTitle">
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="AppEntitiesHeader.Text.Title">
+        <item name="android:textSize">16sp</item>
+    </style>
+
+    <style name="AppEntitiesHeader.Text.Summary"
+           parent="@android:style/TextAppearance.Material.Body1">
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
new file mode 100644
index 0000000..8ccf89f
--- /dev/null
+++ b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * This is used to initialize view which was inflated
+ * from {@link R.xml.app_entities_header.xml}.
+ *
+ * <p>The view looks like below.
+ *
+ * <pre>
+ * --------------------------------------------------------------
+ * |                     Header title                           |
+ * --------------------------------------------------------------
+ * |    App1 icon       |   App2 icon        |   App3 icon      |
+ * |    App1 title      |   App2 title       |   App3 title     |
+ * |    App1 summary    |   App2 summary     |   App3 summary   |
+ * |-------------------------------------------------------------
+ * |                     Header details                         |
+ * --------------------------------------------------------------
+ * </pre>
+ *
+ * <p>How to use AppEntitiesHeaderController?
+ *
+ * <p>1. Add a {@link LayoutPreference} in layout XML file.
+ * <pre>
+ * &lt;com.android.settingslib.widget.LayoutPreference
+ *        android:key="app_entities_header"
+ *        android:layout="@layout/app_entities_header"/&gt;
+ * </pre>
+ *
+ * <p>2. Use AppEntitiesHeaderController to call below methods, then you can initialize
+ * view of <code>app_entities_header</code>.
+ *
+ * <pre>
+ *
+ * View headerView = ((LayoutPreference) screen.findPreference("app_entities_header"))
+ *         .findViewById(R.id.app_entities_header);
+ *
+ * AppEntitiesHeaderController.newInstance(context, headerView)
+ *         .setHeaderTitleRes(R.string.xxxxx)
+ *         .setHeaderDetailsRes(R.string.xxxxx)
+ *         .setHeaderDetailsClickListener(onClickListener)
+ *         .setAppEntity(0, icon, "app title", "app summary")
+ *         .setAppEntity(1, icon, "app title", "app summary")
+ *         .setAppEntity(2, icon, "app title", "app summary")
+ *         .apply();
+ * </pre>
+ */
+public class AppEntitiesHeaderController {
+
+    private static final String TAG = "AppEntitiesHeaderCtl";
+
+    @VisibleForTesting
+    static final int MAXIMUM_APPS = 3;
+
+    private final Context mContext;
+    private final TextView mHeaderTitleView;
+    private final Button mHeaderDetailsView;
+
+    private final AppEntity[] mAppEntities;
+    private final View[] mAppEntityViews;
+    private final ImageView[] mAppIconViews;
+    private final TextView[] mAppTitleViews;
+    private final TextView[] mAppSummaryViews;
+
+    private int mHeaderTitleRes;
+    private int mHeaderDetailsRes;
+    private View.OnClickListener mDetailsOnClickListener;
+
+    /**
+     * Creates a new instance of the controller.
+     *
+     * @param context the Context the view is running in
+     * @param appEntitiesHeaderView view was inflated from <code>app_entities_header</code>
+     */
+    public static AppEntitiesHeaderController newInstance(@NonNull Context context,
+            @NonNull View appEntitiesHeaderView) {
+        return new AppEntitiesHeaderController(context, appEntitiesHeaderView);
+    }
+
+    private AppEntitiesHeaderController(Context context, View appEntitiesHeaderView) {
+        mContext = context;
+        mHeaderTitleView = appEntitiesHeaderView.findViewById(R.id.header_title);
+        mHeaderDetailsView = appEntitiesHeaderView.findViewById(R.id.header_details);
+
+        mAppEntities = new AppEntity[MAXIMUM_APPS];
+        mAppIconViews = new ImageView[MAXIMUM_APPS];
+        mAppTitleViews = new TextView[MAXIMUM_APPS];
+        mAppSummaryViews = new TextView[MAXIMUM_APPS];
+
+        mAppEntityViews = new View[]{
+                appEntitiesHeaderView.findViewById(R.id.app1_view),
+                appEntitiesHeaderView.findViewById(R.id.app2_view),
+                appEntitiesHeaderView.findViewById(R.id.app3_view)
+        };
+
+        // Initialize view in advance, so we won't take too much time to do it when controller is
+        // binding view.
+        for (int index = 0; index < MAXIMUM_APPS; index++) {
+            final View appView = mAppEntityViews[index];
+            mAppIconViews[index] = (ImageView) appView.findViewById(R.id.app_icon);
+            mAppTitleViews[index] = (TextView) appView.findViewById(R.id.app_title);
+            mAppSummaryViews[index] = (TextView) appView.findViewById(R.id.app_summary);
+        }
+    }
+
+    /**
+     * Set the text resource for app entities header title.
+     */
+    public AppEntitiesHeaderController setHeaderTitleRes(@StringRes int titleRes) {
+        mHeaderTitleRes = titleRes;
+        return this;
+    }
+
+    /**
+     * Set the text resource for app entities header details.
+     */
+    public AppEntitiesHeaderController setHeaderDetailsRes(@StringRes int detailsRes) {
+        mHeaderDetailsRes = detailsRes;
+        return this;
+    }
+
+    /**
+     * Register a callback to be invoked when header details view is clicked.
+     */
+    public AppEntitiesHeaderController setHeaderDetailsClickListener(
+            @Nullable View.OnClickListener clickListener) {
+        mDetailsOnClickListener = clickListener;
+        return this;
+    }
+
+    /**
+     * Set an app entity at a specified position view.
+     *
+     * @param index the index at which the specified view is to be inserted
+     * @param icon the icon of app entity
+     * @param titleRes the title of app entity
+     * @param summaryRes the summary of app entity
+     * @return this {@code AppEntitiesHeaderController} object
+     */
+    public AppEntitiesHeaderController setAppEntity(int index, @NonNull Drawable icon,
+            @Nullable CharSequence titleRes, @Nullable CharSequence summaryRes) {
+        final AppEntity appEntity = new AppEntity(icon, titleRes, summaryRes);
+        mAppEntities[index] = appEntity;
+        return this;
+    }
+
+    /**
+     * Remove an app entity at a specified position view.
+     *
+     * @param index the index at which the specified view is to be removed
+     * @return this {@code AppEntitiesHeaderController} object
+     */
+    public AppEntitiesHeaderController removeAppEntity(int index) {
+        mAppEntities[index] = null;
+        return this;
+    }
+
+    /**
+     * Clear all app entities in app entities header.
+     *
+     * @return this {@code AppEntitiesHeaderController} object
+     */
+    public AppEntitiesHeaderController clearAllAppEntities() {
+        for (int index = 0; index < MAXIMUM_APPS; index++) {
+            removeAppEntity(index);
+        }
+        return this;
+    }
+
+    /**
+     * Done mutating app entities header, rebinds everything.
+     */
+    public void apply() {
+        bindHeaderTitleView();
+        bindHeaderDetailsView();
+
+        // Rebind all apps view
+        for (int index = 0; index < MAXIMUM_APPS; index++) {
+            bindAppEntityView(index);
+        }
+    }
+
+    private void bindHeaderTitleView() {
+        CharSequence titleText = "";
+        try {
+            titleText = mContext.getText(mHeaderTitleRes);
+        } catch (Resources.NotFoundException e) {
+            Log.e(TAG, "Resource of header title can't not be found!", e);
+        }
+        mHeaderTitleView.setText(titleText);
+        mHeaderTitleView.setVisibility(
+                TextUtils.isEmpty(titleText) ? View.GONE : View.VISIBLE);
+    }
+
+    private void bindHeaderDetailsView() {
+        CharSequence detailsText = "";
+        try {
+            detailsText = mContext.getText(mHeaderDetailsRes);
+        } catch (Resources.NotFoundException e) {
+            Log.e(TAG, "Resource of header details can't not be found!", e);
+        }
+        mHeaderDetailsView.setText(detailsText);
+        mHeaderDetailsView.setVisibility(
+                TextUtils.isEmpty(detailsText) ? View.GONE : View.VISIBLE);
+        mHeaderDetailsView.setOnClickListener(mDetailsOnClickListener);
+    }
+
+    private void bindAppEntityView(int index) {
+        final AppEntity appEntity = mAppEntities[index];
+        mAppEntityViews[index].setVisibility(appEntity != null ? View.VISIBLE : View.GONE);
+
+        if (appEntity != null) {
+            mAppIconViews[index].setImageDrawable(appEntity.icon);
+
+            mAppTitleViews[index].setVisibility(
+                    TextUtils.isEmpty(appEntity.title) ? View.INVISIBLE : View.VISIBLE);
+            mAppTitleViews[index].setText(appEntity.title);
+
+            mAppSummaryViews[index].setVisibility(
+                    TextUtils.isEmpty(appEntity.summary) ? View.INVISIBLE : View.VISIBLE);
+            mAppSummaryViews[index].setText(appEntity.summary);
+        }
+    }
+
+    private static class AppEntity {
+        public final Drawable icon;
+        public final CharSequence title;
+        public final CharSequence summary;
+
+        AppEntity(Drawable appIcon, CharSequence appTitle, CharSequence appSummary) {
+            icon = appIcon;
+            title = appTitle;
+            summary = appSummary;
+        }
+    }
+}
diff --git a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
index 7e65848..a046332 100644
--- a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
+++ b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
@@ -20,7 +20,7 @@
         android:height="24dp"
         android:viewportWidth="24"
         android:viewportHeight="24"
-        android:tint="?android:attr/colorControlNormal">
+        android:tint="?android:attr/colorAccent">
     <path
         android:fillColor="#FF000000"
         android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 1457fcf..74aaf3c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -38,6 +38,7 @@
 import android.text.Spanned;
 import android.text.style.ForegroundColorSpan;
 import android.text.style.ImageSpan;
+import android.util.Log;
 import android.view.MenuItem;
 import android.widget.TextView;
 
@@ -52,6 +53,9 @@
  * support message dialog.
  */
 public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
+
+    private static final String LOG_TAG = "RestrictedLockUtils";
+
     /**
      * @return drawables for displaying with settings that are locked by a device admin.
      */
@@ -305,6 +309,42 @@
         return null;
     }
 
+    /**
+     * @param userId user id of a managed profile.
+     * @return profile owner admin if cross profile calendar is disallowed.
+     */
+    public static EnforcedAdmin getCrossProfileCalendarEnforcingAdmin(Context context, int userId) {
+        final Context managedProfileContext = createPackageContextAsUser(
+                context, userId);
+        final DevicePolicyManager dpm = managedProfileContext.getSystemService(
+                DevicePolicyManager.class);
+        if (dpm == null) {
+            return null;
+        }
+        final EnforcedAdmin admin = getProfileOwner(context, userId);
+        if (admin == null) {
+            return null;
+        }
+        if (dpm.getCrossProfileCalendarPackages().isEmpty()) {
+            return admin;
+        }
+        return null;
+    }
+
+    /**
+     * @param userId user id of a managed profile.
+     * @return a context created from the given context for the given user, or null if it fails.
+     */
+    private static Context createPackageContextAsUser(Context context, int userId) {
+        try {
+            return context.createPackageContextAsUser(
+                    context.getPackageName(), 0 /* flags */, UserHandle.of(userId));
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(LOG_TAG, "Failed to create user context", e);
+        }
+        return null;
+    }
+
     public static EnforcedAdmin checkIfAccessibilityServiceDisallowed(Context context,
             String packageName, int userId) {
         DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
index ec8e956..2387b01 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
@@ -15,10 +15,9 @@
  */
 package com.android.settingslib.applications;
 
-import android.annotation.NonNull;
 import android.content.Context;
-import android.content.pm.permission.RuntimePermissionPresentationInfo;
-import android.content.pm.permission.RuntimePermissionPresenter;
+import android.permission.RuntimePermissionPresentationInfo;
+import android.permission.RuntimePermissionPresenter;
 
 import java.text.Collator;
 import java.util.ArrayList;
@@ -31,37 +30,33 @@
             final PermissionsResultCallback callback) {
         final RuntimePermissionPresenter presenter =
                 RuntimePermissionPresenter.getInstance(context);
-        presenter.getAppPermissions(pkg, new RuntimePermissionPresenter.OnResultCallback() {
-            @Override
-            public void onGetAppPermissions(
-                    @NonNull List<RuntimePermissionPresentationInfo> permissions) {
-                final int permissionCount = permissions.size();
+        presenter.getAppPermissions(pkg, permissions -> {
+            final int permissionCount = permissions.size();
 
-                int grantedStandardCount = 0;
-                int grantedAdditionalCount = 0;
-                int requestedCount = 0;
-                List<CharSequence> grantedStandardLabels = new ArrayList<>();
+            int grantedStandardCount = 0;
+            int grantedAdditionalCount = 0;
+            int requestedCount = 0;
+            List<CharSequence> grantedStandardLabels = new ArrayList<>();
 
-                for (int i = 0; i < permissionCount; i++) {
-                    RuntimePermissionPresentationInfo permission = permissions.get(i);
-                    requestedCount++;
-                    if (permission.isGranted()) {
-                        if (permission.isStandard()) {
-                            grantedStandardLabels.add(permission.getLabel());
-                            grantedStandardCount++;
-                        } else {
-                            grantedAdditionalCount++;
-                        }
+            for (int i = 0; i < permissionCount; i++) {
+                RuntimePermissionPresentationInfo permission = permissions.get(i);
+                requestedCount++;
+                if (permission.isGranted()) {
+                    if (permission.isStandard()) {
+                        grantedStandardLabels.add(permission.getLabel());
+                        grantedStandardCount++;
+                    } else {
+                        grantedAdditionalCount++;
                     }
                 }
-
-                Collator collator = Collator.getInstance();
-                collator.setStrength(Collator.PRIMARY);
-                Collections.sort(grantedStandardLabels, collator);
-
-                callback.onPermissionSummaryResult(grantedStandardCount, requestedCount,
-                        grantedAdditionalCount, grantedStandardLabels);
             }
+
+            Collator collator = Collator.getInstance();
+            collator.setStrength(Collator.PRIMARY);
+            Collections.sort(grantedStandardLabels, collator);
+
+            callback.onPermissionSummaryResult(grantedStandardCount, requestedCount,
+                    grantedAdditionalCount, grantedStandardLabels);
         }, null);
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 9270d13..1bffff7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -71,15 +71,8 @@
 
     boolean mJustDiscovered;
 
-    private int mMessageRejectionCount;
-
     private final Collection<Callback> mCallbacks = new ArrayList<>();
 
-    // How many times user should reject the connection to make the choice persist.
-    private final static int MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST = 2;
-
-    private final static String MESSAGE_REJECTION_COUNT_PREFS_NAME = "bluetooth_message_reject";
-
     /**
      * Last time a bt profile auto-connect was attempted.
      * If an ACTION_UUID intent comes in within
@@ -348,7 +341,6 @@
         fetchActiveDevices();
         migratePhonebookPermissionChoice();
         migrateMessagePermissionChoice();
-        fetchMessageRejectionCount();
 
         dispatchAttributesChanged();
     }
@@ -642,8 +634,6 @@
             mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
             mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
             mDevice.setSimAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
-            mMessageRejectionCount = 0;
-            saveMessageRejectionCount();
         }
 
         refresh();
@@ -797,34 +787,6 @@
         editor.commit();
     }
 
-    /**
-     * @return Whether this rejection should persist.
-     */
-    public boolean checkAndIncreaseMessageRejectionCount() {
-        if (mMessageRejectionCount < MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST) {
-            mMessageRejectionCount++;
-            saveMessageRejectionCount();
-        }
-        return mMessageRejectionCount >= MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST;
-    }
-
-    private void fetchMessageRejectionCount() {
-        SharedPreferences preference = mContext.getSharedPreferences(
-                MESSAGE_REJECTION_COUNT_PREFS_NAME, Context.MODE_PRIVATE);
-        mMessageRejectionCount = preference.getInt(mDevice.getAddress(), 0);
-    }
-
-    private void saveMessageRejectionCount() {
-        SharedPreferences.Editor editor = mContext.getSharedPreferences(
-                MESSAGE_REJECTION_COUNT_PREFS_NAME, Context.MODE_PRIVATE).edit();
-        if (mMessageRejectionCount == 0) {
-            editor.remove(mDevice.getAddress());
-        } else {
-            editor.putInt(mDevice.getAddress(), mMessageRejectionCount);
-        }
-        editor.commit();
-    }
-
     private void processPhonebookAccess() {
         if (mDevice.getBondState() != BluetoothDevice.BOND_BONDED) return;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 67cfe6b..91892ab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -363,7 +363,8 @@
             return null;
         }
         try {
-            return provider.call(context.getPackageName(), method, uriString, null);
+            return provider.call(context.getPackageName(), uri.getAuthority(),
+                    method, uriString, null);
         } catch (RemoteException e) {
             return null;
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
new file mode 100644
index 0000000..c3bc8da
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppEntitiesHeaderControllerTest {
+
+    private static final CharSequence TITLE = "APP_TITLE";
+    private static final CharSequence SUMMARY = "APP_SUMMARY";
+
+    @Rule
+    public final ExpectedException thrown = ExpectedException.none();
+
+    private Context mContext;
+    private Drawable mIcon;
+    private View mAppEntitiesHeaderView;
+    private AppEntitiesHeaderController mController;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mAppEntitiesHeaderView = LayoutInflater.from(mContext).inflate(
+                R.layout.app_entities_header, null /* root */);
+        mIcon = mContext.getDrawable(R.drawable.ic_menu);
+        mController = AppEntitiesHeaderController.newInstance(mContext,
+                mAppEntitiesHeaderView);
+    }
+
+    @Test
+    public void assert_amountOfMaximumAppsAreThree() {
+        assertThat(AppEntitiesHeaderController.MAXIMUM_APPS).isEqualTo(3);
+    }
+
+    @Test
+    public void setHeaderTitleRes_setTextRes_shouldSetToTitleView() {
+        mController.setHeaderTitleRes(R.string.expand_button_title).apply();
+        final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_title);
+
+        assertThat(view.getText()).isEqualTo(mContext.getText(R.string.expand_button_title));
+    }
+
+    @Test
+    public void setHeaderDetailsRes_setTextRes_shouldSetToDetailsView() {
+        mController.setHeaderDetailsRes(R.string.expand_button_title).apply();
+        final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
+
+        assertThat(view.getText()).isEqualTo(mContext.getText(R.string.expand_button_title));
+    }
+
+    @Test
+    public void setHeaderDetailsClickListener_setClickListener_detailsViewAttachClickListener() {
+        mController.setHeaderDetailsClickListener(v -> {
+        }).apply();
+        final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
+
+        assertThat(view.hasOnClickListeners()).isTrue();
+    }
+
+    @Test
+    public void setAppEntity_indexLessThanZero_shouldThrowArrayIndexOutOfBoundsException() {
+        thrown.expect(ArrayIndexOutOfBoundsException.class);
+
+        mController.setAppEntity(-1, mIcon, TITLE, SUMMARY);
+    }
+
+    @Test
+    public void asetAppEntity_indexGreaterThanMaximum_shouldThrowArrayIndexOutOfBoundsException() {
+        thrown.expect(ArrayIndexOutOfBoundsException.class);
+
+        mController.setAppEntity(AppEntitiesHeaderController.MAXIMUM_APPS + 1, mIcon, TITLE,
+                SUMMARY);
+    }
+
+    @Test
+    public void setAppEntity_addAppToIndex0_shouldShowAppView1() {
+        mController.setAppEntity(0, mIcon, TITLE, SUMMARY).apply();
+        final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
+        final ImageView appIconView = app1View.findViewById(R.id.app_icon);
+        final TextView appTitle = app1View.findViewById(R.id.app_title);
+        final TextView appSummary = app1View.findViewById(R.id.app_summary);
+
+        assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(appIconView.getDrawable()).isNotNull();
+        assertThat(appTitle.getText()).isEqualTo(TITLE);
+        assertThat(appSummary.getText()).isEqualTo(SUMMARY);
+    }
+
+    @Test
+    public void setAppEntity_addAppToIndex1_shouldShowAppView2() {
+        mController.setAppEntity(1, mIcon, TITLE, SUMMARY).apply();
+        final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
+        final ImageView appIconView = app2View.findViewById(R.id.app_icon);
+        final TextView appTitle = app2View.findViewById(R.id.app_title);
+        final TextView appSummary = app2View.findViewById(R.id.app_summary);
+
+        assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(appIconView.getDrawable()).isNotNull();
+        assertThat(appTitle.getText()).isEqualTo(TITLE);
+        assertThat(appSummary.getText()).isEqualTo(SUMMARY);
+    }
+
+    @Test
+    public void setAppEntity_addAppToIndex2_shouldShowAppView3() {
+        mController.setAppEntity(2, mIcon, TITLE, SUMMARY).apply();
+        final View app3View = mAppEntitiesHeaderView.findViewById(R.id.app3_view);
+        final ImageView appIconView = app3View.findViewById(R.id.app_icon);
+        final TextView appTitle = app3View.findViewById(R.id.app_title);
+        final TextView appSummary = app3View.findViewById(R.id.app_summary);
+
+        assertThat(app3View.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(appIconView.getDrawable()).isNotNull();
+        assertThat(appTitle.getText()).isEqualTo(TITLE);
+        assertThat(appSummary.getText()).isEqualTo(SUMMARY);
+    }
+
+    @Test
+    public void removeAppEntity_removeIndex0_shouldNotShowAppView1() {
+        mController.setAppEntity(0, mIcon, TITLE, SUMMARY)
+                .setAppEntity(1, mIcon, TITLE, SUMMARY).apply();
+        final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
+        final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
+
+        assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+
+        mController.removeAppEntity(0).apply();
+
+        assertThat(app1View.getVisibility()).isEqualTo(View.GONE);
+        assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void clearAllAppEntities_shouldNotShowAllAppViews() {
+        mController.setAppEntity(0, mIcon, TITLE, SUMMARY)
+                .setAppEntity(1, mIcon, TITLE, SUMMARY)
+                .setAppEntity(2, mIcon, TITLE, SUMMARY).apply();
+        final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
+        final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
+        final View app3View = mAppEntitiesHeaderView.findViewById(R.id.app3_view);
+
+        assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(app3View.getVisibility()).isEqualTo(View.VISIBLE);
+
+        mController.clearAllAppEntities().apply();
+        assertThat(app1View.getVisibility()).isEqualTo(View.GONE);
+        assertThat(app2View.getVisibility()).isEqualTo(View.GONE);
+        assertThat(app3View.getVisibility()).isEqualTo(View.GONE);
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
new file mode 100644
index 0000000..3520918
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -0,0 +1,355 @@
+/*
+ * 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.providers.settings;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.ActivityManager;
+import android.content.IContentProvider;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+import android.provider.Settings;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Receives shell commands from the command line related to device config flags, and dispatches them
+ * to the SettingsProvider.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DeviceConfigService extends Binder {
+    /**
+     * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
+     *     API.
+     */
+    private static final Uri CONFIG_CONTENT_URI =
+            Uri.parse("content://" + Settings.AUTHORITY + "/config");
+
+    final SettingsProvider mProvider;
+
+    public DeviceConfigService(SettingsProvider provider) {
+        mProvider = provider;
+    }
+
+    @Override
+    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+        (new MyShellCommand(mProvider)).exec(this, in, out, err, args, callback, resultReceiver);
+    }
+
+    static final class MyShellCommand extends ShellCommand {
+        final SettingsProvider mProvider;
+
+        enum CommandVerb {
+            UNSPECIFIED,
+            GET,
+            PUT,
+            DELETE,
+            LIST,
+            RESET,
+        }
+
+        MyShellCommand(SettingsProvider provider) {
+            mProvider = provider;
+        }
+
+        @Override
+        public int onCommand(String cmd) {
+            if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) {
+                onHelp();
+                return -1;
+            }
+
+            final PrintWriter perr = getErrPrintWriter();
+            boolean isValid = false;
+            CommandVerb verb;
+            if ("get".equalsIgnoreCase(cmd)) {
+                verb = CommandVerb.GET;
+            } else if ("put".equalsIgnoreCase(cmd)) {
+                verb = CommandVerb.PUT;
+            } else if ("delete".equalsIgnoreCase(cmd)) {
+                verb = CommandVerb.DELETE;
+            } else if ("list".equalsIgnoreCase(cmd)) {
+                verb = CommandVerb.LIST;
+                if (peekNextArg() == null) {
+                    isValid = true;
+                }
+            } else if ("reset".equalsIgnoreCase(cmd)) {
+                verb = CommandVerb.RESET;
+            } else {
+                // invalid
+                perr.println("Invalid command: " + cmd);
+                return -1;
+            }
+
+            int resetMode = -1;
+            boolean makeDefault = false;
+            String namespace = null;
+            String key = null;
+            String value = null;
+            String arg = null;
+            while ((arg = getNextArg()) != null) {
+                if (verb == CommandVerb.RESET) {
+                    if (resetMode == -1) {
+                        if ("untrusted_defaults".equalsIgnoreCase(arg)) {
+                            resetMode = Settings.RESET_MODE_UNTRUSTED_DEFAULTS;
+                        } else if ("untrusted_clear".equalsIgnoreCase(arg)) {
+                            resetMode = Settings.RESET_MODE_UNTRUSTED_CHANGES;
+                        } else if ("trusted_defaults".equalsIgnoreCase(arg)) {
+                            resetMode = Settings.RESET_MODE_TRUSTED_DEFAULTS;
+                        } else {
+                            // invalid
+                            perr.println("Invalid reset mode: " + arg);
+                            return -1;
+                        }
+                        if (peekNextArg() == null) {
+                            isValid = true;
+                        }
+                    } else {
+                        namespace = arg;
+                        if (peekNextArg() == null) {
+                            isValid = true;
+                        } else {
+                            // invalid
+                            perr.println("Too many arguments");
+                            return -1;
+                        }
+                    }
+                } else if (namespace == null) {
+                    namespace = arg;
+                    if (verb == CommandVerb.LIST) {
+                        if (peekNextArg() == null) {
+                            isValid = true;
+                        } else {
+                            // invalid
+                            perr.println("Too many arguments");
+                            return -1;
+                        }
+                    }
+                } else if (key == null) {
+                    key = arg;
+                    if ((verb == CommandVerb.GET || verb == CommandVerb.DELETE)) {
+                        if (peekNextArg() == null) {
+                            isValid = true;
+                        } else {
+                            // invalid
+                            perr.println("Too many arguments");
+                            return -1;
+                        }
+                    }
+                } else if (value == null) {
+                    value = arg;
+                    if (verb == CommandVerb.PUT && peekNextArg() == null) {
+                        isValid = true;
+                    }
+                } else if ("default".equalsIgnoreCase(arg)) {
+                    makeDefault = true;
+                    if (verb == CommandVerb.PUT && peekNextArg() == null) {
+                        isValid = true;
+                    } else {
+                        // invalid
+                        perr.println("Too many arguments");
+                        return -1;
+                    }
+                }
+            }
+
+            if (!isValid) {
+                perr.println("Bad arguments");
+                return -1;
+            }
+
+            final IContentProvider iprovider = mProvider.getIContentProvider();
+            final PrintWriter pout = getOutPrintWriter();
+            switch (verb) {
+                case GET:
+                    pout.println(get(iprovider, namespace, key));
+                    break;
+                case PUT:
+                    put(iprovider, namespace, key, value, makeDefault);
+                    break;
+                case DELETE:
+                    pout.println(delete(iprovider, namespace, key)
+                            ? "Successfully deleted " + key + " from " + namespace
+                            : "Failed to delete " + key + " from " + namespace);
+                    break;
+                case LIST:
+                    for (String line : list(iprovider, namespace)) {
+                        pout.println(line);
+                    }
+                    break;
+                case RESET:
+                    reset(iprovider, resetMode, namespace);
+                    break;
+                default:
+                    perr.println("Unspecified command");
+                    return -1;
+            }
+            return 0;
+        }
+
+        @Override
+        public void onHelp() {
+            PrintWriter pw = getOutPrintWriter();
+            pw.println("Device Config (device_config) commands:");
+            pw.println("  help");
+            pw.println("      Print this help text.");
+            pw.println("  get NAMESPACE KEY");
+            pw.println("      Retrieve the current value of KEY from the given NAMESPACE.");
+            pw.println("  put NAMESPACE KEY VALUE [default]");
+            pw.println("      Change the contents of KEY to VALUE for the given NAMESPACE.");
+            pw.println("      {default} to set as the default value.");
+            pw.println("  delete NAMESPACE KEY");
+            pw.println("      Delete the entry for KEY for the given NAMESPACE.");
+            pw.println("  list [NAMESPACE]");
+            pw.println("      Print all keys and values defined, optionally for the given "
+                    + "NAMESPACE.");
+            pw.println("  reset RESET_MODE [NAMESPACE]");
+            pw.println("      Reset all flag values, optionally for a NAMESPACE, according to "
+                    + "RESET_MODE.");
+            pw.println("      RESET_MODE is one of {untrusted_defaults, untrusted_clear, "
+                    + "trusted_defaults}");
+            pw.println("      NAMESPACE limits which flags are reset if provided, otherwise all "
+                    + "flags are reset");
+        }
+
+        private String get(IContentProvider provider, String namespace, String key) {
+            String compositeKey = namespace + "/" + key;
+            String result = null;
+            try {
+                Bundle args = new Bundle();
+                args.putInt(Settings.CALL_METHOD_USER_KEY,
+                        ActivityManager.getService().getCurrentUser().id);
+                Bundle b = provider.call(resolveCallingPackage(), Settings.CALL_METHOD_GET_CONFIG,
+                        compositeKey, args);
+                if (b != null) {
+                    result = b.getPairValue();
+                }
+            } catch (RemoteException e) {
+                throw new RuntimeException("Failed in IPC", e);
+            }
+            return result;
+        }
+
+        private void put(IContentProvider provider, String namespace, String key, String value,
+                boolean makeDefault) {
+            String compositeKey = namespace + "/" + key;
+
+            try {
+                Bundle args = new Bundle();
+                args.putString(Settings.NameValueTable.VALUE, value);
+                args.putInt(Settings.CALL_METHOD_USER_KEY,
+                        ActivityManager.getService().getCurrentUser().id);
+                if (makeDefault) {
+                    args.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
+                }
+                provider.call(resolveCallingPackage(), Settings.CALL_METHOD_PUT_CONFIG,
+                        compositeKey, args);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Failed in IPC", e);
+            }
+        }
+
+        private boolean delete(IContentProvider provider, String namespace, String key) {
+            String compositeKey = namespace + "/" + key;
+            boolean success;
+
+            try {
+                Bundle args = new Bundle();
+                args.putInt(Settings.CALL_METHOD_USER_KEY,
+                        ActivityManager.getService().getCurrentUser().id);
+                Bundle b = provider.call(resolveCallingPackage(),
+                        Settings.CALL_METHOD_DELETE_CONFIG, compositeKey, args);
+                success = (b != null && b.getInt(SettingsProvider.RESULT_ROWS_DELETED) == 1);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Failed in IPC", e);
+            }
+            return success;
+        }
+
+        private List<String> list(IContentProvider provider, @Nullable String namespace) {
+            final ArrayList<String> lines = new ArrayList<>();
+
+            try {
+                Bundle args = new Bundle();
+                args.putInt(Settings.CALL_METHOD_USER_KEY,
+                        ActivityManager.getService().getCurrentUser().id);
+                if (namespace != null) {
+                    args.putString(Settings.CALL_METHOD_PREFIX_KEY, namespace);
+                }
+                Bundle b = provider.call(resolveCallingPackage(),
+                        Settings.CALL_METHOD_LIST_CONFIG, null, args);
+                if (b != null) {
+                    Map<String, String> flagsToValues =
+                            (HashMap) b.getSerializable(Settings.NameValueTable.VALUE);
+                    for (String key : flagsToValues.keySet()) {
+                        lines.add(key + "=" + flagsToValues.get(key));
+                    }
+                }
+
+                Collections.sort(lines);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Failed in IPC", e);
+            }
+            return lines;
+        }
+
+        private void reset(IContentProvider provider, int resetMode, @Nullable String namespace) {
+            try {
+                Bundle args = new Bundle();
+                args.putInt(Settings.CALL_METHOD_USER_KEY,
+                        ActivityManager.getService().getCurrentUser().id);
+                args.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, resetMode);
+                args.putString(Settings.CALL_METHOD_PREFIX_KEY, namespace);
+                provider.call(
+                        resolveCallingPackage(), Settings.CALL_METHOD_RESET_CONFIG, null, args);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Failed in IPC", e);
+            }
+        }
+
+        private static String resolveCallingPackage() {
+            switch (Binder.getCallingUid()) {
+                case Process.ROOT_UID: {
+                    return "root";
+                }
+
+                case Process.SHELL_UID: {
+                    return "com.android.shell";
+                }
+
+                default: {
+                    return null;
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index ad88432..e3d3d81 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -208,9 +208,14 @@
                 GlobalSettingsProto.Autofill.MAX_VISIBLE_DATASETS);
         p.end(autofillToken);
 
+        final long backupToken = p.start(GlobalSettingsProto.BACKUP);
         dumpSetting(s, p,
                 Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
-                GlobalSettingsProto.BACKUP_AGENT_TIMEOUT_PARAMETERS);
+                GlobalSettingsProto.Backup.BACKUP_AGENT_TIMEOUT_PARAMETERS);
+        dumpSetting(s, p,
+                Settings.Global.BACKUP_MULTI_USER_ENABLED,
+                GlobalSettingsProto.Backup.BACKUP_MULTI_USER_ENABLED);
+        p.end(backupToken);
 
         final long batteryToken = p.start(GlobalSettingsProto.BATTERY);
         dumpSetting(s, p,
@@ -685,8 +690,14 @@
                 Settings.Global.GPU_DEBUG_LAYERS,
                 GlobalSettingsProto.Gpu.DEBUG_LAYERS);
         dumpSetting(s, p,
-                Settings.Global.ANGLE_ENABLED_APP,
-                GlobalSettingsProto.Gpu.ANGLE_ENABLED_APP);
+                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE,
+                GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_ALL_ANGLE);
+        dumpSetting(s, p,
+                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
+                GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_PKGS);
+        dumpSetting(s, p,
+                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
+                GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES);
         dumpSetting(s, p,
                 Settings.Global.GPU_DEBUG_LAYER_APP,
                 GlobalSettingsProto.Gpu.DEBUG_LAYER_APP);
@@ -1004,6 +1015,9 @@
         dumpSetting(s, p,
                 Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
                 GlobalSettingsProto.Notification.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS);
+        dumpSetting(s, p,
+                Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+                GlobalSettingsProto.Notification.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS);
         p.end(notificationToken);
 
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 00ea45c..424368d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -94,6 +94,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
@@ -147,6 +148,7 @@
     private static final String TABLE_SYSTEM = "system";
     private static final String TABLE_SECURE = "secure";
     private static final String TABLE_GLOBAL = "global";
+    private static final String TABLE_CONFIG = "config";
 
     // Old tables no longer exist.
     private static final String TABLE_FAVORITES = "favorites";
@@ -333,6 +335,7 @@
             startWatchingUserRestrictionChanges();
         });
         ServiceManager.addService("settings", new SettingsService(this));
+        ServiceManager.addService("device_config", new DeviceConfigService(this));
         return true;
     }
 
@@ -414,9 +417,8 @@
 
             case Settings.CALL_METHOD_PUT_CONFIG: {
                 String value = getSettingValue(args);
-                String tag = getSettingTag(args);
                 final boolean makeDefault = getSettingMakeDefault(args);
-                insertConfigSetting(name, value, tag, makeDefault, requestingUserId, false);
+                insertConfigSetting(name, value, null, makeDefault, requestingUserId, false);
                 break;
             }
 
@@ -444,8 +446,8 @@
 
             case Settings.CALL_METHOD_RESET_CONFIG: {
                 final int mode = getResetModeEnforcingPermission(args);
-                String tag = getSettingTag(args);
-                resetConfigSetting(requestingUserId, mode, tag);
+                String prefix = getSettingPrefix(args);
+                resetConfigSetting(requestingUserId, mode, prefix);
                 break;
             }
 
@@ -463,15 +465,8 @@
                 break;
             }
 
-            case Settings.CALL_METHOD_DELETE_SYSTEM: {
-                int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0;
-                Bundle result = new Bundle();
-                result.putInt(RESULT_ROWS_DELETED, rows);
-                return result;
-            }
-
-            case Settings.CALL_METHOD_DELETE_SECURE: {
-                int rows = deleteSecureSetting(name, requestingUserId, false) ? 1 : 0;
+            case Settings.CALL_METHOD_DELETE_CONFIG: {
+                int rows  = deleteConfigSetting(name, requestingUserId, false) ? 1 : 0;
                 Bundle result = new Bundle();
                 result.putInt(RESULT_ROWS_DELETED, rows);
                 return result;
@@ -484,10 +479,32 @@
                 return result;
             }
 
-            case Settings.CALL_METHOD_LIST_SYSTEM: {
+            case Settings.CALL_METHOD_DELETE_SECURE: {
+                int rows = deleteSecureSetting(name, requestingUserId, false) ? 1 : 0;
+                Bundle result = new Bundle();
+                result.putInt(RESULT_ROWS_DELETED, rows);
+                return result;
+            }
+
+            case Settings.CALL_METHOD_DELETE_SYSTEM: {
+                int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0;
+                Bundle result = new Bundle();
+                result.putInt(RESULT_ROWS_DELETED, rows);
+                return result;
+            }
+
+            case Settings.CALL_METHOD_LIST_CONFIG: {
+                String prefix = getSettingPrefix(args);
+                Bundle result = new Bundle();
+                result.putSerializable(
+                        Settings.NameValueTable.VALUE, (HashMap) getAllConfigFlags(prefix));
+                return result;
+            }
+
+            case Settings.CALL_METHOD_LIST_GLOBAL: {
                 Bundle result = new Bundle();
                 result.putStringArrayList(RESULT_SETTINGS_LIST,
-                        buildSettingsList(getAllSystemSettings(requestingUserId, null)));
+                        buildSettingsList(getAllGlobalSettings(null)));
                 return result;
             }
 
@@ -498,10 +515,10 @@
                 return result;
             }
 
-            case Settings.CALL_METHOD_LIST_GLOBAL: {
+            case Settings.CALL_METHOD_LIST_SYSTEM: {
                 Bundle result = new Bundle();
                 result.putStringArrayList(RESULT_SETTINGS_LIST,
-                        buildSettingsList(getAllGlobalSettings(null)));
+                        buildSettingsList(getAllSystemSettings(requestingUserId, null)));
                 return result;
             }
 
@@ -1061,36 +1078,47 @@
                 MUTATION_OPERATION_INSERT, forceNotify, 0);
     }
 
-    private void resetConfigSetting(int requestingUserId, int mode, String tag) {
+    private boolean deleteConfigSetting(String name, int requestingUserId, boolean forceNotify) {
+        if (DEBUG) {
+            Slog.v(LOG_TAG, "deleteConfigSetting(" + name + ", " + requestingUserId
+                    + ", " + forceNotify + ")");
+        }
+        return mutateConfigSetting(name, null, null, false, requestingUserId,
+                MUTATION_OPERATION_DELETE, forceNotify, 0);
+    }
+
+    private void resetConfigSetting(int requestingUserId, int mode, String prefix) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "resetConfigSetting(" + requestingUserId + ", "
-                    + mode + ", " + tag + ")");
+                    + mode + ", " + prefix + ")");
         }
-        mutateConfigSetting(null, null, tag, false, requestingUserId,
+        mutateConfigSetting(null, null, prefix, false, requestingUserId,
                 MUTATION_OPERATION_RESET, false, mode);
     }
 
-    private boolean mutateConfigSetting(String name, String value, String tag,
+    private boolean mutateConfigSetting(String name, String value, String prefix,
             boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
             int mode) {
         // TODO(b/117663715): check the new permission when it's added.
         // enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
 
-        // Resolve the userId on whose behalf the call is made.
-        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
-
         // Perform the mutation.
         synchronized (mLock) {
             switch (operation) {
                 case MUTATION_OPERATION_INSERT: {
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
-                            UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
+                            UserHandle.USER_SYSTEM, name, value, null, makeDefault,
                             getCallingPackage(), forceNotify, null);
                 }
 
+                case MUTATION_OPERATION_DELETE: {
+                    return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_CONFIG,
+                            UserHandle.USER_SYSTEM, name, forceNotify, null);
+                }
+
                 case MUTATION_OPERATION_RESET: {
                     mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
-                            UserHandle.USER_SYSTEM, getCallingPackage(), mode, tag);
+                            UserHandle.USER_SYSTEM, getCallingPackage(), mode, null, prefix);
                 } return true;
             }
         }
@@ -1098,6 +1126,34 @@
         return false;
     }
 
+    private Map<String, String> getAllConfigFlags(@Nullable String prefix) {
+        if (DEBUG) {
+            Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix);
+        }
+
+        synchronized (mLock) {
+            // Get the settings.
+            SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
+                    SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
+
+            List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_CONFIG,
+                    UserHandle.USER_SYSTEM);
+
+            final int nameCount = names.size();
+            Map<String, String> flagsToValues = new HashMap<>(names.size());
+
+            for (int i = 0; i < nameCount; i++) {
+                String name = names.get(i);
+                Setting setting = settingsState.getSettingLocked(name);
+                if (prefix == null || setting.getName().startsWith(prefix)) {
+                    flagsToValues.put(setting.getName(), setting.getValue());
+                }
+            }
+
+            return flagsToValues;
+        }
+    }
+
     private Cursor getAllGlobalSettings(String[] projection) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "getAllGlobalSettings()");
@@ -2085,6 +2141,13 @@
         return (args != null) ? args.getString(Settings.CALL_METHOD_TAG_KEY) : null;
     }
 
+    private static String getSettingPrefix(Bundle args) {
+        String prefix = (args != null) ? args.getString(Settings.CALL_METHOD_PREFIX_KEY) : null;
+        // Append '/' to ensure we only match properties with this exact prefix.
+        // i.e. "foo" should match "foo/property" but not "foobar/property"
+        return prefix != null ? prefix + "/" : null;
+    }
+
     private static boolean getSettingMakeDefault(Bundle args) {
         return (args != null) && args.getBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY);
     }
@@ -2644,6 +2707,11 @@
 
         public void resetSettingsLocked(int type, int userId, String packageName, int mode,
                 String tag) {
+            resetSettingsLocked(type, userId, packageName, mode, tag, null);
+        }
+
+        public void resetSettingsLocked(int type, int userId, String packageName, int mode,
+                String tag, @Nullable String prefix) {
             final int key = makeKey(type, userId);
             SettingsState settingsState = peekSettingsStateLocked(key);
             if (settingsState == null) {
@@ -2656,7 +2724,8 @@
                         boolean someSettingChanged = false;
                         Setting setting = settingsState.getSettingLocked(name);
                         if (packageName.equals(setting.getPackageName())) {
-                            if (tag != null && !tag.equals(setting.getTag())) {
+                            if ((tag != null && !tag.equals(setting.getTag()))
+                                    || (prefix != null && !setting.getName().startsWith(prefix))) {
                                 continue;
                             }
                             if (settingsState.resetSettingLocked(name)) {
@@ -2676,6 +2745,9 @@
                         Setting setting = settingsState.getSettingLocked(name);
                         if (!SettingsState.isSystemPackage(getContext(),
                                 setting.getPackageName())) {
+                            if (prefix != null && !setting.getName().startsWith(prefix)) {
+                                continue;
+                            }
                             if (settingsState.resetSettingLocked(name)) {
                                 someSettingChanged = true;
                                 notifyForSettingsChange(key, name);
@@ -2693,6 +2765,9 @@
                         Setting setting = settingsState.getSettingLocked(name);
                         if (!SettingsState.isSystemPackage(getContext(),
                                 setting.getPackageName())) {
+                            if (prefix != null && !setting.getName().startsWith(prefix)) {
+                                continue;
+                            }
                             if (setting.isDefaultFromSystem()) {
                                 if (settingsState.resetSettingLocked(name)) {
                                     someSettingChanged = true;
@@ -2713,6 +2788,9 @@
                     for (String name : settingsState.getSettingNamesLocked()) {
                         Setting setting = settingsState.getSettingLocked(name);
                         boolean someSettingChanged = false;
+                        if (prefix != null && !setting.getName().startsWith(prefix)) {
+                            continue;
+                        }
                         if (setting.isDefaultFromSystem()) {
                             if (settingsState.resetSettingLocked(name)) {
                                 someSettingChanged = true;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 13537c4..36360a3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -105,7 +105,7 @@
             RESET,
         }
 
-        int mUser = -1;     // unspecified
+        int mUser = UserHandle.USER_NULL;
         CommandVerb mVerb = CommandVerb.UNSPECIFIED;
         String mTable = null;
         String mKey = null;
@@ -132,15 +132,15 @@
             String arg = cmd;
             do {
                 if ("--user".equals(arg)) {
-                    if (mUser != -1) {
-                        // --user specified more than once; invalid
+                    if (mUser != UserHandle.USER_NULL) {
+                        perr.println("Invalid user: --user specified more than once");
                         break;
                     }
-                    arg = getNextArgRequired();
-                    if ("current".equals(arg) || "cur".equals(arg)) {
-                        mUser = UserHandle.USER_CURRENT;
-                    } else {
-                        mUser = Integer.parseInt(arg);
+                    mUser = UserHandle.parseUserArg(getNextArgRequired());
+
+                    if (mUser == UserHandle.USER_ALL) {
+                        perr.println("Invalid user: all");
+                        return -1;
                     }
                 } else if (mVerb == CommandVerb.UNSPECIFIED) {
                     if ("get".equalsIgnoreCase(arg)) {
@@ -254,16 +254,13 @@
                 return -1;
             }
 
-            if (mUser == UserHandle.USER_CURRENT) {
+            if (mUser == UserHandle.USER_NULL || mUser == UserHandle.USER_CURRENT) {
                 try {
                     mUser = ActivityManager.getService().getCurrentUser().id;
                 } catch (RemoteException e) {
                     throw new RuntimeException("Failed in IPC", e);
                 }
             }
-            if (mUser < 0) {
-                mUser = UserHandle.USER_SYSTEM;
-            }
             UserManager userManager = UserManager.get(mProvider.getContext());
             if (userManager.getUserInfo(mUser) == null) {
                 perr.println("Invalid user: " + mUser);
@@ -312,8 +309,8 @@
             try {
                 Bundle arg = new Bundle();
                 arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
-                Bundle result =
-                        provider.call(resolveCallingPackage(), callListCommand, null, arg);
+                Bundle result = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+                        callListCommand, null, arg);
                 lines.addAll(result.getStringArrayList(SettingsProvider.RESULT_SETTINGS_LIST));
                 Collections.sort(lines);
             } catch (RemoteException e) {
@@ -337,7 +334,8 @@
             try {
                 Bundle arg = new Bundle();
                 arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
-                Bundle b = provider.call(resolveCallingPackage(), callGetCommand, key, arg);
+                Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+                        callGetCommand, key, arg);
                 if (b != null) {
                     result = b.getPairValue();
                 }
@@ -374,7 +372,8 @@
                 if (makeDefault) {
                     arg.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
                 }
-                provider.call(resolveCallingPackage(), callPutCommand, key, arg);
+                provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+                        callPutCommand, key, arg);
             } catch (RemoteException e) {
                 throw new RuntimeException("Failed in IPC", e);
             }
@@ -397,8 +396,8 @@
             try {
                 Bundle arg = new Bundle();
                 arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
-                Bundle result =
-                        provider.call(resolveCallingPackage(), callDeleteCommand, key, arg);
+                Bundle result = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
+                        callDeleteCommand, key, arg);
                 return result.getInt(SettingsProvider.RESULT_ROWS_DELETED);
             } catch (RemoteException e) {
                 throw new RuntimeException("Failed in IPC", e);
@@ -424,7 +423,7 @@
                 }
                 String packageName = mPackageName != null ? mPackageName : resolveCallingPackage();
                 arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
-                provider.call(packageName, callResetCommand, null, arg);
+                provider.call(packageName, Settings.AUTHORITY, callResetCommand, null, arg);
             } catch (RemoteException e) {
                 throw new RuntimeException("Failed in IPC", e);
             }
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
new file mode 100644
index 0000000..59de6a7e
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/DeviceConfigServiceTest.java
@@ -0,0 +1,236 @@
+/*
+ * 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.providers.settings;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import libcore.io.Streams;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Tests for {@link DeviceConfigService}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class DeviceConfigServiceTest {
+    /**
+     * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
+     *     API.
+     */
+    private static final Uri CONFIG_CONTENT_URI =
+            Uri.parse("content://" + Settings.AUTHORITY + "/config");
+    private static final String sNamespace = "namespace1";
+    private static final String sKey = "key1";
+    private static final String sValue = "value1";
+
+    private ContentResolver mContentResolver;
+
+    @Before
+    public void setUp() {
+        mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
+    }
+
+    @After
+    public void cleanUp() {
+        deleteFromContentProvider(mContentResolver, sNamespace, sKey);
+    }
+
+    @Test
+    public void testPut() throws Exception {
+        final String newNamespace = "namespace2";
+        final String newValue = "value2";
+
+        String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        assertNull(result);
+
+        try {
+            executeShellCommand("device_config put " + sNamespace + " " + sKey + " " + sValue);
+            executeShellCommand("device_config put " + newNamespace + " " + sKey + " " + newValue);
+
+            result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+            assertEquals(sValue, result);
+            result = getFromContentProvider(mContentResolver, newNamespace, sKey);
+            assertEquals(newValue, result);
+        } finally {
+            deleteFromContentProvider(mContentResolver, newNamespace, sKey);
+        }
+    }
+
+    @Test
+    public void testPut_invalidArgs() throws Exception {
+        // missing sNamespace
+        executeShellCommand("device_config put " + sKey + " " + sValue);
+        String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        // still null
+        assertNull(result);
+
+        // too many arguments
+        executeShellCommand(
+                "device_config put " + sNamespace + " " + sKey + " " + sValue + " extra_arg");
+        result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        // still null
+        assertNull(result);
+    }
+
+    @Test
+    public void testDelete() throws Exception {
+        final String newNamespace = "namespace2";
+
+        putWithContentProvider(mContentResolver, sNamespace, sKey, sValue);
+        putWithContentProvider(mContentResolver, newNamespace, sKey, sValue);
+        String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        assertEquals(sValue, result);
+        result = getFromContentProvider(mContentResolver, newNamespace, sKey);
+        assertEquals(sValue, result);
+
+        try {
+            executeShellCommand("device_config delete " + sNamespace + " " + sKey);
+            // sKey is deleted from sNamespace
+            result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+            assertNull(result);
+            // sKey is not deleted from newNamespace
+            result = getFromContentProvider(mContentResolver, newNamespace, sKey);
+            assertEquals(sValue, result);
+        } finally {
+            deleteFromContentProvider(mContentResolver, newNamespace, sKey);
+        }
+    }
+
+    @Test
+    public void testDelete_invalidArgs() throws Exception {
+        putWithContentProvider(mContentResolver, sNamespace, sKey, sValue);
+        String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        assertEquals(sValue, result);
+
+        // missing sNamespace
+        executeShellCommand("device_config delete " + sKey);
+        result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        // sValue was not deleted
+        assertEquals(sValue, result);
+
+        // too many arguments
+        executeShellCommand("device_config delete " + sNamespace + " " + sKey + " extra_arg");
+        result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        // sValue was not deleted
+        assertEquals(sValue, result);
+    }
+
+    @Test
+    public void testReset_setUntrustedDefault() throws Exception {
+        String newValue = "value2";
+
+        // make sValue the untrusted default (set by root)
+        executeShellCommand(
+                "device_config put " + sNamespace + " " + sKey + " " + sValue + " default");
+        // make newValue the current value
+        executeShellCommand(
+                "device_config put " + sNamespace + " " + sKey + " " + newValue);
+        String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        assertEquals(newValue, result);
+
+        executeShellCommand("device_config reset untrusted_defaults " + sNamespace);
+        result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        // back to the default
+        assertEquals(sValue, result);
+
+        executeShellCommand("device_config reset trusted_defaults " + sNamespace);
+        result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        // not trusted default was set
+        assertNull(result);
+    }
+
+    @Test
+    public void testReset_setTrustedDefault() throws Exception {
+        String newValue = "value2";
+
+        // make sValue the trusted default (set by system)
+        putWithContentProvider(mContentResolver, sNamespace, sKey, sValue, true);
+        // make newValue the current value
+        executeShellCommand(
+                "device_config put " + sNamespace + " " + sKey + " " + newValue);
+        String result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        assertEquals(newValue, result);
+
+        executeShellCommand("device_config reset untrusted_defaults " + sNamespace);
+        result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        // back to the default
+        assertEquals(sValue, result);
+
+        executeShellCommand("device_config reset trusted_defaults " + sNamespace);
+        result = getFromContentProvider(mContentResolver, sNamespace, sKey);
+        // our trusted default is still set
+        assertEquals(sValue, result);
+    }
+
+    private static void executeShellCommand(String command) throws IOException {
+        InputStream is = new FileInputStream(InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation().executeShellCommand(command).getFileDescriptor());
+        Streams.readFully(is);
+    }
+
+    private static void putWithContentProvider(ContentResolver resolver, String namespace,
+            String key, String value) {
+        putWithContentProvider(resolver, namespace, key, value, false);
+    }
+
+    private static void putWithContentProvider(ContentResolver resolver, String namespace,
+            String key, String value, boolean makeDefault) {
+        String compositeName = namespace + "/" + key;
+        Bundle args = new Bundle();
+        args.putString(Settings.NameValueTable.VALUE, value);
+        if (makeDefault) {
+            args.putBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY, true);
+        }
+        resolver.call(
+                CONFIG_CONTENT_URI, Settings.CALL_METHOD_PUT_CONFIG, compositeName, args);
+    }
+
+    private static String getFromContentProvider(ContentResolver resolver, String namespace,
+            String key) {
+        String compositeName = namespace + "/" + key;
+        Bundle result = resolver.call(
+                CONFIG_CONTENT_URI, Settings.CALL_METHOD_GET_CONFIG, compositeName, null);
+        assertNotNull(result);
+        return result.getString(Settings.NameValueTable.VALUE);
+    }
+
+    private static boolean deleteFromContentProvider(ContentResolver resolver, String namespace,
+            String key) {
+        String compositeName = namespace + "/" + key;
+        Bundle result = resolver.call(
+                CONFIG_CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, compositeName, null);
+        assertNotNull(result);
+        return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
+    }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e564711..5fe08aa 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -51,6 +51,7 @@
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
     <uses-permission android:name="android.permission.REORDER_TASKS" />
+    <uses-permission android:name="android.permission.REMOVE_TASKS" />
     <uses-permission android:name="android.permission.SET_ANIMATION_SCALE" />
     <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 7cb63ea..1a18f60 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -18,6 +18,8 @@
 
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
+import java.util.TimeZone;
+
 /**
  * This plugin is used to replace main clock in keyguard.
  */
@@ -55,4 +57,9 @@
      * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
      */
     default void setDarkAmount(float darkAmount) {}
+
+    /**
+     * Notifies that the time zone has changed.
+     */
+    default void onTimeZoneChanged(TimeZone timeZone) {}
 }
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8e0bfb6..b6c9b8c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -23,6 +23,8 @@
     <dimen name="navigation_bar_size">@*android:dimen/navigation_bar_height</dimen>
     <!-- Minimum swipe distance to catch the swipe gestures to invoke assist or switch tasks. -->
     <dimen name="navigation_bar_min_swipe_distance">48dp</dimen>
+    <!-- The distance from a side of device of the navigation bar to start an edge swipe -->
+    <dimen name="navigation_bar_edge_swipe_threshold">60dp</dimen>
 
     <!-- thickness (height) of the dead zone at the top of the navigation bar,
          reducing false presses on navbar buttons; approx 2mm -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index b439c6c..0ec9014 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -17,6 +17,7 @@
 import com.android.systemui.shared.plugins.PluginManager;
 
 import java.util.Objects;
+import java.util.TimeZone;
 
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
@@ -162,6 +163,15 @@
     }
 
     /**
+     * Notifies that the time zone has changed.
+     */
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        if (mClockPlugin != null) {
+            mClockPlugin.onTimeZoneChanged(timeZone);
+        }
+    }
+
+    /**
      * When plugin changes, set all kept parameters into newer plugin.
      */
     private void initPluginParams() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 6b24ad5..41afa9a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -193,7 +193,8 @@
             public void onClick(View v) {
                 mCallback.userActivity(); // Leave the screen on a bit longer
                 // Do not show auxiliary subtypes in password lock screen.
-                mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */);
+                mImm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */,
+                        getContext().getDisplayId());
             }
         });
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index be795d2..1e9d288 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -51,6 +51,7 @@
 import com.google.android.collect.Sets;
 
 import java.util.Locale;
+import java.util.TimeZone;
 
 public class KeyguardStatusView extends GridLayout implements
         ConfigurationController.ConfigurationListener, View.OnLayoutChangeListener {
@@ -85,6 +86,11 @@
         }
 
         @Override
+        public void onTimeZoneChanged(TimeZone timeZone) {
+            updateTimeZone(timeZone);
+        }
+
+        @Override
         public void onKeyguardVisibilityChanged(boolean showing) {
             if (showing) {
                 if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
@@ -282,6 +288,10 @@
         mClockView.refresh();
     }
 
+    private void updateTimeZone(TimeZone timeZone) {
+        mClockView.onTimeZoneChanged(timeZone);
+    }
+
     private void refreshFormat() {
         Patterns.update(mContext);
         mClockView.setFormat12Hour(Patterns.clockView12);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 904f944..416441e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -98,6 +98,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map.Entry;
+import java.util.TimeZone;
 
 /**
  * Watches for updates that may be interesting to the keyguard, and provides
@@ -151,6 +152,7 @@
     private static final int MSG_BIOMETRIC_AUTHENTICATION_CONTINUE = 336;
     private static final int MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED = 337;
     private static final int MSG_TELEPHONY_CAPABLE = 338;
+    private static final int MSG_TIMEZONE_UPDATE = 339;
 
     /** Biometric authentication state: Not listening. */
     private static final int BIOMETRIC_STATE_STOPPED = 0;
@@ -260,6 +262,9 @@
                 case MSG_TIME_UPDATE:
                     handleTimeUpdate();
                     break;
+                case MSG_TIMEZONE_UPDATE:
+                    handleTimeZoneUpdate((String) msg.obj);
+                    break;
                 case MSG_BATTERY_UPDATE:
                     handleBatteryUpdate((BatteryStatus) msg.obj);
                     break;
@@ -964,9 +969,12 @@
             if (DEBUG) Log.d(TAG, "received broadcast " + action);
 
             if (Intent.ACTION_TIME_TICK.equals(action)
-                    || Intent.ACTION_TIME_CHANGED.equals(action)
-                    || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
+                    || Intent.ACTION_TIME_CHANGED.equals(action)) {
                 mHandler.sendEmptyMessage(MSG_TIME_UPDATE);
+            } else if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
+                final Message msg = mHandler.obtainMessage(
+                        MSG_TIMEZONE_UPDATE, intent.getStringExtra("time-zone"));
+                mHandler.sendMessage(msg);
             } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
                 final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
                 final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
@@ -1860,6 +1868,21 @@
     }
 
     /**
+     * Handle (@line #MSG_TIMEZONE_UPDATE}
+     */
+    private void handleTimeZoneUpdate(String timeZone) {
+        if (DEBUG) Log.d(TAG, "handleTimeZoneUpdate");
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onTimeZoneChanged(TimeZone.getTimeZone(timeZone));
+                // Also notify callbacks about time change to remain compatible.
+                cb.onTimeChanged();
+            }
+        }
+    }
+
+    /**
      * Handle {@link #MSG_BATTERY_UPDATE}
      */
     private void handleBatteryUpdate(BatteryStatus status) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index f818d05..8696bb7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -26,6 +26,8 @@
 import com.android.internal.telephony.IccCardConstants;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 
+import java.util.TimeZone;
+
 /**
  * Callback for general information relevant to lock screen.
  */
@@ -49,6 +51,13 @@
     public void onTimeChanged() { }
 
     /**
+     * Called when time zone changes.
+     *
+     * @note When time zone changes, onTimeChanged will be called too.
+     */
+    public void onTimeZoneChanged(TimeZone timeZone) { }
+
+    /**
      * Called when the carrier PLMN or SPN changes.
      */
     public void onRefreshCarrierInfo() { }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 8495fd3..86ce60d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -224,6 +224,9 @@
                         // next tap
                         mTouchState.scheduleDoubleTapTimeoutCallback();
                     }
+                    // Fall through
+                case MotionEvent.ACTION_CANCEL:
+                    mTouchState.reset();
                     break;
             }
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 451297b..cc27135 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -17,9 +17,10 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_CLICK;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_LONG_PRESS;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CONTEXT;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_IS_FULL_QS;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
@@ -52,6 +53,7 @@
 import com.android.systemui.qs.PagedTileLayout.TilePage;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QuickStatusBarHeader;
+import com.android.systemui.statusbar.StatusBarStateController;
 
 import java.util.ArrayList;
 
@@ -61,6 +63,8 @@
  * State management done on a looper provided by the host.  Tiles should update state in
  * handleUpdateState.  Callbacks affecting state should use refreshState to trigger another
  * state update pass on tile looper.
+ *
+ * @param <TState> see above
  */
 public abstract class QSTileImpl<TState extends State> implements QSTile {
     protected final String TAG = "Tile." + getClass().getSimpleName();
@@ -76,6 +80,8 @@
     protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
     private final ArraySet<Object> mListeners = new ArraySet<>();
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+    private final StatusBarStateController
+            mStatusBarStateController = Dependency.get(StatusBarStateController.class);
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
     private final Object mStaleListener = new Object();
@@ -172,17 +178,23 @@
     }
 
     public void click() {
-        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)));
+        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)
+                .addTaggedData(FIELD_STATUS_BAR_STATE,
+                        mStatusBarStateController.getState())));
         mHandler.sendEmptyMessage(H.CLICK);
     }
 
     public void secondaryClick() {
-        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_SECONDARY_CLICK).setType(TYPE_ACTION)));
+        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_SECONDARY_CLICK).setType(TYPE_ACTION)
+                .addTaggedData(FIELD_STATUS_BAR_STATE,
+                        mStatusBarStateController.getState())));
         mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
     }
 
     public void longClick() {
-        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)));
+        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)
+                .addTaggedData(FIELD_STATUS_BAR_STATE,
+                        mStatusBarStateController.getState())));
         mHandler.sendEmptyMessage(H.LONG_CLICK);
 
         Prefs.putInt(
@@ -196,7 +208,7 @@
             logMaker.addTaggedData(FIELD_QS_VALUE, ((BooleanState) mState).value ? 1 : 0);
         }
         return logMaker.setSubtype(getMetricsCategory())
-                .addTaggedData(FIELD_CONTEXT, mIsFullQs)
+                .addTaggedData(FIELD_IS_FULL_QS, mIsFullQs)
                 .addTaggedData(FIELD_QS_POSITION, mHost.indexOf(mTileSpec));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 3334f8b..a5c0a2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -67,7 +67,7 @@
     /**
      * True if the presenter is currently locked.
      */
-    default boolean isPresenterLocked() { return false; }
+    boolean isPresenterLocked();
 
     /**
      * Called when the row states are updated by {@link NotificationViewHierarchyManager}.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index 3f8583c..d9fe982 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -815,9 +815,18 @@
     public boolean isHighPriority(StatusBarNotification statusBarNotification) {
         if (mRankingMap != null) {
             getRanking(statusBarNotification.getKey(), mTmpRanking);
-            return mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
+            if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
                     || statusBarNotification.getNotification().isForegroundService()
-                    || statusBarNotification.getNotification().hasMediaSession();
+                    || statusBarNotification.getNotification().hasMediaSession()) {
+                return true;
+            }
+            if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
+                for (Entry child : mGroupManager.getLogicalChildren(statusBarNotification)) {
+                    if (isHighPriority(child.notification)) {
+                        return true;
+                    }
+                }
+            }
         }
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 50564e3..d97162c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -30,6 +30,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -47,6 +48,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener,
         ExpandableNotificationRow.LayoutListener {
@@ -74,6 +76,7 @@
     private MenuItem mSnoozeItem;
     private ArrayList<MenuItem> mLeftMenuItems;
     private ArrayList<MenuItem> mRightMenuItems;
+    private final Map<View, MenuItem> mMenuItemsByView = new ArrayMap<>();
     private OnMenuEventListener mMenuListener;
 
     private ValueAnimator mFadeAnimator;
@@ -287,6 +290,7 @@
     private void populateMenuViews() {
         if (mMenuContainer != null) {
             mMenuContainer.removeAllViews();
+            mMenuItemsByView.clear();
         } else {
             mMenuContainer = new FrameLayout(mContext);
         }
@@ -486,10 +490,8 @@
         final int centerY = v.getHeight() / 2;
         final int x = mIconLocation[0] - mParentLocation[0] + centerX;
         final int y = mIconLocation[1] - mParentLocation[1] + centerY;
-        final int index = mMenuContainer.indexOfChild(v);
-        if (mMenuListener != null) {
-            mMenuListener.onMenuClicked(mParent, x, y,
-                    (mOnLeft ? mLeftMenuItems : mRightMenuItems).get(index));
+        if (mMenuItemsByView.containsKey(v)) {
+            mMenuListener.onMenuClicked(mParent, x, y, mMenuItemsByView.get(v));
         }
     }
 
@@ -641,8 +643,8 @@
         NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
                 R.layout.notification_info, null, false);
         int iconResId = isCurrentlySilent
-                ? R.drawable.ic_notifications_alert
-                : R.drawable.ic_notifications_silence;
+                ? R.drawable.ic_notifications_silence
+                : R.drawable.ic_notifications_alert;
         return new NotificationMenuItem(context, infoDescription, infoContent, iconResId);
     }
 
@@ -665,6 +667,7 @@
             lp.height = mHorizSpaceForIcon;
             menuView.setLayoutParams(lp);
         }
+        mMenuItemsByView.put(menuView, item);
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
index b83ebc7..9c8b1b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
@@ -56,6 +56,11 @@
     }
 
     @Override
+    public boolean allowHitTargetToMoveOverDrag() {
+        return true;
+    }
+
+    @Override
     public boolean canPerformAction() {
         return mProxySender.getBackButtonAlpha() > 0;
     }
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 cd6e1d7..30e8409 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -63,6 +63,7 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.FrameLayout;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dependency;
 import com.android.systemui.DockedStackExistsListener;
 import com.android.systemui.Interpolators;
@@ -96,7 +97,6 @@
 
     final static boolean ALTERNATE_CAR_MODE_UI = false;
 
-    final Display mDisplay;
     View mCurrentView = null;
     View[] mRotatedViews = new View[4];
 
@@ -154,6 +154,7 @@
     private QuickScrubAction mQuickScrubAction;
     private QuickStepAction mQuickStepAction;
     private NavigationBackAction mBackAction;
+    private QuickSwitchAction mQuickSwitchAction;
 
     /**
      * Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -212,8 +213,8 @@
     private final OnClickListener mImeSwitcherClickListener = new OnClickListener() {
         @Override
         public void onClick(View view) {
-            mContext.getSystemService(InputMethodManager.class)
-                    .showInputMethodPicker(true /* showAuxiliarySubtypes */);
+            mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
+                    true /* showAuxiliarySubtypes */, getContext().getDisplayId());
         }
     };
 
@@ -281,8 +282,6 @@
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mDisplay = context.getDisplay();
-
         mVertical = false;
         mLongClickableAccessibilityButton = false;
 
@@ -326,9 +325,10 @@
         mQuickScrubAction = new QuickScrubAction(this, mOverviewProxyService);
         mQuickStepAction = new QuickStepAction(this, mOverviewProxyService);
         mBackAction = new NavigationBackAction(this, mOverviewProxyService);
+        mQuickSwitchAction = new QuickSwitchAction(this, mOverviewProxyService);
         mDefaultGestureMap = new NavigationGestureAction[] {
                 mQuickStepAction, null /* swipeDownAction*/, null /* swipeLeftAction */,
-                mQuickScrubAction
+                mQuickScrubAction, null /* swipeLeftEdgeAction */, null /* swipeRightEdgeAction */
         };
 
         mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
@@ -359,7 +359,9 @@
                     getNavigationActionFromType(assignedMap[0], mDefaultGestureMap[0]),
                     getNavigationActionFromType(assignedMap[1], mDefaultGestureMap[1]),
                     getNavigationActionFromType(assignedMap[2], mDefaultGestureMap[2]),
-                    getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3]));
+                    getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3]),
+                    getNavigationActionFromType(assignedMap[4], mDefaultGestureMap[4]),
+                    getNavigationActionFromType(assignedMap[5], mDefaultGestureMap[5]));
         }
     }
 
@@ -372,6 +374,8 @@
                 return mQuickScrubAction;
             case NavigationPrototypeController.ACTION_BACK:
                 return mBackAction;
+            case NavigationPrototypeController.ACTION_QUICKSWITCH:
+                return mQuickSwitchAction;
             default:
                 return defaultAction;
         }
@@ -652,8 +656,8 @@
         Log.i(TAG, "updateNavButtonIcons (b/113914868): home disabled=" + disableHome
                 + " mDisabledFlags=" + mDisabledFlags);
 
-        // Always disable recents when alternate car mode UI is active.
-        boolean disableRecent = mUseCarModeUi || !isOverviewEnabled();
+        // Always disable recents when alternate car mode UI is active and for secondary displays.
+        boolean disableRecent = isRecentsButtonDisabled();
 
         boolean disableBack = QuickStepController.shouldhideBackButton(getContext())
                 || (((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) && !useAltBack);
@@ -689,6 +693,16 @@
         getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
     }
 
+    @VisibleForTesting
+    boolean isRecentsButtonDisabled() {
+        return mUseCarModeUi || !isOverviewEnabled()
+                || getContext().getDisplayId() != Display.DEFAULT_DISPLAY;
+    }
+
+    private Display getContextDisplay() {
+        return getContext().getDisplay();
+    }
+
     public boolean inScreenPinning() {
         return ActivityManagerWrapper.getInstance().isScreenPinningActive();
     }
@@ -890,7 +904,7 @@
     }
 
     private void updateCurrentView() {
-        final int rot = mDisplay.getRotation();
+        final int rot = getContextDisplay().getRotation();
         for (int i=0; i<4; i++) {
             mRotatedViews[i].setVisibility(View.GONE);
         }
@@ -954,7 +968,7 @@
         int navBarPos = NAV_BAR_INVALID;
         try {
             navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition(
-                    mDisplay.getDisplayId());
+                    getContext().getDisplayId());
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to get nav bar position.", e);
         }
@@ -1128,7 +1142,7 @@
         pw.println("NavigationBarView {");
         final Rect r = new Rect();
         final Point size = new Point();
-        mDisplay.getRealSize(size);
+        getContextDisplay().getRealSize(size);
 
         pw.println(String.format("      this: " + StatusBar.viewInfo(this)
                         + " " + visibilityToString(getVisibility())));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
index a8d00c4..8c57fc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
@@ -112,7 +112,7 @@
     /**
      * @return whether or not to move the button that started gesture over with user input drag
      */
-    public boolean requiresDragWithHitTarget() {
+    public boolean allowHitTargetToMoveOverDrag() {
         return false;
     }
 
@@ -139,9 +139,9 @@
      * Tell if action is enabled. Compared to {@link #canPerformAction()} this is based on settings
      * if the action is disabled for a particular gesture. For example a back action can be enabled
      * however if there is nothing to back to then {@link #canPerformAction()} should return false.
-     * In this way if the action requires {@link #requiresDragWithHitTarget()} then if enabled, the
-     * button can be dragged with a large dampening factor during the gesture but will not activate
-     * the action.
+     * In this way if the action requires {@link #allowHitTargetToMoveOverDrag()} then if enabled,
+     * the button can be dragged with a large dampening factor during the gesture but will not
+     * activate the action.
      * @return true if this action is enabled and can run
      */
     public abstract boolean isEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index e8c0bf1..b11b6d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -24,7 +24,6 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 
-import android.util.Log;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -46,6 +45,7 @@
     static final int ACTION_QUICKSTEP = 1;
     static final int ACTION_QUICKSCRUB = 2;
     static final int ACTION_BACK = 3;
+    static final int ACTION_QUICKSWITCH = 4;
 
     private OnPrototypeChangedListener mListener;
 
@@ -53,7 +53,7 @@
      * Each index corresponds to a different action set in QuickStepController
      * {@see updateSwipeLTRBackSetting}
      */
-    private int[] mActionMap = new int[4];
+    private int[] mActionMap = new int[6];
 
     private final Context mContext;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
index 2b202eb..bbfd51a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
@@ -18,8 +18,6 @@
 
 import static com.android.systemui.Interpolators.ALPHA_IN;
 import static com.android.systemui.Interpolators.ALPHA_OUT;
-import static com.android.systemui.recents.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
-import static com.android.systemui.recents.OverviewProxyService.TAG_OPS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -31,11 +29,8 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.RadialGradient;
-import android.graphics.Rect;
 import android.graphics.Shader;
-import android.os.RemoteException;
 import android.util.FloatProperty;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 
@@ -46,7 +41,7 @@
 /**
  * QuickScrub action to send to launcher to start quickscrub gesture
  */
-public class QuickScrubAction extends NavigationGestureAction {
+public class QuickScrubAction extends QuickSwitchAction {
     private static final String TAG = "QuickScrubAction";
 
     private static final float TRACK_SCALE = 0.95f;
@@ -65,7 +60,6 @@
     private final int mTrackThickness;
     private final int mTrackEndPadding;
     private final Paint mTrackPaint = new Paint();
-    private final Rect mTrackRect = new Rect();
 
     private final FloatProperty<QuickScrubAction> mTrackAlphaProperty =
             new FloatProperty<QuickScrubAction>("TrackAlpha") {
@@ -177,7 +171,7 @@
             x1 = mNavigationBarView.getPaddingStart() + mTrackEndPadding;
             x2 = x1 + width - 2 * mTrackEndPadding;
         }
-        mTrackRect.set(x1, y1, x2, y2);
+        mDragOverRect.set(x1, y1, x2, y2);
     }
 
     @Override
@@ -194,15 +188,16 @@
         mTrackPaint.setAlpha(Math.round(255f * mTrackAlpha));
 
         // Scale the track, but apply the inverse scale from the nav bar
-        final float radius = mTrackRect.height() / 2;
+        final float radius = mDragOverRect.height() / 2;
         canvas.save();
-        float translate = Utilities.clamp(mHighlightCenter, mTrackRect.left, mTrackRect.right);
+        float translate = Utilities.clamp(mHighlightCenter, mDragOverRect.left,
+                mDragOverRect.right);
         canvas.translate(translate, 0);
         canvas.scale(mTrackScale / mNavigationBarView.getScaleX(),
                 1f / mNavigationBarView.getScaleY(),
-                mTrackRect.centerX(), mTrackRect.centerY());
-        canvas.drawRoundRect(mTrackRect.left - translate, mTrackRect.top,
-                mTrackRect.right - translate, mTrackRect.bottom, radius, radius, mTrackPaint);
+                mDragOverRect.centerX(), mDragOverRect.centerY());
+        canvas.drawRoundRect(mDragOverRect.left - translate, mDragOverRect.top,
+                mDragOverRect.right - translate, mDragOverRect.bottom, radius, radius, mTrackPaint);
         canvas.restore();
     }
 
@@ -212,11 +207,6 @@
     }
 
     @Override
-    public boolean disableProxyEvents() {
-        return true;
-    }
-
-    @Override
     protected void onGestureStart(MotionEvent event) {
         updateHighlight();
         ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
@@ -231,42 +221,12 @@
         mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
         mTrackAnimator.start();
 
-        // Disable slippery for quick scrub to not cancel outside the nav bar
-        mNavigationBarView.updateSlippery();
-
-        try {
-            mProxySender.getProxy().onQuickScrubStart();
-            if (DEBUG_OVERVIEW_PROXY) {
-                Log.d(TAG_OPS, "Quick Scrub Start");
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to send start of quick scrub.", e);
-        }
-        mProxySender.notifyQuickScrubStarted();
+        startQuickGesture(event);
     }
 
     @Override
     public void onGestureMove(int x, int y) {
-        int trackSize, offset;
-        if (isNavBarVertical()) {
-            trackSize = mTrackRect.height();
-            offset = y - mTrackRect.top;
-        } else {
-            offset = x - mTrackRect.left;
-            trackSize = mTrackRect.width();
-        }
-        if (!mDragHorizontalPositive || !mDragVerticalPositive) {
-            offset -= isNavBarVertical() ? mTrackRect.height() : mTrackRect.width();
-        }
-        float scrubFraction = Utilities.clamp(Math.abs(offset) * 1f / trackSize, 0, 1);
-        try {
-            mProxySender.getProxy().onQuickScrubProgress(scrubFraction);
-            if (DEBUG_OVERVIEW_PROXY) {
-                Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to send progress of quick scrub.", e);
-        }
+        super.onGestureMove(x, y);
         mHighlightCenter = x;
         mNavigationBarView.invalidate();
     }
@@ -278,14 +238,7 @@
 
     private void endQuickScrub(boolean animate) {
         animateEnd();
-        try {
-            mProxySender.getProxy().onQuickScrubEnd();
-            if (DEBUG_OVERVIEW_PROXY) {
-                Log.d(TAG_OPS, "Quick Scrub End");
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to send end of quick scrub.", e);
-        }
+        endQuickGesture(animate);
         if (!animate) {
             if (mTrackAnimator != null) {
                 mTrackAnimator.end();
@@ -295,7 +248,7 @@
     }
 
     private void updateHighlight() {
-        if (mTrackRect.isEmpty()) {
+        if (mDragOverRect.isEmpty()) {
             return;
         }
         int colorBase, colorGrad;
@@ -306,8 +259,8 @@
             colorBase = getContext().getColor(R.color.quick_step_track_background_background_light);
             colorGrad = getContext().getColor(R.color.quick_step_track_background_foreground_light);
         }
-        final RadialGradient mHighlight = new RadialGradient(0, mTrackRect.height() / 2,
-                mTrackRect.width() * GRADIENT_WIDTH, colorGrad, colorBase,
+        final RadialGradient mHighlight = new RadialGradient(0, mDragOverRect.height() / 2,
+                mDragOverRect.width() * GRADIENT_WIDTH, colorGrad, colorBase,
                 Shader.TileMode.CLAMP);
         mTrackPaint.setShader(mHighlight);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 4983618..9eb5737a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -76,7 +76,9 @@
     private static final int ACTION_SWIPE_DOWN_INDEX = 1;
     private static final int ACTION_SWIPE_LEFT_INDEX = 2;
     private static final int ACTION_SWIPE_RIGHT_INDEX = 3;
-    private static final int MAX_GESTURES = 4;
+    private static final int ACTION_SWIPE_LEFT_FROM_EDGE_INDEX = 4;
+    private static final int ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX = 5;
+    private static final int MAX_GESTURES = 6;
 
     private NavigationBarView mNavigationBarView;
 
@@ -97,6 +99,7 @@
     private float mMaxDragLimit;
     private float mMinDragLimit;
     private float mDragDampeningFactor;
+    private float mEdgeSwipeThreshold;
 
     private NavigationGestureAction mCurrentAction;
     private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];
@@ -128,15 +131,21 @@
      * @param swipeDownAction action after swiping down
      * @param swipeLeftAction action after swiping left
      * @param swipeRightAction action after swiping right
+     * @param swipeLeftFromEdgeAction action swiping left starting from the right side
+     * @param swipeRightFromEdgeAction action swiping right starting from the left side
      */
     public void setGestureActions(@Nullable NavigationGestureAction swipeUpAction,
             @Nullable NavigationGestureAction swipeDownAction,
             @Nullable NavigationGestureAction swipeLeftAction,
-            @Nullable NavigationGestureAction swipeRightAction) {
+            @Nullable NavigationGestureAction swipeRightAction,
+            @Nullable NavigationGestureAction swipeLeftFromEdgeAction,
+            @Nullable NavigationGestureAction swipeRightFromEdgeAction) {
         mGestureActions[ACTION_SWIPE_UP_INDEX] = swipeUpAction;
         mGestureActions[ACTION_SWIPE_DOWN_INDEX] = swipeDownAction;
         mGestureActions[ACTION_SWIPE_LEFT_INDEX] = swipeLeftAction;
         mGestureActions[ACTION_SWIPE_RIGHT_INDEX] = swipeRightAction;
+        mGestureActions[ACTION_SWIPE_LEFT_FROM_EDGE_INDEX] = swipeLeftFromEdgeAction;
+        mGestureActions[ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX] = swipeRightFromEdgeAction;
 
         // Set the current state to all actions
         for (NavigationGestureAction action: mGestureActions) {
@@ -233,6 +242,8 @@
                 mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
                 mAllowGestureDetection = true;
                 mNotificationsVisibleOnDown = !mNavigationBarView.isNotificationsFullyCollapsed();
+                mEdgeSwipeThreshold = mContext.getResources()
+                        .getDimensionPixelSize(R.dimen.navigation_bar_edge_swipe_threshold);
                 break;
             }
             case MotionEvent.ACTION_MOVE: {
@@ -284,13 +295,17 @@
                         }
                     } else if (exceededSwipeHorizontalTouchSlop) {
                         if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) {
-                            // Swiping left (ltr) gesture
-                            tryToStartGesture(mGestureActions[ACTION_SWIPE_LEFT_INDEX],
-                                    true /* alignedWithNavBar */, event);
+                            // Swiping left (rtl) gesture
+                            int index = isEdgeSwipeAlongNavBar(touchDownH, !mDragHPositive)
+                                    ? ACTION_SWIPE_LEFT_FROM_EDGE_INDEX : ACTION_SWIPE_LEFT_INDEX;
+                            tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */,
+                                    event);
                         } else {
                             // Swiping right (ltr) gesture
-                            tryToStartGesture(mGestureActions[ACTION_SWIPE_RIGHT_INDEX],
-                                    true /* alignedWithNavBar */, event);
+                            int index = isEdgeSwipeAlongNavBar(touchDownH, mDragHPositive)
+                                    ? ACTION_SWIPE_RIGHT_FROM_EDGE_INDEX : ACTION_SWIPE_RIGHT_INDEX;
+                            tryToStartGesture(mGestureActions[index], true /* alignedWithNavBar */,
+                                    event);
                         }
                     }
                 }
@@ -333,6 +348,17 @@
         return mCurrentAction != null || deadZoneConsumed;
     }
 
+    private boolean isEdgeSwipeAlongNavBar(int touchDown, boolean dragPositiveDirection) {
+        // Detect edge swipe from side of 0 -> threshold
+        if (dragPositiveDirection) {
+            return touchDown < mEdgeSwipeThreshold;
+        }
+        // Detect edge swipe from side of size -> (size - threshold)
+        final int largeSide = isNavBarVertical()
+                ? mNavigationBarView.getHeight() : mNavigationBarView.getWidth();
+        return touchDown > largeSide - mEdgeSwipeThreshold;
+    }
+
     private void handleDragHitTarget(int position, int touchDown) {
         // Drag the hit target if gesture action requires it
         if (mHitTarget != null && (mGestureVerticalDragsButton || mGestureHorizontalDragsButton)) {
@@ -480,7 +506,7 @@
                 event.transform(mTransformLocalMatrix);
 
                 // Calculate the bounding limits of drag to avoid dragging off nav bar's window
-                if (action.requiresDragWithHitTarget() && mHitTarget != null) {
+                if (action.allowHitTargetToMoveOverDrag() && mHitTarget != null) {
                     final int[] buttonCenter = new int[2];
                     View button = mHitTarget.getCurrentView();
                     button.getLocationInWindow(buttonCenter);
@@ -505,7 +531,7 @@
 
             // Handle direction of the hit target drag from the axis that started the gesture
             // Also calculate the dampening factor, weaker dampening if there is an active action
-            if (action.requiresDragWithHitTarget()) {
+            if (action.allowHitTargetToMoveOverDrag()) {
                 if (alignedWithNavBar) {
                     mGestureHorizontalDragsButton = true;
                     mGestureVerticalDragsButton = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java
new file mode 100644
index 0000000..40f2392
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSwitchAction.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.recents.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
+import static com.android.systemui.recents.OverviewProxyService.TAG_OPS;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.recents.utilities.Utilities;
+
+/**
+ * QuickSwitch action to send to launcher
+ */
+public class QuickSwitchAction extends NavigationGestureAction {
+    private static final String TAG = "QuickSwitchAction";
+    private static final String QUICKSWITCH_ENABLED_SETTING = "QUICK_SWITCH";
+
+    protected final Rect mDragOverRect = new Rect();
+
+    public QuickSwitchAction(@NonNull NavigationBarView navigationBar,
+            @NonNull OverviewProxyService service) {
+        super(navigationBar, service);
+    }
+
+    @Override
+    public void setBarState(boolean changed, int navBarPos, boolean dragHorPositive,
+            boolean dragVerPositive) {
+        super.setBarState(changed, navBarPos, dragHorPositive, dragVerPositive);
+        if (changed && isActive()) {
+            // End quickscrub if the state changes mid-transition
+            endQuickGesture(false /* animate */);
+        }
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return mNavigationBarView.isQuickScrubEnabled();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        mDragOverRect.set(top, left, right, bottom);
+    }
+
+    @Override
+    public boolean disableProxyEvents() {
+        return true;
+    }
+
+    @Override
+    protected void onGestureStart(MotionEvent event) {
+        // Temporarily enable launcher to allow quick switch instead of quick scrub
+        Settings.Global.putInt(mNavigationBarView.getContext().getContentResolver(),
+                QUICKSWITCH_ENABLED_SETTING, 1 /* enabled */);
+
+        startQuickGesture(event);
+    }
+
+    @Override
+    public void onGestureMove(int x, int y) {
+        int dragLength, offset;
+        if (isNavBarVertical()) {
+            dragLength = mDragOverRect.height();
+            offset = y - mDragOverRect.top;
+        } else {
+            offset = x - mDragOverRect.left;
+            dragLength = mDragOverRect.width();
+        }
+        if (!mDragHorizontalPositive || !mDragVerticalPositive) {
+            offset -= dragLength;
+        }
+        float scrubFraction = Utilities.clamp(Math.abs(offset) * 1f / dragLength, 0, 1);
+        try {
+            mProxySender.getProxy().onQuickScrubProgress(scrubFraction);
+            if (DEBUG_OVERVIEW_PROXY) {
+                Log.d(TAG_OPS, "Quick Switch Progress:" + scrubFraction);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to send progress of quick switch.", e);
+        }
+    }
+
+    @Override
+    protected void onGestureEnd() {
+        endQuickGesture(true /* animate */);
+
+        // Disable launcher to use quick switch instead of quick scrub
+        Settings.Global.putInt(mNavigationBarView.getContext().getContentResolver(),
+                QUICKSWITCH_ENABLED_SETTING, 0 /* disabled */);
+    }
+
+    protected void startQuickGesture(MotionEvent event) {
+        // Disable slippery for quick scrub to not cancel outside the nav bar
+        mNavigationBarView.updateSlippery();
+
+        try {
+            mProxySender.getProxy().onQuickScrubStart();
+            if (DEBUG_OVERVIEW_PROXY) {
+                Log.d(TAG_OPS, "Quick Scrub Start");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to send start of quick scrub.", e);
+        }
+        mProxySender.notifyQuickScrubStarted();
+    }
+
+    protected void endQuickGesture(boolean animate) {
+        try {
+            mProxySender.getProxy().onQuickScrubEnd();
+            if (DEBUG_OVERVIEW_PROXY) {
+                Log.d(TAG_OPS, "Quick Scrub End");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to send end of quick scrub.", e);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index bf53b77..b351f1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1023,7 +1023,7 @@
 
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
                 mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
-                mScrimController, mActivityLaunchAnimator);
+                mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager);
 
         mAppOpsController.addCallback(APP_OPS, this);
         mNotificationListener.setUpWithPresenter(mPresenter);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 3550bd6..d99af1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -97,6 +97,7 @@
     private final AccessibilityManager mAccessibilityManager;
     private final KeyguardManager mKeyguardManager;
     private final ActivityLaunchAnimator mActivityLaunchAnimator;
+    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private final int mMaxAllowedKeyguardNotifications;
     private final IStatusBarService mBarService;
     private boolean mReinflateNotificationsOnUserSwitched;
@@ -113,13 +114,15 @@
             ViewGroup stackScroller,
             DozeScrimController dozeScrimController,
             ScrimController scrimController,
-            ActivityLaunchAnimator activityLaunchAnimator) {
+            ActivityLaunchAnimator activityLaunchAnimator,
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
         mContext = context;
         mNotificationPanel = panel;
         mHeadsUpManager = headsUp;
         mCommandQueue = getComponent(context, CommandQueue.class);
         mAboveShelfObserver = new AboveShelfObserver(stackScroller);
         mActivityLaunchAnimator = activityLaunchAnimator;
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mAboveShelfObserver.setListener(statusBarWindow.findViewById(
                 R.id.notification_container_parent));
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
@@ -367,6 +370,12 @@
         return mVrMode;
     }
 
+    @Override
+    public boolean isPresenterLocked() {
+        return mStatusBarKeyguardViewManager.isShowing()
+                && mStatusBarKeyguardViewManager.isSecure();
+    }
+
     private void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
         mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
         mActivityStarter.dismissKeyguardThenExecute(dismissAction, null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
index e811270..f792d7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -18,6 +18,7 @@
 import android.testing.LeakCheck;
 import android.testing.TestableContext;
 import android.util.ArrayMap;
+import android.view.Display;
 
 public class SysuiTestableContext extends TestableContext implements SysUiServiceProvider {
 
@@ -47,4 +48,15 @@
         if (mComponents == null) mComponents = new ArrayMap<>();
         mComponents.put(interfaceType, component);
     }
+
+    @Override
+    public Context createDisplayContext(Display display) {
+        if (display == null) {
+            throw new IllegalArgumentException("display must not be null");
+        }
+
+        SysuiTestableContext context =
+                new SysuiTestableContext(getBaseContext().createDisplayContext(display));
+        return context;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 6764634..e5464e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -19,8 +19,10 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -37,7 +39,6 @@
 import android.content.Intent;
 import android.metrics.LogMaker;
 import android.support.test.filters.SmallTest;
-import android.support.test.InstrumentationRegistry;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -48,12 +49,17 @@
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
 
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
+import org.mockito.Captor;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
@@ -65,13 +71,20 @@
     private TileImpl mTile;
     private QSTileHost mHost;
     private MetricsLogger mMetricsLogger;
+    private StatusBarStateController mStatusBarStateController;
+
+    @Captor
+    private ArgumentCaptor<LogMaker> mLogCaptor;
 
     @Before
     public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
         String spec = "spec";
         mTestableLooper = TestableLooper.get(this);
         mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
         mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
+        mStatusBarStateController =
+            mDependency.injectMockDependency(StatusBarStateController.class);
         mHost = mock(QSTileHost.class);
         when(mHost.indexOf(spec)).thenReturn(POSITION);
         when(mHost.getContext()).thenReturn(mContext.getBaseContext());
@@ -88,19 +101,57 @@
     }
 
     @Test
+    public void testClick_Metrics_Status_Bar_Status() {
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+        mTile.click();
+        verify(mMetricsLogger).write(mLogCaptor.capture());
+        assertEquals(StatusBarState.SHADE, mLogCaptor.getValue()
+                .getTaggedData(FIELD_STATUS_BAR_STATE));
+    }
+
+    @Test
     public void testSecondaryClick_Metrics() {
         mTile.secondaryClick();
         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
     }
 
     @Test
+    public void testSecondaryClick_Metrics_Status_Bar_Status() {
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+        mTile.secondaryClick();
+        verify(mMetricsLogger).write(mLogCaptor.capture());
+        assertEquals(StatusBarState.KEYGUARD, mLogCaptor.getValue()
+                .getTaggedData(FIELD_STATUS_BAR_STATE));
+    }
+
+    @Test
     public void testLongClick_Metrics() {
         mTile.longClick();
         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
     }
 
     @Test
-    public void testPopulate() {
+    public void testLongClick_Metrics_Status_Bar_Status() {
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+        mTile.click();
+        verify(mMetricsLogger).write(mLogCaptor.capture());
+        assertEquals(StatusBarState.SHADE_LOCKED, mLogCaptor.getValue()
+                .getTaggedData(FIELD_STATUS_BAR_STATE));
+    }
+
+    @Test
+    public void testPopulateWithLockedScreen() {
+        LogMaker maker = mock(LogMaker.class);
+        when(maker.setSubtype(anyInt())).thenReturn(maker);
+        when(maker.addTaggedData(anyInt(), any())).thenReturn(maker);
+        mTile.getState().value = true;
+        mTile.populate(maker);
+        verify(maker).addTaggedData(eq(FIELD_QS_VALUE), eq(1));
+        verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION));
+    }
+
+    @Test
+    public void testPopulateWithUnlockedScreen() {
         LogMaker maker = mock(LogMaker.class);
         when(maker.setSubtype(anyInt())).thenReturn(maker);
         when(maker.addTaggedData(anyInt(), any())).thenReturn(maker);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
new file mode 100644
index 0000000..73f3b43
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.statusbar.CommandQueue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** atest NavigationBarButtonTest */
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class NavigationBarButtonTest extends SysuiTestCase {
+
+    private ImageReader mReader;
+    private NavigationBarView mNavBar;
+    private VirtualDisplay mVirtualDisplay;
+
+    @Before
+    public void setup() {
+        final Display display = createVirtualDisplay();
+        final SysuiTestableContext context =
+                (SysuiTestableContext) mContext.createDisplayContext(display);
+        context.putComponent(CommandQueue.class, mock(CommandQueue.class));
+
+        mNavBar = new NavigationBarView(context, null);
+    }
+
+    private Display createVirtualDisplay() {
+        final String displayName = "NavVirtualDisplay";
+        final DisplayInfo displayInfo = new DisplayInfo();
+        mContext.getDisplay().getDisplayInfo(displayInfo);
+
+        final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+
+        mReader = ImageReader.newInstance(displayInfo.logicalWidth,
+                displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
+
+        assertNotNull("ImageReader must not be null", mReader);
+
+        mVirtualDisplay = displayManager.createVirtualDisplay(displayName, displayInfo.logicalWidth,
+                displayInfo.logicalHeight, displayInfo.logicalDensityDpi, mReader.getSurface(),
+                0 /*flags*/);
+
+        assertNotNull("virtual display must not be null", mVirtualDisplay);
+
+        return mVirtualDisplay.getDisplay();
+    }
+
+    @After
+    public void tearDown() {
+        releaseDisplay();
+    }
+
+    private void releaseDisplay() {
+        mVirtualDisplay.release();
+        mReader.close();
+    }
+
+    @Test
+    public void testRecentsButtonDisabledOnSecondaryDisplay() {
+        assertTrue("The recents button must be disabled",
+                mNavBar.isRecentsButtonDisabled());
+    }
+}
+
+
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
index cdaa242..abb8c79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
@@ -61,6 +61,10 @@
 @RunWithLooper
 @SmallTest
 public class QuickStepControllerTest extends SysuiTestCase {
+    private static final int NAVBAR_WIDTH = 1000;
+    private static final int NAVBAR_HEIGHT = 300;
+    private static final int EDGE_THRESHOLD = 100;
+
     private QuickStepController mController;
     private NavigationBarView mNavigationBarView;
     private StatusBar mStatusBar;
@@ -73,6 +77,8 @@
         MockitoAnnotations.initMocks(this);
         final ButtonDispatcher backButton = mock(ButtonDispatcher.class);
         mResources = mock(Resources.class);
+        doReturn(EDGE_THRESHOLD).when(mResources)
+                .getDimensionPixelSize(R.dimen.navigation_bar_edge_swipe_threshold);
 
         mProxyService = mock(OverviewProxyService.class);
         mProxy = mock(IOverviewProxy.Stub.class);
@@ -109,7 +115,8 @@
     public void testNoGesturesWhenSwipeUpDisabled() throws Exception {
         doReturn(false).when(mProxyService).shouldShowSwipeUpUI();
         mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
-                null /* swipeLeftAction */, null /* swipeRightAction */);
+                null /* swipeLeftAction */, null /* swipeRightAction */,  null /* leftEdgeSwipe */,
+                null /* rightEdgeSwipe */);
 
         MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
         assertFalse(mController.onInterceptTouchEvent(ev));
@@ -124,7 +131,8 @@
         // Add enabled gesture action
         NavigationGestureAction action = mockAction(true);
         mController.setGestureActions(action, null /* swipeDownAction */,
-                null /* swipeLeftAction */, null /* swipeRightAction */);
+                null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+                null /* rightEdgeSwipe */);
 
         assertFalse(mController.onInterceptTouchEvent(ev));
         verify(mNavigationBarView, times(1)).requestUnbufferedDispatch(ev);
@@ -140,7 +148,8 @@
 
         // Add enabled gesture action
         mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
-                null /* swipeLeftAction */, null /* swipeRightAction */);
+                null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+                null /* rightEdgeSwipe */);
 
         // Set the gesture on deadzone
         doReturn(null).when(mProxyService).getProxy();
@@ -165,7 +174,8 @@
     @Test
     public void testOnTouchIgnoredDownEventAfterOnIntercept() {
         mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
-                null /* swipeLeftAction */, null /* swipeRightAction */);
+                null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+                null /* rightEdgeSwipe */);
 
         MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
         assertFalse(touch(ev));
@@ -178,29 +188,45 @@
 
     @Test
     public void testGesturesCallCorrectAction() throws Exception {
+        doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
+        doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
+
         NavigationGestureAction swipeUp = mockAction(true);
         NavigationGestureAction swipeDown = mockAction(true);
         NavigationGestureAction swipeLeft = mockAction(true);
         NavigationGestureAction swipeRight = mockAction(true);
-        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+        NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+        NavigationGestureAction swipeRightFromEdge = mockAction(true);
+        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+                swipeRightFromEdge);
 
         // Swipe Up
         assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
         // Swipe Down
         assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
         // Swipe Left
-        assertGestureTriggersAction(swipeLeft, 100, 1, 5, 1);
+        assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, 5, 1);
         // Swipe Right
-        assertGestureTriggersAction(swipeRight, 1, 1, 100, 5);
+        assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 5);
+        // Swipe Left from Edge
+        assertGestureTriggersAction(swipeLeftFromEdge, NAVBAR_WIDTH, 1, 5, 1);
+        // Swipe Right from Edge
+        assertGestureTriggersAction(swipeRightFromEdge, 0, 1, NAVBAR_WIDTH, 5);
     }
 
     @Test
     public void testGesturesCallCorrectActionLandscape() throws Exception {
+        doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
+        doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
+
         NavigationGestureAction swipeUp = mockAction(true);
         NavigationGestureAction swipeDown = mockAction(true);
         NavigationGestureAction swipeLeft = mockAction(true);
         NavigationGestureAction swipeRight = mockAction(true);
-        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+        NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+        NavigationGestureAction swipeRightFromEdge = mockAction(true);
+        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+                swipeRightFromEdge);
 
         // In landscape
         mController.setBarState(false /* isRTL */, NAV_BAR_RIGHT);
@@ -208,34 +234,50 @@
         // Swipe Up
         assertGestureTriggersAction(swipeRight, 1, 100, 5, 1);
         // Swipe Down
-        assertGestureTriggersAction(swipeLeft, 1, 1, 5, 100);
+        assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
         // Swipe Left
         assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
         // Swipe Right
         assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
+        // Swipe Up from Edge
+        assertGestureTriggersAction(swipeRightFromEdge, 1, NAVBAR_WIDTH, 5, 0);
+        // Swipe Down from Edge
+        assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, 0, NAVBAR_WIDTH);
     }
 
     @Test
     public void testGesturesCallCorrectActionSeascape() throws Exception {
+        doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
+        doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
+
         mController.setBarState(false /* isRTL */, NAV_BAR_LEFT);
         NavigationGestureAction swipeUp = mockAction(true);
         NavigationGestureAction swipeDown = mockAction(true);
         NavigationGestureAction swipeLeft = mockAction(true);
         NavigationGestureAction swipeRight = mockAction(true);
-        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+        NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+        NavigationGestureAction swipeRightFromEdge = mockAction(true);
+        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+                swipeRightFromEdge);
 
         // Swipe Up
-        assertGestureTriggersAction(swipeLeft, 1, 100, 5, 1);
+        assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
         // Swipe Down
-        assertGestureTriggersAction(swipeRight, 1, 1, 5, 100);
+        assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
         // Swipe Left
         assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
         // Swipe Right
         assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
+        // Swipe Up from Edge
+        assertGestureTriggersAction(swipeLeftFromEdge, 1, NAVBAR_WIDTH, 5, 0);
+        // Swipe Down from Edge
+        assertGestureTriggersAction(swipeRightFromEdge, 0, 1, 0, NAVBAR_WIDTH);
     }
 
     @Test
     public void testGesturesCallCorrectActionRTL() throws Exception {
+        doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
+        doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
         mController.setBarState(true /* isRTL */, NAV_BAR_BOTTOM);
 
         // The swipe gestures below are for LTR, so RTL in portrait will be swapped
@@ -243,20 +285,29 @@
         NavigationGestureAction swipeDown = mockAction(true);
         NavigationGestureAction swipeLeft = mockAction(true);
         NavigationGestureAction swipeRight = mockAction(true);
-        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+        NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+        NavigationGestureAction swipeRightFromEdge = mockAction(true);
+        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+                swipeRightFromEdge);
 
         // Swipe Up in RTL
         assertGestureTriggersAction(swipeUp, 1, 100, 5, 1);
         // Swipe Down in RTL
         assertGestureTriggersAction(swipeDown, 1, 1, 5, 100);
         // Swipe Left in RTL
-        assertGestureTriggersAction(swipeRight, 100, 1, 5, 1);
+        assertGestureTriggersAction(swipeRight, NAVBAR_WIDTH / 2, 1, 5, 1);
         // Swipe Right in RTL
-        assertGestureTriggersAction(swipeLeft, 1, 1, 100, 5);
+        assertGestureTriggersAction(swipeLeft, NAVBAR_WIDTH / 2, 1, NAVBAR_WIDTH, 0);
+        // Swipe Left from Edge
+        assertGestureTriggersAction(swipeRightFromEdge, NAVBAR_WIDTH, 1, 5, 1);
+        // Swipe Right from Edge
+        assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, NAVBAR_WIDTH, 5);
     }
 
     @Test
     public void testGesturesCallCorrectActionLandscapeRTL() throws Exception {
+        doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
+        doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
         mController.setBarState(true /* isRTL */, NAV_BAR_RIGHT);
 
         // The swipe gestures below are for LTR, so RTL in landscape will be swapped
@@ -264,20 +315,29 @@
         NavigationGestureAction swipeDown = mockAction(true);
         NavigationGestureAction swipeLeft = mockAction(true);
         NavigationGestureAction swipeRight = mockAction(true);
-        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+        NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+        NavigationGestureAction swipeRightFromEdge = mockAction(true);
+        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+                swipeRightFromEdge);
 
         // Swipe Up
-        assertGestureTriggersAction(swipeLeft, 1, 100, 5, 1);
+        assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, 1);
         // Swipe Down
-        assertGestureTriggersAction(swipeRight, 1, 1, 5, 100);
+        assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
         // Swipe Left
         assertGestureTriggersAction(swipeUp, 100, 1, 5, 1);
         // Swipe Right
         assertGestureTriggersAction(swipeDown, 1, 1, 100, 5);
+        // Swipe Up from Edge
+        assertGestureTriggersAction(swipeLeftFromEdge, 1, NAVBAR_WIDTH, 5, 0);
+        // Swipe Down from Edge
+        assertGestureTriggersAction(swipeRightFromEdge, 0, 1, 0, NAVBAR_WIDTH);
     }
 
     @Test
     public void testGesturesCallCorrectActionSeascapeRTL() throws Exception {
+        doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getWidth();
+        doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getHeight();
         mController.setBarState(true /* isRTL */, NAV_BAR_LEFT);
 
         // The swipe gestures below are for LTR, so RTL in seascape will be swapped
@@ -285,16 +345,23 @@
         NavigationGestureAction swipeDown = mockAction(true);
         NavigationGestureAction swipeLeft = mockAction(true);
         NavigationGestureAction swipeRight = mockAction(true);
-        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight);
+        NavigationGestureAction swipeLeftFromEdge = mockAction(true);
+        NavigationGestureAction swipeRightFromEdge = mockAction(true);
+        mController.setGestureActions(swipeUp, swipeDown, swipeLeft, swipeRight, swipeLeftFromEdge,
+                swipeRightFromEdge);
 
         // Swipe Up
-        assertGestureTriggersAction(swipeRight, 1, 100, 5, 1);
+        assertGestureTriggersAction(swipeRight, 1, NAVBAR_WIDTH / 2, 5, 1);
         // Swipe Down
-        assertGestureTriggersAction(swipeLeft, 1, 1, 5, 100);
+        assertGestureTriggersAction(swipeLeft, 1, NAVBAR_WIDTH / 2, 5, NAVBAR_WIDTH);
         // Swipe Left
         assertGestureTriggersAction(swipeDown, 100, 1, 5, 1);
         // Swipe Right
         assertGestureTriggersAction(swipeUp, 1, 1, 100, 5);
+        // Swipe Up from Edge
+        assertGestureTriggersAction(swipeRightFromEdge, 1, NAVBAR_WIDTH, 5, 0);
+        // Swipe Down from Edge
+        assertGestureTriggersAction(swipeLeftFromEdge, 0, 1, 0, NAVBAR_WIDTH);
     }
 
     @Test
@@ -305,7 +372,8 @@
         // Add enabled gesture action
         NavigationGestureAction action = mockAction(true);
         mController.setGestureActions(action, null /* swipeDownAction */,
-                null /* swipeLeftAction */, null /* swipeRightAction */);
+                null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+                null /* rightEdgeSwipe */);
 
         // Touch down to begin swipe
         MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 1, 100);
@@ -326,7 +394,8 @@
         NavigationGestureAction action = mockAction(true);
         doReturn(false).when(action).canRunWhenNotificationsShowing();
         mController.setGestureActions(action, null /* swipeDownAction */,
-                null /* swipeLeftAction */, null /* swipeRightAction */);
+                null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+                null /* rightEdgeSwipe */);
 
         // Show the notifications
         doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
@@ -351,7 +420,8 @@
     public void testActionCannotPerform() throws Exception {
         NavigationGestureAction action = mockAction(true);
         mController.setGestureActions(action, null /* swipeDownAction */,
-                null /* swipeLeftAction */, null /* swipeRightAction */);
+                null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+                null /* rightEdgeSwipe */);
 
         // Cannot perform action
         doReturn(false).when(action).canPerformAction();
@@ -374,13 +444,17 @@
 
     @Test
     public void testQuickScrub() throws Exception {
+        doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
+        doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
         QuickScrubAction action = spy(new QuickScrubAction(mNavigationBarView, mProxyService));
         mController.setGestureActions(null /* swipeUpAction */, null /* swipeDownAction */,
-                null /* swipeLeftAction */, action);
+                null /* swipeLeftAction */, action, null /* leftEdgeSwipe */,
+                null /* rightEdgeSwipe */);
+        int x = NAVBAR_WIDTH / 2;
         int y = 20;
 
         // Set the layout and other padding to make sure the scrub fraction is calculated correctly
-        action.onLayout(true, 0, 0, 400, 100);
+        action.onLayout(true, 0, 0, NAVBAR_WIDTH, NAVBAR_HEIGHT);
         doReturn(0).when(mNavigationBarView).getPaddingLeft();
         doReturn(0).when(mNavigationBarView).getPaddingRight();
         doReturn(0).when(mNavigationBarView).getPaddingStart();
@@ -393,14 +467,14 @@
         doReturn(true).when(mNavigationBarView).isQuickScrubEnabled();
 
         // Touch down
-        MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, 0, y);
+        MotionEvent downEvent = event(MotionEvent.ACTION_DOWN, x, y);
         assertFalse(touch(downEvent));
         assertNull(mController.getCurrentAction());
         verify(mProxy, times(1)).onPreMotionEvent(mNavigationBarView.getDownHitTarget());
         verify(mProxy, times(1)).onMotionEvent(downEvent);
 
         // Move to start trigger action from gesture
-        MotionEvent moveEvent1 = event(MotionEvent.ACTION_MOVE, 100, y);
+        MotionEvent moveEvent1 = event(MotionEvent.ACTION_MOVE, x + 100, y);
         assertTrue(touch(moveEvent1));
         assertEquals(action, mController.getCurrentAction());
         verify(action, times(1)).onGestureStart(moveEvent1);
@@ -410,11 +484,13 @@
         verify(mProxy, never()).onMotionEvent(moveEvent1);
 
         // Move again for scrub
-        MotionEvent moveEvent2 = event(MotionEvent.ACTION_MOVE, 200, y);
+        float fraction = 3f / 4;
+        x = (int) (NAVBAR_WIDTH * fraction);
+        MotionEvent moveEvent2 = event(MotionEvent.ACTION_MOVE, x, y);
         assertTrue(touch(moveEvent2));
         assertEquals(action, mController.getCurrentAction());
-        verify(action, times(1)).onGestureMove(200, y);
-        verify(mProxy, times(1)).onQuickScrubProgress(1f / 2);
+        verify(action, times(1)).onGestureMove(x, y);
+        verify(mProxy, times(1)).onQuickScrubProgress(fraction);
         verify(mProxy, never()).onMotionEvent(moveEvent2);
 
         // Action up
@@ -430,7 +506,8 @@
     public void testQuickStep() throws Exception {
         QuickStepAction action = new QuickStepAction(mNavigationBarView, mProxyService);
         mController.setGestureActions(action, null /* swipeDownAction */,
-                null /* swipeLeftAction */, null /* swipeRightAction */);
+                null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+                null /* rightEdgeSwipe */);
 
         // Notifications are up, should prevent quickstep
         doReturn(false).when(mNavigationBarView).isNotificationsFullyCollapsed();
@@ -466,7 +543,8 @@
     public void testLongPressPreventDetection() throws Exception {
         NavigationGestureAction action = mockAction(true);
         mController.setGestureActions(action, null /* swipeDownAction */,
-                null /* swipeLeftAction */, null /* swipeRightAction */);
+                null /* swipeLeftAction */, null /* swipeRightAction */, null /* leftEdgeSwipe */,
+                null /* rightEdgeSwipe */);
 
         // Start the drag up
         assertFalse(touch(MotionEvent.ACTION_DOWN, 100, 1));
@@ -488,23 +566,21 @@
 
     @Test
     public void testHitTargetDragged() throws Exception {
-        final int navbarWidth = 1000;
-        final int navbarHeight = 1000;
         ButtonDispatcher button = mock(ButtonDispatcher.class);
-        FakeLocationView buttonView = spy(new FakeLocationView(mContext, navbarWidth / 2,
-                navbarHeight / 2));
+        FakeLocationView buttonView = spy(new FakeLocationView(mContext, NAVBAR_WIDTH / 2,
+                NAVBAR_HEIGHT / 2));
         doReturn(buttonView).when(button).getCurrentView();
 
         NavigationGestureAction action = mockAction(true);
-        mController.setGestureActions(action, action, action, action);
+        mController.setGestureActions(action, action, action, action, action, action);
 
         // Setup getting the hit target
         doReturn(HIT_TARGET_HOME).when(action).requiresTouchDownHitTarget();
-        doReturn(true).when(action).requiresDragWithHitTarget();
+        doReturn(true).when(action).allowHitTargetToMoveOverDrag();
         doReturn(HIT_TARGET_HOME).when(mNavigationBarView).getDownHitTarget();
         doReturn(button).when(mNavigationBarView).getHomeButton();
-        doReturn(navbarWidth).when(mNavigationBarView).getWidth();
-        doReturn(navbarHeight).when(mNavigationBarView).getHeight();
+        doReturn(NAVBAR_WIDTH).when(mNavigationBarView).getWidth();
+        doReturn(NAVBAR_HEIGHT).when(mNavigationBarView).getHeight();
 
         // Portrait
         assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_BOTTOM);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 27123e4..02f8949 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -70,7 +70,7 @@
                 mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
                 statusBarWindowView, mock(NotificationListContainerViewGroup.class),
                 mock(DozeScrimController.class), mock(ScrimController.class),
-                mock(ActivityLaunchAnimator.class));
+                mock(ActivityLaunchAnimator.class), mock(StatusBarKeyguardViewManager.class));
     }
 
     @Test
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 89220d5..f2ae161 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6609,6 +6609,24 @@
     // OS: Q
     DIALOG_DISABLE_DEVELOPMENT_OPTIONS = 1591;
 
+    // Tag for an ACTION: QS -> Tile click / Secondary click / long press
+    //    indicating the StatusBarState when menu was pulled down
+    // CATEGORY: QUICK_SETTINGS
+    // OS: Q
+    FIELD_STATUS_BAR_STATE = 1592;
+
+    // Tag for an ACTION: QS -> Tile click / Secondary click / long press
+    //    indicating whether current state is full QS
+    // CATEGORY: QUICK_SETTINGS
+    // OS: Q
+    FIELD_IS_FULL_QS = 1593;
+
+    // ACTION: Display folding state was changed
+    // CATEGORY: OTHER
+    //   SUBTYPE: 1 if display is folded, 0 if not.
+    // OS: Q
+    ACTION_DISPLAY_FOLD = 1594;
+
     // ---- End Q Constants, all Q constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 1fda074..e9ce737 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1344,6 +1344,27 @@
 
   // Amount of time wifi is in tx (ms)
   optional int64 tx_time_ms = 5;
+
+  // Amount of time kernel is active because of wifi data (ms)
+  optional int64 wifi_kernel_active_time_ms = 6;
+
+  // Number of packets sent (tx)
+  optional int64 num_packets_tx = 7;
+
+  // Number of bytes sent (tx)
+  optional int64 num_bytes_tx = 8;
+
+  // Number of packets received (rx)
+  optional int64 num_packets_rx = 9;
+
+  // Number of bytes sent (rx)
+  optional int64 num_bytes_rx = 10;
+
+  // Amount of time wifi is in sleep (ms)
+  optional int64 sleep_time_ms = 11;
+
+  // Amount of time wifi is scanning (ms)
+  optional int64 scan_time_ms = 12;
 }
 
 // Metrics for Wifi Wake
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d76a5df..aef16b1 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1238,7 +1238,15 @@
             return;
         }
 
-        final UserData userData = mService.getUserData();
+        final UserData packageUserData = lastResponse.getUserData();
+
+        final UserData userData;
+        if (packageUserData != null) {
+            // Replace default userData
+            userData = packageUserData;
+        } else {
+            userData = mService.getUserData();
+        }
 
         for (int i = 0; i < mViewStates.size(); i++) {
             final ViewState viewState = mViewStates.valueAt(i);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 0b06f28..3acdc8e3 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -27,7 +27,6 @@
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -38,8 +37,6 @@
 import android.os.RemoteException;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -82,11 +79,11 @@
         return sInstance;
     }
 
-    /** Helper to create the {@link BackupManagerService} instance. */
-    public static BackupManagerService create(
-            Context context,
-            Trampoline parent,
-            HandlerThread backupThread) {
+    private UserBackupManagerService mUserBackupManagerService;
+
+    /** Instantiate a new instance of {@link BackupManagerService}. */
+    public BackupManagerService(
+            Context context, Trampoline trampoline, HandlerThread backupThread) {
         // Set up our transport options and initialize the default transport
         SystemConfig systemConfig = SystemConfig.getInstance();
         Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
@@ -94,50 +91,9 @@
             transportWhitelist = Collections.emptySet();
         }
 
-        String transport =
-                Settings.Secure.getString(
-                        context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
-        if (TextUtils.isEmpty(transport)) {
-            transport = null;
-        }
-        if (DEBUG) {
-            Slog.v(TAG, "Starting with transport " + transport);
-        }
-        TransportManager transportManager =
-                new TransportManager(
-                        context,
-                        transportWhitelist,
-                        transport);
-
-        // If encrypted file systems is enabled or disabled, this call will return the
-        // correct directory.
-        File baseStateDir = new File(Environment.getDataDirectory(), "backup");
-
-        // This dir on /cache is managed directly in init.rc
-        File dataDir = new File(Environment.getDownloadCacheDirectory(), "backup_stage");
-
-        return new BackupManagerService(
-                context,
-                parent,
-                backupThread,
-                baseStateDir,
-                dataDir,
-                transportManager);
-    }
-
-    private UserBackupManagerService mUserBackupManagerService;
-
-    /** Instantiate a new instance of {@link BackupManagerService}. */
-    public BackupManagerService(
-            Context context,
-            Trampoline trampoline,
-            HandlerThread backupThread,
-            File baseStateDir,
-            File dataDir,
-            TransportManager transportManager) {
         mUserBackupManagerService =
-                new UserBackupManagerService(
-                        context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+                UserBackupManagerService.createAndInitializeService(
+                        context, trampoline, backupThread, transportWhitelist);
     }
 
     // TODO(b/118520567): Remove when tests are modified to use per-user instance.
@@ -151,30 +107,6 @@
      * a background thread to keep the unlock time down.
      */
     public void unlockSystemUser() {
-        // Migrate legacy setting
-        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
-        if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
-            if (DEBUG) {
-                Slog.i(TAG, "Backup enable apparently not migrated");
-            }
-            ContentResolver resolver = sInstance.getContext().getContentResolver();
-            int enableState = Settings.Secure.getIntForUser(resolver,
-                    Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
-            if (enableState >= 0) {
-                if (DEBUG) {
-                    Slog.i(TAG, "Migrating enable state " + (enableState != 0));
-                }
-                writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
-                Settings.Secure.putStringForUser(resolver,
-                        Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
-            } else {
-                if (DEBUG) {
-                    Slog.i(TAG, "Backup not yet configured; retaining null enable state");
-                }
-            }
-        }
-        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
         try {
             sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
@@ -184,6 +116,15 @@
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
+    /**
+     * Starts the backup service for user {@code userId} by creating a new instance of {@link
+     * UserBackupManagerService} and registering it with this service.
+     */
+    // TODO(b/120212806): Add UserBackupManagerService initialization logic.
+    void startServiceForUser(int userId) {
+        // Intentionally empty.
+    }
+
     /*
      * The following methods are implementations of IBackupManager methods called from Trampoline.
      * They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
@@ -563,12 +504,6 @@
         mUserBackupManagerService.dump(fd, pw, args);
     }
 
-    private static boolean backupSettingMigrated(int userId) {
-        File base = new File(Environment.getDataDirectory(), "backup");
-        File enableFile = new File(base, BACKUP_ENABLE_FILE);
-        return enableFile.exists();
-    }
-
     private static boolean readBackupEnableState(int userId) {
         File base = new File(Environment.getDataDirectory(), "backup");
         File enableFile = new File(base, BACKUP_ENABLE_FILE);
@@ -598,14 +533,8 @@
             stage.renameTo(enableFile);
             // will be synced immediately by the try-with-resources call to close()
         } catch (IOException | RuntimeException e) {
-            // Whoops; looks like we're doomed.  Roll everything out, disabled,
-            // including the legacy state.
             Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
                     + e.getMessage());
-
-            ContentResolver resolver = sInstance.getContext().getContentResolver();
-            Settings.Secure.putStringForUser(resolver,
-                    Settings.Secure.BACKUP_ENABLED, null, userId);
             enableFile.delete();
             stage.delete();
         }
@@ -626,7 +555,9 @@
         @Override
         public void onUnlockUser(int userId) {
             if (userId == UserHandle.USER_SYSTEM) {
-                sInstance.unlockSystemUser();
+                sInstance.initializeServiceAndUnlockSystemUser();
+            } else {
+                sInstance.startServiceForUser(userId);
             }
         }
     }
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 59629aa..108f50d 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -41,6 +41,7 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -81,6 +82,12 @@
     // Product-level suppression of backup/restore.
     private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
 
+    private static final String BACKUP_THREAD = "backup";
+
+    /** Values for setting {@link Settings.Global#BACKUP_MULTI_USER_ENABLED} */
+    private static final int MULTI_USER_DISABLED = 0;
+    private static final int MULTI_USER_ENABLED = 1;
+
     private final Context mContext;
 
     @GuardedBy("mStateLock")
@@ -91,18 +98,31 @@
 
     private volatile BackupManagerService mService;
     private HandlerThread mHandlerThread;
+    private Handler mHandler;
 
     public Trampoline(Context context) {
         mContext = context;
         mGlobalDisable = isBackupDisabled();
         mSuppressFile = getSuppressFile();
         mSuppressFile.getParentFile().mkdirs();
+
+        mHandlerThread = new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
     }
 
     protected boolean isBackupDisabled() {
         return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
     }
 
+    protected boolean isMultiUserEnabled() {
+        return Settings.Global.getInt(
+                mContext.getContentResolver(),
+                Settings.Global.BACKUP_MULTI_USER_ENABLED,
+                MULTI_USER_DISABLED)
+                == MULTI_USER_ENABLED;
+    }
+
     protected int binderGetCallingUid() {
         return Binder.getCallingUid();
     }
@@ -117,7 +137,7 @@
     }
 
     protected BackupManagerService createBackupManagerService() {
-        return BackupManagerService.create(mContext, this, mHandlerThread);
+        return new BackupManagerService(mContext, this, mHandlerThread);
     }
 
     /**
@@ -147,15 +167,9 @@
     /**
      * Called from {@link BackupManagerService.Lifecycle} when the system user is unlocked. Attempts
      * to initialize {@link BackupManagerService} and set backup state for the system user.
-     *
-     * @see BackupManagerService#unlockSystemUser()
      */
-    void unlockSystemUser() {
-        mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
-        mHandlerThread.start();
-
-        Handler h = new Handler(mHandlerThread.getLooper());
-        h.post(
+    void initializeServiceAndUnlockSystemUser() {
+        mHandler.post(
                 () -> {
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
                     initializeService(UserHandle.USER_SYSTEM);
@@ -170,6 +184,29 @@
     }
 
     /**
+     * Called from {@link BackupManagerService.Lifecycle} when a non-system user {@code userId} is
+     * unlocked. Starts the backup service for this user if the service supports multi-user.
+     * Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time low.
+     */
+    // TODO(b/120212806): Consolidate service start for system and non-system users when system
+    // user-only logic is removed.
+    void startServiceForUser(int userId) {
+        if (!isMultiUserEnabled()) {
+            Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
+            return;
+        }
+
+        mHandler.post(
+                () -> {
+                    BackupManagerService service = mService;
+                    if (service != null) {
+                        Slog.i(TAG, "Starting service for user: " + userId);
+                        service.startServiceForUser(userId);
+                    }
+                });
+    }
+
+    /**
      * Only privileged callers should be changing the backup state. This method only acts on {@link
      * UserHandle#USER_SYSTEM} and is a no-op if passed non-system users. Deactivating backup in the
      * system user also deactivates backup in all users.
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index fe16afe..5220a59 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
@@ -68,6 +69,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -86,6 +88,7 @@
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.EventLog;
@@ -167,6 +170,10 @@
     // Persistently track the need to do a full init.
     private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
 
+    // Name of the directories the service stores bookkeeping data under.
+    private static final String BACKUP_PERSISTENT_DIR = "backup";
+    private static final String BACKUP_STAGING_DIR = "backup_stage";
+
     // System-private key used for backing up an app's widget state.  Must
     // begin with U+FFxx by convention (we reserve all keys starting
     // with U+FF00 or higher for system use).
@@ -360,15 +367,71 @@
     private long mAncestralToken = 0;
     private long mCurrentToken = 0;
 
+    /**
+     * Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
+     * includes setting up the directories where we keep our bookkeeping and transport management.
+     *
+     * @see #createAndInitializeService(Context, Trampoline, HandlerThread, File, File,
+     *     TransportManager)
+     */
+    static UserBackupManagerService createAndInitializeService(
+            Context context,
+            Trampoline trampoline,
+            HandlerThread backupThread,
+            Set<ComponentName> transportWhitelist) {
+        String currentTransport =
+                Settings.Secure.getString(
+                        context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+        if (TextUtils.isEmpty(currentTransport)) {
+            currentTransport = null;
+        }
+
+        if (DEBUG) {
+            Slog.v(TAG, "Starting with transport " + currentTransport);
+        }
+        TransportManager transportManager =
+                new TransportManager(context, transportWhitelist, currentTransport);
+
+        File baseStateDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
+
+        // This dir on /cache is managed directly in init.rc
+        File dataDir = new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
+
+        return createAndInitializeService(
+                context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+    }
+
+    /**
+     * Creates an instance of {@link UserBackupManagerService}.
+     *
+     * @param context The system server context.
+     * @param trampoline A reference to the proxy to {@link BackupManagerService}.
+     * @param backupThread The thread running backup/restore operations for the user.
+     * @param baseStateDir The directory we store the user's persistent bookkeeping data.
+     * @param dataDir The directory we store the user's temporary staging data.
+     * @param transportManager The {@link TransportManager} responsible for handling the user's
+     *     transports.
+     */
     @VisibleForTesting
-    public UserBackupManagerService(
+    public static UserBackupManagerService createAndInitializeService(
+            Context context,
+            Trampoline trampoline,
+            HandlerThread backupThread,
+            File baseStateDir,
+            File dataDir,
+            TransportManager transportManager) {
+        return new UserBackupManagerService(
+                context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+    }
+
+    private UserBackupManagerService(
             Context context,
             Trampoline parent,
             HandlerThread backupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
-        mContext = context;
+        mContext = checkNotNull(context, "context cannot be null");
         mPackageManager = context.getPackageManager();
         mPackageManagerBinder = AppGlobals.getPackageManager();
         mActivityManager = ActivityManager.getService();
@@ -377,6 +440,7 @@
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
 
+        checkNotNull(parent, "trampoline cannot be null");
         mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
 
         mAgentTimeoutParameters = new
@@ -384,6 +448,7 @@
         mAgentTimeoutParameters.start();
 
         // spin up the backup/restore handler thread
+        checkNotNull(backupThread, "backupThread cannot be null");
         mBackupHandler = new BackupHandler(this, backupThread.getLooper());
 
         // Set up our bookkeeping
@@ -398,13 +463,13 @@
                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
                 false, mProvisionedObserver);
 
-        mBaseStateDir = baseStateDir;
+        mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null");
         mBaseStateDir.mkdirs();
         if (!SELinux.restorecon(mBaseStateDir)) {
             Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
         }
 
-        mDataDir = dataDir;
+        mDataDir = checkNotNull(dataDir, "dataDir cannot be null");
 
         mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
 
@@ -451,7 +516,7 @@
             addPackageParticipantsLocked(null);
         }
 
-        mTransportManager = transportManager;
+        mTransportManager = checkNotNull(transportManager, "transportManager cannot be null");
         mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
         mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
         mBackupHandler.postDelayed(
@@ -465,7 +530,6 @@
         mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
     }
 
-
     public BackupManagerConstants getConstants() {
         return mConstants;
     }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 70b8339..cccacf4 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -21,7 +21,6 @@
         ":mediaupdateservice_aidl",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
-        ":netd_aidl",
         ":netd_metrics_aidl",
     ],
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 14503f9..eda9fe1 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -902,6 +902,7 @@
         // Listen to package add and removal events for all users.
         intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
         mContext.registerReceiverAsUser(
@@ -4203,12 +4204,46 @@
         mPermissionMonitor.onPackageAdded(packageName, uid);
     }
 
-    private void onPackageRemoved(String packageName, int uid) {
+    private void onPackageReplaced(String packageName, int uid) {
+        if (TextUtils.isEmpty(packageName) || uid < 0) {
+            Slog.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
+            return;
+        }
+        final int userId = UserHandle.getUserId(uid);
+        synchronized (mVpns) {
+            final Vpn vpn = mVpns.get(userId);
+            if (vpn == null) {
+                return;
+            }
+            // Legacy always-on VPN won't be affected since the package name is not set.
+            if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
+                Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user "
+                        + userId);
+                vpn.startAlwaysOnVpn();
+            }
+        }
+    }
+
+    private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
         if (TextUtils.isEmpty(packageName) || uid < 0) {
             Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
             return;
         }
         mPermissionMonitor.onPackageRemoved(uid);
+
+        final int userId = UserHandle.getUserId(uid);
+        synchronized (mVpns) {
+            final Vpn vpn = mVpns.get(userId);
+            if (vpn == null) {
+                return;
+            }
+            // Legacy always-on VPN won't be affected since the package name is not set.
+            if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
+                Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
+                        + userId);
+                vpn.setAlwaysOnPackage(null, false);
+            }
+        }
     }
 
     private void onUserUnlocked(int userId) {
@@ -4245,8 +4280,12 @@
                 onUserUnlocked(userId);
             } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
                 onPackageAdded(packageName, uid);
+            } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+                onPackageReplaced(packageName, uid);
             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                onPackageRemoved(packageName, uid);
+                final boolean isReplacing = intent.getBooleanExtra(
+                        Intent.EXTRA_REPLACING, false);
+                onPackageRemoved(packageName, uid, isReplacing);
             }
         }
     };
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index cc7bf33..8b992eb 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,8 +17,11 @@
 package com.android.server;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.location.LocationProvider.AVAILABLE;
 import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
 
+import static com.android.internal.util.Preconditions.checkState;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -47,14 +50,12 @@
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
 import android.location.IGnssStatusListener;
-import android.location.IGnssStatusProvider;
 import android.location.IGpsGeofenceHardware;
 import android.location.ILocationListener;
 import android.location.ILocationManager;
 import android.location.INetInitiatedListener;
 import android.location.Location;
 import android.location.LocationManager;
-import android.location.LocationProvider;
 import android.location.LocationRequest;
 import android.os.Binder;
 import android.os.Bundle;
@@ -84,6 +85,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.server.location.AbstractLocationProvider;
 import com.android.server.location.ActivityRecognitionProxy;
 import com.android.server.location.GeocoderProxy;
 import com.android.server.location.GeofenceManager;
@@ -92,9 +94,9 @@
 import com.android.server.location.GnssLocationProvider;
 import com.android.server.location.GnssMeasurementsProvider;
 import com.android.server.location.GnssNavigationMessageProvider;
+import com.android.server.location.GnssStatusListenerHelper;
 import com.android.server.location.LocationBlacklist;
 import com.android.server.location.LocationFudger;
-import com.android.server.location.LocationProviderInterface;
 import com.android.server.location.LocationProviderProxy;
 import com.android.server.location.LocationRequestStatistics;
 import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
@@ -131,8 +133,6 @@
     // Location resolution level: fine location data
     private static final int RESOLUTION_LEVEL_FINE = 2;
 
-    private static final String ACCESS_MOCK_LOCATION =
-            android.Manifest.permission.ACCESS_MOCK_LOCATION;
     private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
             android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
     private static final String INSTALL_LOCATION_PROVIDER =
@@ -182,7 +182,7 @@
     private ActivityManager mActivityManager;
     private UserManager mUserManager;
     private GeocoderProxy mGeocodeProvider;
-    private IGnssStatusProvider mGnssStatusProvider;
+    private GnssStatusListenerHelper mGnssStatusProvider;
     private INetInitiatedListener mNetInitiatedListener;
     private LocationWorkerHandler mLocationHandler;
     private PassiveProvider mPassiveProvider;  // track passive provider for special cases
@@ -192,12 +192,6 @@
     private IGpsGeofenceHardware mGpsGeofenceProxy;
 
     // --- fields below are protected by mLock ---
-    // Set of providers that are explicitly enabled
-    // Only used by passive, fused & test.  Network & GPS are controlled separately, and not listed.
-    private final Set<String> mEnabledProviders = new HashSet<>();
-
-    // Set of providers that are explicitly disabled
-    private final Set<String> mDisabledProviders = new HashSet<>();
 
     // Mock (test) providers
     private final HashMap<String, MockProvider> mMockProviders =
@@ -207,15 +201,15 @@
     private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
 
     // currently installed providers (with mocks replacing real providers)
-    private final ArrayList<LocationProviderInterface> mProviders =
+    private final ArrayList<LocationProvider> mProviders =
             new ArrayList<>();
 
     // real providers, saved here when mocked out
-    private final HashMap<String, LocationProviderInterface> mRealProviders =
+    private final HashMap<String, LocationProvider> mRealProviders =
             new HashMap<>();
 
     // mapping from provider name to provider
-    private final HashMap<String, LocationProviderInterface> mProvidersByName =
+    private final HashMap<String, LocationProvider> mProvidersByName =
             new HashMap<>();
 
     // mapping from provider name to all its UpdateRecords
@@ -270,13 +264,8 @@
         PackageManagerInternal packageManagerInternal = LocalServices.getService(
                 PackageManagerInternal.class);
         packageManagerInternal.setLocationPackagesProvider(
-                new PackageManagerInternal.PackagesProvider() {
-                    @Override
-                    public String[] getPackages(int userId) {
-                        return mContext.getResources().getStringArray(
-                                com.android.internal.R.array.config_locationProviderPackageNames);
-                    }
-                });
+                userId -> mContext.getResources().getStringArray(
+                        com.android.internal.R.array.config_locationProviderPackageNames));
 
         if (D) Log.d(TAG, "Constructed");
 
@@ -321,30 +310,17 @@
             mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
                     AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
 
-            PackageManager.OnPermissionsChangedListener permissionListener
-                    = new PackageManager.OnPermissionsChangedListener() {
-                @Override
-                public void onPermissionsChanged(final int uid) {
-                    synchronized (mLock) {
-                        applyAllProviderRequirementsLocked();
-                    }
+            PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
+                synchronized (mLock) {
+                    applyAllProviderRequirementsLocked();
                 }
             };
             mPackageManager.addOnPermissionsChangeListener(permissionListener);
 
             // listen for background/foreground changes
-            ActivityManager.OnUidImportanceListener uidImportanceListener
-                    = new ActivityManager.OnUidImportanceListener() {
-                @Override
-                public void onUidImportance(final int uid, final int importance) {
-                    mLocationHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            onUidImportanceChanged(uid, importance);
-                        }
-                    });
-                }
-            };
+            ActivityManager.OnUidImportanceListener uidImportanceListener =
+                    (uid, importance) -> mLocationHandler.post(
+                            () -> onUidImportanceChanged(uid, importance));
             mActivityManager.addOnUidImportanceListener(uidImportanceListener,
                     FOREGROUND_IMPORTANCE_CUTOFF);
 
@@ -356,7 +332,10 @@
 
             // prepare providers
             loadProvidersLocked();
-            updateProvidersLocked();
+            updateProvidersSettingsLocked();
+            for (LocationProvider provider : mProviders) {
+                applyRequirementsLocked(provider.getName());
+            }
         }
 
         // listen for settings changes
@@ -366,7 +345,7 @@
                     @Override
                     public void onChange(boolean selfChange) {
                         synchronized (mLock) {
-                            updateProvidersLocked();
+                            updateProvidersSettingsLocked();
                         }
                     }
                 }, UserHandle.USER_ALL);
@@ -377,7 +356,9 @@
                     @Override
                     public void onChange(boolean selfChange) {
                         synchronized (mLock) {
-                            updateProvidersLocked();
+                            for (LocationProvider provider : mProviders) {
+                                applyRequirementsLocked(provider.getName());
+                            }
                         }
                     }
                 }, UserHandle.USER_ALL);
@@ -402,7 +383,9 @@
                     public void onChange(boolean selfChange) {
                         synchronized (mLock) {
                             updateBackgroundThrottlingWhitelistLocked();
-                            updateProvidersLocked();
+                            for (LocationProvider provider : mProviders) {
+                                applyRequirementsLocked(provider.getName());
+                            }
                         }
                     }
                 }, UserHandle.USER_ALL);
@@ -414,7 +397,6 @@
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
         intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
         intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
-        intentFilter.addAction(Intent.ACTION_SHUTDOWN);
 
         mContext.registerReceiverAsUser(new BroadcastReceiver() {
             @Override
@@ -425,12 +407,6 @@
                 } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
                         || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
                     updateUserProfiles(mCurrentUserId);
-                } else if (Intent.ACTION_SHUTDOWN.equals(action)) {
-                    // shutdown only if UserId indicates whole system, not just one user
-                    if (D) Log.d(TAG, "Shutdown received with UserId: " + getSendingUserId());
-                    if (getSendingUserId() == UserHandle.USER_ALL) {
-                        shutdownComponents();
-                    }
                 }
             }
         }, UserHandle.ALL, intentFilter, null, mLocationHandler);
@@ -463,14 +439,16 @@
             }
 
             for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
-                if (entry.getValue().mUid == uid) {
+                Identity callerIdentity = entry.getValue();
+                if (callerIdentity.mUid == uid) {
                     if (D) {
                         Log.d(TAG, "gnss measurements listener from uid " + uid
                                 + " is now " + (foreground ? "foreground" : "background)"));
                     }
                     if (foreground || isThrottlingExemptLocked(entry.getValue())) {
                         mGnssMeasurementsProvider.addListener(
-                                IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
+                                IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
+                                callerIdentity.mUid, callerIdentity.mPackageName);
                     } else {
                         mGnssMeasurementsProvider.removeListener(
                                 IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
@@ -479,7 +457,8 @@
             }
 
             for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
-                if (entry.getValue().mUid == uid) {
+                Identity callerIdentity = entry.getValue();
+                if (callerIdentity.mUid == uid) {
                     if (D) {
                         Log.d(TAG, "gnss navigation message listener from uid "
                                 + uid + " is now "
@@ -487,13 +466,16 @@
                     }
                     if (foreground || isThrottlingExemptLocked(entry.getValue())) {
                         mGnssNavigationMessageProvider.addListener(
-                                IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
+                                IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
+                                callerIdentity.mUid, callerIdentity.mPackageName);
                     } else {
                         mGnssNavigationMessageProvider.removeListener(
                                 IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
                     }
                 }
             }
+
+            // TODO(b/120449926): The GNSS status listeners should be handled similar to the above.
         }
     }
 
@@ -502,30 +484,13 @@
     }
 
     /**
-     * Provides a way for components held by the {@link LocationManagerService} to clean-up
-     * gracefully on system's shutdown.
-     *
-     * NOTES:
-     * 1) Only provides a chance to clean-up on an opt-in basis. This guarantees back-compat
-     * support for components that do not wish to handle such event.
-     */
-    private void shutdownComponents() {
-        if (D) Log.d(TAG, "Shutting down components...");
-
-        LocationProviderInterface gpsProvider = mProvidersByName.get(LocationManager.GPS_PROVIDER);
-        if (gpsProvider != null && gpsProvider.isEnabled()) {
-            gpsProvider.disable();
-        }
-    }
-
-    /**
      * Makes a list of userids that are related to the current user. This is
      * relevant when using managed profiles. Otherwise the list only contains
      * the current user.
      *
      * @param currentUserId the current user, who might have an alter-ego.
      */
-    void updateUserProfiles(int currentUserId) {
+    private void updateUserProfiles(int currentUserId) {
         int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
         synchronized (mLock) {
             mCurrentUserProfiles = profileIds;
@@ -614,22 +579,28 @@
 
     private void loadProvidersLocked() {
         // create a passive location provider, which is always enabled
-        PassiveProvider passiveProvider = new PassiveProvider(this);
-        addProviderLocked(passiveProvider);
-        mEnabledProviders.add(passiveProvider.getName());
+        LocationProvider passiveProviderManager = new LocationProvider(
+                LocationManager.PASSIVE_PROVIDER);
+        PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager);
+
+        addProviderLocked(passiveProviderManager);
         mPassiveProvider = passiveProvider;
 
         if (GnssLocationProvider.isSupported()) {
             // Create a gps location provider
-            GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext, this,
+            LocationProvider gnssProviderManager = new LocationProvider(
+                    LocationManager.GPS_PROVIDER);
+            GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
+                    gnssProviderManager,
                     mLocationHandler.getLooper());
+
             mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
             mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
             mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
             mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
             mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
-            addProviderLocked(gnssProvider);
-            mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProvider);
+            addProviderLocked(gnssProviderManager);
+            mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
             mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
             mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
             mGpsGeofenceProxy = gnssProvider.getGpsGeofenceProxy();
@@ -657,34 +628,38 @@
         ensureFallbackFusedProviderPresentLocked(pkgs);
 
         // bind to network provider
+
+        LocationProvider networkProviderManager = new LocationProvider(
+                LocationManager.NETWORK_PROVIDER);
         LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
                 mContext,
-                LocationManager.NETWORK_PROVIDER,
+                networkProviderManager,
                 NETWORK_LOCATION_SERVICE_ACTION,
                 com.android.internal.R.bool.config_enableNetworkLocationOverlay,
                 com.android.internal.R.string.config_networkLocationProviderPackageName,
                 com.android.internal.R.array.config_locationProviderPackageNames);
         if (networkProvider != null) {
-            mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
+            mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
             mProxyProviders.add(networkProvider);
-            addProviderLocked(networkProvider);
+            addProviderLocked(networkProviderManager);
         } else {
             Slog.w(TAG, "no network location provider found");
         }
 
         // bind to fused provider
-        LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
+        LocationProvider fusedProviderManager = new LocationProvider(
+                LocationManager.FUSED_PROVIDER);
+        LocationProviderProxy fusedProvider = LocationProviderProxy.createAndBind(
                 mContext,
-                LocationManager.FUSED_PROVIDER,
+                fusedProviderManager,
                 FUSED_LOCATION_SERVICE_ACTION,
                 com.android.internal.R.bool.config_enableFusedLocationOverlay,
                 com.android.internal.R.string.config_fusedLocationProviderPackageName,
                 com.android.internal.R.array.config_locationProviderPackageNames);
-        if (fusedLocationProvider != null) {
-            addProviderLocked(fusedLocationProvider);
-            mProxyProviders.add(fusedLocationProvider);
-            mEnabledProviders.add(fusedLocationProvider.getName());
-            mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedLocationProvider);
+        if (fusedProvider != null) {
+            addProviderLocked(fusedProviderManager);
+            mProxyProviders.add(fusedProvider);
+            mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
         } else {
             Slog.e(TAG, "no fused location provider found",
                     new IllegalStateException("Location service needs a fused location provider"));
@@ -765,12 +740,9 @@
         synchronized (mLock) {
             mLastLocation.clear();
             mLastLocationCoarseInterval.clear();
-            for (LocationProviderInterface p : mProviders) {
-                updateProviderListenersLocked(p.getName(), false);
-            }
-            mCurrentUserId = userId;
             updateUserProfiles(userId);
-            updateProvidersLocked();
+            updateProvidersSettingsLocked();
+            mCurrentUserId = userId;
         }
     }
 
@@ -786,6 +758,165 @@
         }
     }
 
+    private class LocationProvider implements AbstractLocationProvider.LocationProviderManager {
+
+        private final String mName;
+        private AbstractLocationProvider mProvider;
+
+        // whether the provider is enabled in location settings
+        private boolean mSettingsEnabled;
+
+        // whether the provider considers itself enabled
+        private volatile boolean mEnabled;
+
+        @Nullable
+        private volatile ProviderProperties mProperties;
+
+        private LocationProvider(String name) {
+            mName = name;
+            // TODO: initialize settings enabled?
+        }
+
+        @Override
+        public void onAttachProvider(AbstractLocationProvider provider, boolean initiallyEnabled) {
+            checkState(mProvider == null);
+
+            // the provider is not yet fully constructed at this point, so we may not do anything
+            // except save a reference for later use here. do not call any provider methods.
+            mProvider = provider;
+            mEnabled = initiallyEnabled;
+            mProperties = null;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public boolean isEnabled() {
+            return mSettingsEnabled && mEnabled;
+        }
+
+        @Nullable
+        public ProviderProperties getProperties() {
+            return mProperties;
+        }
+
+        public void setRequest(ProviderRequest request, WorkSource workSource) {
+            mProvider.setRequest(request, workSource);
+        }
+
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            pw.println(mName + " provider:");
+            pw.println(" setting=" + mSettingsEnabled);
+            pw.println(" enabled=" + mEnabled);
+            pw.println(" properties=" + mProperties);
+            mProvider.dump(fd, pw, args);
+        }
+
+        public long getStatusUpdateTime() {
+            return mProvider.getStatusUpdateTime();
+        }
+
+        public int getStatus(Bundle extras) {
+            return mProvider.getStatus(extras);
+        }
+
+        public void sendExtraCommand(String command, Bundle extras) {
+            mProvider.sendExtraCommand(command, extras);
+        }
+
+        // called from any thread
+        @Override
+        public void onReportLocation(Location location) {
+            runOnHandler(() -> LocationManagerService.this.reportLocation(location,
+                    mProvider == mPassiveProvider));
+        }
+
+        // called from any thread
+        @Override
+        public void onReportLocation(List<Location> locations) {
+            runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
+        }
+
+        // called from any thread
+        @Override
+        public void onSetEnabled(boolean enabled) {
+            runOnHandler(() -> {
+                if (enabled == mEnabled) {
+                    return;
+                }
+
+                mEnabled = enabled;
+
+                if (!mSettingsEnabled) {
+                    // this provider was disabled in settings anyways, so a change to it's own
+                    // enabled status won't have any affect.
+                    return;
+                }
+
+                // traditionally clients can listen for changes to the LOCATION_PROVIDERS_ALLOWED
+                // setting to detect when providers are enabled or disabled (even though they aren't
+                // supposed to). to continue to support this we must force a change to this setting.
+                // we use the fused provider because this is forced to be always enabled in settings
+                // anyways, and so won't have any visible effect beyond triggering content observers
+                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        "+" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
+                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        "-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
+
+                synchronized (mLock) {
+                    if (!enabled) {
+                        // If any provider has been disabled, clear all last locations for all
+                        // providers. This is to be on the safe side in case a provider has location
+                        // derived from this disabled provider.
+                        mLastLocation.clear();
+                        mLastLocationCoarseInterval.clear();
+                    }
+
+                    updateProviderListenersLocked(mName);
+                }
+
+                mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
+                        UserHandle.ALL);
+            });
+        }
+
+        @Override
+        public void onSetProperties(ProviderProperties properties) {
+            runOnHandler(() -> mProperties = properties);
+        }
+
+        private void setSettingsEnabled(boolean enabled) {
+            synchronized (mLock) {
+                if (mSettingsEnabled == enabled) {
+                    return;
+                }
+
+                mSettingsEnabled = enabled;
+                if (!mSettingsEnabled) {
+                    // if any provider has been disabled, clear all last locations for all
+                    // providers. this is to be on the safe side in case a provider has location
+                    // derived from this disabled provider.
+                    mLastLocation.clear();
+                    mLastLocationCoarseInterval.clear();
+                    updateProviderListenersLocked(mName);
+                } else if (mEnabled) {
+                    updateProviderListenersLocked(mName);
+                }
+            }
+        }
+
+        private void runOnHandler(Runnable runnable) {
+            if (Looper.myLooper() == mLocationHandler.getLooper()) {
+                runnable.run();
+            } else {
+                mLocationHandler.post(runnable);
+            }
+        }
+    }
+
     /**
      * A wrapper class holding either an ILocationListener or a PendingIntent to receive
      * location updates.
@@ -793,24 +924,24 @@
     private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
         private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
         final Identity mIdentity;
-        final int mAllowedResolutionLevel;  // resolution level allowed to receiver
+        private final int mAllowedResolutionLevel;  // resolution level allowed to receiver
 
-        final ILocationListener mListener;
+        private final ILocationListener mListener;
         final PendingIntent mPendingIntent;
         final WorkSource mWorkSource; // WorkSource for battery blame, or null to assign to caller.
-        final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
-        final Object mKey;
+        private final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
+        private final Object mKey;
 
         final HashMap<String, UpdateRecord> mUpdateRecords = new HashMap<>();
 
         // True if app ops has started monitoring this receiver for locations.
-        boolean mOpMonitoring;
+        private boolean mOpMonitoring;
         // True if app ops has started monitoring this receiver for high power (gps) locations.
-        boolean mOpHighPowerMonitoring;
-        int mPendingBroadcasts;
+        private boolean mOpHighPowerMonitoring;
+        private int mPendingBroadcasts;
         PowerManager.WakeLock mWakeLock;
 
-        Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
+        private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
                 String packageName, WorkSource workSource, boolean hideFromAppOps) {
             mListener = listener;
             mPendingIntent = intent;
@@ -885,9 +1016,10 @@
                 // See if receiver has any enabled update records.  Also note if any update records
                 // are high power (has a high power provider with an interval under a threshold).
                 for (UpdateRecord updateRecord : mUpdateRecords.values()) {
-                    if (isAllowedByCurrentUserSettingsLocked(updateRecord.mProvider)) {
+                    if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
+                            mCurrentUserId)) {
                         requestingLocation = true;
-                        LocationProviderInterface locationProvider
+                        LocationManagerService.LocationProvider locationProvider
                                 = mProvidersByName.get(updateRecord.mProvider);
                         ProviderProperties properties = locationProvider != null
                                 ? locationProvider.getProperties() : null;
@@ -1034,7 +1166,7 @@
             return true;
         }
 
-        public boolean callProviderEnabledLocked(String provider, boolean enabled) {
+        private boolean callProviderEnabledLocked(String provider, boolean enabled) {
             // First update AppOp monitoring.
             // An app may get/lose location access as providers are enabled/disabled.
             updateMonitoring(true);
@@ -1236,7 +1368,7 @@
     private class LinkedCallback implements IBinder.DeathRecipient {
         private final IBatchedLocationCallback mCallback;
 
-        public LinkedCallback(@NonNull IBatchedLocationCallback callback) {
+        private LinkedCallback(@NonNull IBatchedLocationCallback callback) {
             mCallback = callback;
         }
 
@@ -1337,7 +1469,7 @@
         checkCallerIsProvider();
 
         // Currently used only for GNSS locations - update permissions check if changed
-        if (isAllowedByCurrentUserSettingsLocked(LocationManager.GPS_PROVIDER)) {
+        if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
             if (mGnssBatchingCallback == null) {
                 Slog.e(TAG, "reportLocationBatch() called without active Callback");
                 return;
@@ -1352,29 +1484,17 @@
         }
     }
 
-    private void addProviderLocked(LocationProviderInterface provider) {
+    private void addProviderLocked(LocationProvider provider) {
         mProviders.add(provider);
         mProvidersByName.put(provider.getName(), provider);
     }
 
-    private void removeProviderLocked(LocationProviderInterface provider) {
-        provider.disable();
+    private void removeProviderLocked(LocationProvider provider) {
         mProviders.remove(provider);
         mProvidersByName.remove(provider.getName());
     }
 
     /**
-     * Returns "true" if access to the specified location provider is allowed by the current
-     * user's settings. Access to all location providers is forbidden to non-location-provider
-     * processes belonging to background users.
-     *
-     * @param provider the name of the location provider
-     */
-    private boolean isAllowedByCurrentUserSettingsLocked(String provider) {
-        return isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId);
-    }
-
-    /**
      * Returns "true" if access to the specified location provider is allowed by the specified
      * user's settings. Access to all location providers is forbidden to non-location-provider
      * processes belonging to background users.
@@ -1383,13 +1503,28 @@
      * @param userId   the user id to query
      */
     private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
-        if (mEnabledProviders.contains(provider)) {
-            return true;
+        if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
+            return isLocationEnabledForUser(userId);
         }
-        if (mDisabledProviders.contains(provider)) {
-            return false;
+        if (LocationManager.FUSED_PROVIDER.equals(provider)) {
+            return isLocationEnabledForUser(userId);
         }
-        return isLocationProviderEnabledForUser(provider, userId);
+        synchronized (mLock) {
+            if (mMockProviders.containsKey(provider)) {
+                return isLocationEnabledForUser(userId);
+            }
+        }
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            // Use system settings
+            ContentResolver cr = mContext.getContentResolver();
+            String allowedProviders = Settings.Secure.getStringForUser(
+                    cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
+            return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
 
@@ -1481,9 +1616,11 @@
             // network and fused providers are ok with COARSE or FINE
             return RESOLUTION_LEVEL_COARSE;
         } else {
-            // mock providers
-            LocationProviderInterface lp = mMockProviders.get(provider);
-            if (lp != null) {
+            for (LocationProvider lp : mProviders) {
+                if (!lp.getName().equals(provider)) {
+                    continue;
+                }
+
                 ProviderProperties properties = lp.getProperties();
                 if (properties != null) {
                     if (properties.mRequiresSatellite) {
@@ -1496,6 +1633,7 @@
                 }
             }
         }
+
         return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
     }
 
@@ -1550,7 +1688,7 @@
     }
 
     private static String resolutionLevelToOpStr(int allowedResolutionLevel) {
-        switch(allowedResolutionLevel) {
+        switch (allowedResolutionLevel) {
             case RESOLUTION_LEVEL_COARSE:
                 return AppOpsManager.OPSTR_COARSE_LOCATION;
             case RESOLUTION_LEVEL_FINE:
@@ -1565,7 +1703,7 @@
         }
     }
 
-    boolean reportLocationAccessNoThrow(
+    private boolean reportLocationAccessNoThrow(
             int pid, int uid, String packageName, int allowedResolutionLevel) {
         int op = resolutionLevelToOp(allowedResolutionLevel);
         if (op >= 0) {
@@ -1577,7 +1715,8 @@
         return getAllowedResolutionLevel(pid, uid) >= allowedResolutionLevel;
     }
 
-    boolean checkLocationAccess(int pid, int uid, String packageName, int allowedResolutionLevel) {
+    private boolean checkLocationAccess(int pid, int uid, String packageName,
+            int allowedResolutionLevel) {
         int op = resolutionLevelToOp(allowedResolutionLevel);
         if (op >= 0) {
             if (mAppOps.noteOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
@@ -1597,7 +1736,7 @@
         ArrayList<String> out;
         synchronized (mLock) {
             out = new ArrayList<>(mProviders.size());
-            for (LocationProviderInterface provider : mProviders) {
+            for (LocationProvider provider : mProviders) {
                 String name = provider.getName();
                 if (LocationManager.FUSED_PROVIDER.equals(name)) {
                     continue;
@@ -1623,7 +1762,7 @@
         try {
             synchronized (mLock) {
                 out = new ArrayList<>(mProviders.size());
-                for (LocationProviderInterface provider : mProviders) {
+                for (LocationProvider provider : mProviders) {
                     String name = provider.getName();
                     if (LocationManager.FUSED_PROVIDER.equals(name)) {
                         continue;
@@ -1633,7 +1772,8 @@
                                 && !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
                             continue;
                         }
-                        if (criteria != null && !LocationProvider.propertiesMeetCriteria(
+                        if (criteria != null
+                                && !android.location.LocationProvider.propertiesMeetCriteria(
                                 name, provider.getProperties(), criteria)) {
                             continue;
                         }
@@ -1658,7 +1798,7 @@
      */
     @Override
     public String getBestProvider(Criteria criteria, boolean enabledOnly) {
-        String result = null;
+        String result;
 
         List<String> providers = getProviders(criteria, enabledOnly);
         if (!providers.isEmpty()) {
@@ -1673,7 +1813,7 @@
             return result;
         }
 
-        if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+        if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
         return null;
     }
 
@@ -1689,51 +1829,32 @@
 
     @Override
     public boolean providerMeetsCriteria(String provider, Criteria criteria) {
-        LocationProviderInterface p = mProvidersByName.get(provider);
+        LocationProvider p = mProvidersByName.get(provider);
         if (p == null) {
             throw new IllegalArgumentException("provider=" + provider);
         }
 
-        boolean result = LocationProvider.propertiesMeetCriteria(
+        boolean result = android.location.LocationProvider.propertiesMeetCriteria(
                 p.getName(), p.getProperties(), criteria);
         if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
         return result;
     }
 
-    private void updateProvidersLocked() {
-        boolean changesMade = false;
-        for (int i = mProviders.size() - 1; i >= 0; i--) {
-            LocationProviderInterface p = mProviders.get(i);
-            boolean isEnabled = p.isEnabled();
-            String name = p.getName();
-            boolean shouldBeEnabled = isAllowedByCurrentUserSettingsLocked(name);
-            if (isEnabled && !shouldBeEnabled) {
-                updateProviderListenersLocked(name, false);
-                // If any provider has been disabled, clear all last locations for all providers.
-                // This is to be on the safe side in case a provider has location derived from
-                // this disabled provider.
-                mLastLocation.clear();
-                mLastLocationCoarseInterval.clear();
-                changesMade = true;
-            } else if (!isEnabled && shouldBeEnabled) {
-                updateProviderListenersLocked(name, true);
-                changesMade = true;
-            }
+    private void updateProvidersSettingsLocked() {
+        for (LocationProvider p : mProviders) {
+            p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
         }
-        if (changesMade) {
-            mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
-                    UserHandle.ALL);
-            mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
-                    UserHandle.ALL);
-        }
+
+        mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
+                UserHandle.ALL);
     }
 
-    private void updateProviderListenersLocked(String provider, boolean enabled) {
-        int listeners = 0;
-
-        LocationProviderInterface p = mProvidersByName.get(provider);
+    private void updateProviderListenersLocked(String provider) {
+        LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
 
+        boolean enabled = p.isEnabled();
+
         ArrayList<Receiver> deadReceivers = null;
 
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1747,7 +1868,6 @@
                         }
                         deadReceivers.add(record.mReceiver);
                     }
-                    listeners++;
                 }
             }
         }
@@ -1758,16 +1878,11 @@
             }
         }
 
-        if (enabled) {
-            p.enable();
-            applyRequirementsLocked(provider);
-        } else {
-            p.disable();
-        }
+        applyRequirementsLocked(provider);
     }
 
     private void applyRequirementsLocked(String provider) {
-        LocationProviderInterface p = mProvidersByName.get(provider);
+        LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
 
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1779,10 +1894,10 @@
                 resolver,
                 Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
                 DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
-        // initialize the low power mode to true and set to false if any of the records requires
 
-        providerRequest.lowPowerMode = true;
-        if (records != null) {
+        if (p.isEnabled() && records != null && !records.isEmpty()) {
+            // initialize the low power mode to true and set to false if any of the records requires
+            providerRequest.lowPowerMode = true;
             for (UpdateRecord record : records) {
                 if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
                     if (checkLocationAccess(
@@ -1875,7 +1990,7 @@
     public String[] getBackgroundThrottlingWhitelist() {
         synchronized (mLock) {
             return mBackgroundThrottlePackageWhitelist.toArray(
-                    new String[mBackgroundThrottlePackageWhitelist.size()]);
+                    new String[0]);
         }
     }
 
@@ -1922,17 +2037,17 @@
 
     private class UpdateRecord {
         final String mProvider;
-        final LocationRequest mRealRequest;  // original request from client
+        private final LocationRequest mRealRequest;  // original request from client
         LocationRequest mRequest;  // possibly throttled version of the request
-        final Receiver mReceiver;
-        boolean mIsForegroundUid;
-        Location mLastFixBroadcast;
-        long mLastStatusBroadcast;
+        private final Receiver mReceiver;
+        private boolean mIsForegroundUid;
+        private Location mLastFixBroadcast;
+        private long mLastStatusBroadcast;
 
         /**
          * Note: must be constructed with lock held.
          */
-        UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
+        private UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
             mProvider = provider;
             mRealRequest = request;
             mRequest = request;
@@ -1958,7 +2073,7 @@
         /**
          * Method to be called when record changes foreground/background
          */
-        void updateForeground(boolean isForeground){
+        private void updateForeground(boolean isForeground) {
             mIsForegroundUid = isForeground;
             mRequestStatistics.updateForeground(
                     mReceiver.mIdentity.mPackageName, mProvider, isForeground);
@@ -1967,7 +2082,7 @@
         /**
          * Method to be called when a record will no longer be used.
          */
-        void disposeLocked(boolean removeReceiver) {
+        private void disposeLocked(boolean removeReceiver) {
             mRequestStatistics.stopRequesting(mReceiver.mIdentity.mPackageName, mProvider);
 
             // remove from mRecordsByProvider
@@ -1980,13 +2095,11 @@
 
             // remove from Receiver#mUpdateRecords
             HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
-            if (receiverRecords != null) {
-                receiverRecords.remove(this.mProvider);
+            receiverRecords.remove(this.mProvider);
 
-                // and also remove the Receiver if it has no more update records
-                if (receiverRecords.size() == 0) {
-                    removeUpdatesLocked(mReceiver);
-                }
+            // and also remove the Receiver if it has no more update records
+            if (receiverRecords.size() == 0) {
+                removeUpdatesLocked(mReceiver);
             }
         }
 
@@ -2070,7 +2183,7 @@
 
     private void checkPackageName(String packageName) {
         if (packageName == null) {
-            throw new SecurityException("invalid package name: " + packageName);
+            throw new SecurityException("invalid package name: " + null);
         }
         int uid = Binder.getCallingUid();
         String[] packages = mPackageManager.getPackagesForUid(uid);
@@ -2085,7 +2198,7 @@
 
     private void checkPendingIntent(PendingIntent intent) {
         if (intent == null) {
-            throw new IllegalArgumentException("invalid pending intent: " + intent);
+            throw new IllegalArgumentException("invalid pending intent: " + null);
         }
     }
 
@@ -2137,7 +2250,7 @@
             synchronized (mLock) {
                 Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
                         packageName, workSource, hideFromAppOps);
-                requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
+                requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -2145,7 +2258,7 @@
     }
 
     private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
-            int pid, int uid, String packageName) {
+            int uid, String packageName) {
         // Figure out the provider. Either its explicitly request (legacy use cases), or
         // use the fused provider
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2154,7 +2267,7 @@
             throw new IllegalArgumentException("provider name must not be null");
         }
 
-        LocationProviderInterface provider = mProvidersByName.get(name);
+        LocationProvider provider = mProvidersByName.get(name);
         if (provider == null) {
             throw new IllegalArgumentException("provider doesn't exist: " + name);
         }
@@ -2173,8 +2286,7 @@
             oldRecord.disposeLocked(false);
         }
 
-        boolean isProviderEnabled = isAllowedByUserSettingsLocked(name, uid, mCurrentUserId);
-        if (isProviderEnabled) {
+        if (provider.isEnabled()) {
             applyRequirementsLocked(name);
         } else {
             // Notify the listener that updates are currently disabled
@@ -2194,10 +2306,8 @@
         final int uid = Binder.getCallingUid();
 
         synchronized (mLock) {
-            WorkSource workSource = null;
-            boolean hideFromAppOps = false;
             Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
-                    packageName, workSource, hideFromAppOps);
+                    packageName, null, false);
 
             // providers may use public location API's, need to clear identity
             long identity = Binder.clearCallingIdentity();
@@ -2236,22 +2346,12 @@
 
         // update provider
         for (String provider : providers) {
-            // If provider is already disabled, don't need to do anything
-            if (!isAllowedByCurrentUserSettingsLocked(provider)) {
-                continue;
-            }
-
             applyRequirementsLocked(provider);
         }
     }
 
     private void applyAllProviderRequirementsLocked() {
-        for (LocationProviderInterface p : mProviders) {
-            // If provider is already disabled, don't need to do anything
-            if (!isAllowedByCurrentUserSettingsLocked(p.getName())) {
-                continue;
-            }
-
+        for (LocationProvider p : mProviders) {
             applyRequirementsLocked(p.getName());
         }
     }
@@ -2291,7 +2391,7 @@
                 // or use the fused provider
                 String name = request.getProvider();
                 if (name == null) name = LocationManager.FUSED_PROVIDER;
-                LocationProviderInterface provider = mProvidersByName.get(name);
+                LocationProvider provider = mProvidersByName.get(name);
                 if (provider == null) return null;
 
                 if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
@@ -2314,7 +2414,7 @@
                         location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
                 if ((locationAgeMs > mLastLocationMaxAgeMs)
                         && (mAppOps.unsafeCheckOp(op, uid, packageName)
-                            == AppOpsManager.MODE_FOREGROUND)) {
+                        == AppOpsManager.MODE_FOREGROUND)) {
                     return null;
                 }
 
@@ -2358,7 +2458,7 @@
             }
             return false;
         }
-        LocationProviderInterface p = null;
+        LocationProvider p = null;
         String provider = location.getProvider();
         if (provider != null) {
             p = mProvidersByName.get(provider);
@@ -2370,7 +2470,7 @@
             return false;
         }
         synchronized (mLock) {
-            if (!isAllowedByCurrentUserSettingsLocked(provider)) {
+            if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
                 if (D) {
                     Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
                 }
@@ -2444,31 +2544,20 @@
         }
     }
 
-
     @Override
     public boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName) {
         if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) {
             return false;
         }
 
-        try {
-            mGnssStatusProvider.registerGnssStatusCallback(callback);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "mGpsStatusProvider.registerGnssStatusCallback failed", e);
-            return false;
-        }
-        return true;
+        // TODO(b/120449926): The GNSS status listeners should be handled similar to the GNSS
+        // measurements listeners.
+        return mGnssStatusProvider.addListener(callback, Binder.getCallingUid(), packageName);
     }
 
     @Override
     public void unregisterGnssStatusCallback(IGnssStatusListener callback) {
-        synchronized (mLock) {
-            try {
-                mGnssStatusProvider.unregisterGnssStatusCallback(callback);
-            } catch (Exception e) {
-                Slog.e(TAG, "mGpsStatusProvider.unregisterGnssStatusCallback failed", e);
-            }
-        }
+        mGnssStatusProvider.removeListener(callback);
     }
 
     @Override
@@ -2481,13 +2570,15 @@
         synchronized (mLock) {
             Identity callerIdentity
                     = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+            // TODO(b/120481270): Register for client death notification and update map.
             mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
             long identity = Binder.clearCallingIdentity();
             try {
                 if (isThrottlingExemptLocked(callerIdentity)
                         || isImportanceForeground(
                         mActivityManager.getPackageImportance(packageName))) {
-                    return mGnssMeasurementsProvider.addListener(listener);
+                    return mGnssMeasurementsProvider.addListener(listener,
+                            callerIdentity.mUid, callerIdentity.mPackageName);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -2499,11 +2590,13 @@
 
     @Override
     public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
-        if (mGnssMeasurementsProvider != null) {
-            synchronized (mLock) {
-                mGnssMeasurementsListeners.remove(listener.asBinder());
-                mGnssMeasurementsProvider.removeListener(listener);
-            }
+        if (mGnssMeasurementsProvider == null) {
+            return;
+        }
+
+        synchronized (mLock) {
+            mGnssMeasurementsListeners.remove(listener.asBinder());
+            mGnssMeasurementsProvider.removeListener(listener);
         }
     }
 
@@ -2518,13 +2611,15 @@
         synchronized (mLock) {
             Identity callerIdentity
                     = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+            // TODO(b/120481270): Register for client death notification and update map.
             mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
             long identity = Binder.clearCallingIdentity();
             try {
                 if (isThrottlingExemptLocked(callerIdentity)
                         || isImportanceForeground(
                         mActivityManager.getPackageImportance(packageName))) {
-                    return mGnssNavigationMessageProvider.addListener(listener);
+                    return mGnssNavigationMessageProvider.addListener(listener,
+                            callerIdentity.mUid, callerIdentity.mPackageName);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -2560,10 +2655,11 @@
         }
 
         synchronized (mLock) {
-            LocationProviderInterface p = mProvidersByName.get(provider);
+            LocationProvider p = mProvidersByName.get(provider);
             if (p == null) return false;
 
-            return p.sendExtraCommand(command, extras);
+            p.sendExtraCommand(command, extras);
+            return true;
         }
     }
 
@@ -2588,14 +2684,10 @@
      */
     @Override
     public ProviderProperties getProviderProperties(String provider) {
-        if (mProvidersByName.get(provider) == null) {
-            return null;
-        }
-
         checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
                 provider);
 
-        LocationProviderInterface p;
+        LocationProvider p;
         synchronized (mLock) {
             p = mProvidersByName.get(provider);
         }
@@ -2611,25 +2703,25 @@
      */
     @Override
     public String getNetworkProviderPackage() {
-        LocationProviderInterface p;
+        LocationProvider p;
         synchronized (mLock) {
-            if (mProvidersByName.get(LocationManager.NETWORK_PROVIDER) == null) {
-                return null;
-            }
             p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
         }
 
-        if (p instanceof LocationProviderProxy) {
-            return ((LocationProviderProxy) p).getConnectedPackageName();
+        if (p == null) {
+            return null;
+        }
+        if (p.mProvider instanceof LocationProviderProxy) {
+            return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
         }
         return null;
     }
 
     /**
-     *  Returns the current location enabled/disabled status for a user
+     * Returns the current location enabled/disabled status for a user
      *
-     *  @param userId the id of the user
-     *  @return true if location is enabled
+     * @param userId the id of the user
+     * @return true if location is enabled
      */
     @Override
     public boolean isLocationEnabledForUser(int userId) {
@@ -2647,7 +2739,7 @@
                     return false;
                 }
                 final List<String> providerList = Arrays.asList(allowedProviders.split(","));
-                for(String provider : mRealProviders.keySet()) {
+                for (String provider : mRealProviders.keySet()) {
                     if (provider.equals(LocationManager.PASSIVE_PROVIDER)
                             || provider.equals(LocationManager.FUSED_PROVIDER)) {
                         continue;
@@ -2664,16 +2756,16 @@
     }
 
     /**
-     *  Enable or disable location for a user
+     * Enable or disable location for a user
      *
-     *  @param enabled true to enable location, false to disable location
-     *  @param userId the id of the user
+     * @param enabled true to enable location, false to disable location
+     * @param userId  the id of the user
      */
     @Override
     public void setLocationEnabledForUser(boolean enabled, int userId) {
         mContext.enforceCallingPermission(
-            android.Manifest.permission.WRITE_SECURE_SETTINGS,
-            "Requires WRITE_SECURE_SETTINGS permission");
+                android.Manifest.permission.WRITE_SECURE_SETTINGS,
+                "Requires WRITE_SECURE_SETTINGS permission");
 
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
         checkInteractAcrossUsersPermission(userId);
@@ -2688,7 +2780,7 @@
                 allProvidersSet.addAll(allRealProviders);
                 // When disabling location, disable gps and network provider that could have been
                 // enabled by location mode api.
-                if (enabled == false) {
+                if (!enabled) {
                     allProvidersSet.add(LocationManager.GPS_PROVIDER);
                     allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
                 }
@@ -2723,26 +2815,34 @@
     }
 
     /**
-     *  Returns the current enabled/disabled status of a location provider and user
+     * Returns the current enabled/disabled status of a location provider and user
      *
-     *  @param provider name of the provider
-     *  @param userId the id of the user
-     *  @return true if the provider exists and is enabled
+     * @param providerName name of the provider
+     * @param userId       the id of the user
+     * @return true if the provider exists and is enabled
      */
     @Override
-    public boolean isProviderEnabledForUser(String provider, int userId) {
+    public boolean isProviderEnabledForUser(String providerName, int userId) {
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
         checkInteractAcrossUsersPermission(userId);
 
+        if (!isLocationEnabledForUser(userId)) {
+            return false;
+        }
+
         // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
         // so we discourage its use
-        if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
+        if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false;
 
-        int uid = Binder.getCallingUid();
-        synchronized (mLock) {
-            LocationProviderInterface p = mProvidersByName.get(provider);
-            return p != null
-                    && isAllowedByUserSettingsLocked(provider, uid, userId);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            LocationProvider provider;
+            synchronized (mLock) {
+                provider = mProvidersByName.get(providerName);
+            }
+            return provider != null && provider.isEnabled();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -2750,66 +2850,13 @@
      * Enable or disable a single location provider.
      *
      * @param provider name of the provider
-     * @param enabled true to enable the provider. False to disable the provider
-     * @param userId the id of the user to set
+     * @param enabled  true to enable the provider. False to disable the provider
+     * @param userId   the id of the user to set
      * @return true if the value was set, false on errors
      */
     @Override
     public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS,
-                "Requires WRITE_SECURE_SETTINGS permission");
-
-        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
-        // so we discourage its use
-        if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                // No such provider exists
-                if (!mProvidersByName.containsKey(provider)) return false;
-
-                // If it is a test provider, do not write to Settings.Secure
-                if (mMockProviders.containsKey(provider)) {
-                    setTestProviderEnabled(provider, enabled);
-                    return true;
-                }
-
-                // to ensure thread safety, we write the provider name with a '+' or '-'
-                // and let the SettingsProvider handle it rather than reading and modifying
-                // the list of enabled providers.
-                String providerChange = (enabled ? "+" : "-") + provider;
-                return Settings.Secure.putStringForUser(
-                        mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        providerChange, userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
-     * Read location provider status from Settings.Secure
-     *
-     * @param provider the location provider to query
-     * @param userId the user id to query
-     * @return true if the provider is enabled
-     */
-    private boolean isLocationProviderEnabledForUser(String provider, int userId) {
-        long identity = Binder.clearCallingIdentity();
-        try {
-            // Use system settings
-            ContentResolver cr = mContext.getContentResolver();
-            String allowedProviders = Settings.Secure.getStringForUser(
-                    cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
-            return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+        return false;
     }
 
     /**
@@ -2941,7 +2988,7 @@
         long now = SystemClock.elapsedRealtime();
         String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
         // Skip if the provider is unknown.
-        LocationProviderInterface p = mProvidersByName.get(provider);
+        LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
         updateLastLocationLocked(location, provider);
         // mLastLocation should have been updated from the updateLastLocationLocked call above.
@@ -3053,7 +3100,7 @@
                     LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
                 long prevStatusUpdateTime = r.mLastStatusBroadcast;
                 if ((newStatusUpdateTime > prevStatusUpdateTime)
-                        && (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
+                        && (prevStatusUpdateTime != 0 || status != AVAILABLE)) {
 
                     r.mLastStatusBroadcast = newStatusUpdateTime;
                     if (!receiver.callStatusChangedLocked(provider, status, extras)) {
@@ -3098,8 +3145,8 @@
     /**
      * Updates last location with the given location
      *
-     * @param location             new location to update
-     * @param provider             Location provider to update for
+     * @param location new location to update
+     * @param provider Location provider to update for
      */
     private void updateLastLocationLocked(Location location, String provider) {
         Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
@@ -3154,13 +3201,11 @@
         }
 
         synchronized (mLock) {
-            if (isAllowedByCurrentUserSettingsLocked(provider)) {
-                if (!passive) {
-                    // notify passive provider of the new location
-                    mPassiveProvider.updateLocation(myLocation);
-                }
-                handleLocationChangedLocked(myLocation, passive);
+            if (!passive) {
+                // notify passive provider of the new location
+                mPassiveProvider.updateLocation(myLocation);
             }
+            handleLocationChangedLocked(myLocation, passive);
         }
     }
 
@@ -3245,13 +3290,12 @@
             if (LocationManager.GPS_PROVIDER.equals(name)
                     || LocationManager.NETWORK_PROVIDER.equals(name)
                     || LocationManager.FUSED_PROVIDER.equals(name)) {
-                LocationProviderInterface p = mProvidersByName.get(name);
+                LocationProvider p = mProvidersByName.get(name);
                 if (p != null) {
                     removeProviderLocked(p);
                 }
             }
             addTestProviderLocked(name, properties);
-            updateProvidersLocked();
         }
         Binder.restoreCallingIdentity(identity);
     }
@@ -3260,9 +3304,12 @@
         if (mProvidersByName.get(name) != null) {
             throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
         }
-        MockProvider provider = new MockProvider(name, this, properties);
+
+        LocationProvider provider = new LocationProvider(name);
+        MockProvider mockProvider = new MockProvider(provider, properties);
+
         addProviderLocked(provider);
-        mMockProviders.put(name, provider);
+        mMockProviders.put(name, mockProvider);
         mLastLocation.put(name, null);
         mLastLocationCoarseInterval.put(name, null);
     }
@@ -3274,28 +3321,25 @@
         }
 
         synchronized (mLock) {
-
-            // These methods can't be called after removing the test provider, so first make sure
-            // we don't leave anything dangling.
-            clearTestProviderEnabled(provider, opPackageName);
-            clearTestProviderLocation(provider, opPackageName);
-
             MockProvider mockProvider = mMockProviders.remove(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
-            long identity = Binder.clearCallingIdentity();
-            removeProviderLocked(mProvidersByName.get(provider));
 
-            // reinstate real provider if available
-            LocationProviderInterface realProvider = mRealProviders.get(provider);
-            if (realProvider != null) {
-                addProviderLocked(realProvider);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                removeProviderLocked(mProvidersByName.get(provider));
+
+                // reinstate real provider if available
+                LocationProvider realProvider = mRealProviders.get(provider);
+                if (realProvider != null) {
+                    addProviderLocked(realProvider);
+                }
+                mLastLocation.put(provider, null);
+                mLastLocationCoarseInterval.put(provider, null);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-            mLastLocation.put(provider, null);
-            mLastLocationCoarseInterval.put(provider, null);
-            updateProvidersLocked();
-            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -3325,23 +3369,11 @@
 
             // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
             long identity = Binder.clearCallingIdentity();
-            mockProvider.setLocation(mock);
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void clearTestProviderLocation(String provider, String opPackageName) {
-        if (!canCallerAccessMockLocation(opPackageName)) {
-            return;
-        }
-
-        synchronized (mLock) {
-            MockProvider mockProvider = mMockProviders.get(provider);
-            if (mockProvider == null) {
-                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
+            try {
+                mockProvider.setLocation(mock);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-            mockProvider.clearLocation();
         }
     }
 
@@ -3350,36 +3382,6 @@
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
-        setTestProviderEnabled(provider, enabled);
-    }
-
-    /** Enable or disable a test location provider. */
-    private void setTestProviderEnabled(String provider, boolean enabled) {
-        synchronized (mLock) {
-            MockProvider mockProvider = mMockProviders.get(provider);
-            if (mockProvider == null) {
-                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
-            }
-            long identity = Binder.clearCallingIdentity();
-            if (enabled) {
-                mockProvider.enable();
-                mEnabledProviders.add(provider);
-                mDisabledProviders.remove(provider);
-            } else {
-                mockProvider.disable();
-                mEnabledProviders.remove(provider);
-                mDisabledProviders.add(provider);
-            }
-            updateProvidersLocked();
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void clearTestProviderEnabled(String provider, String opPackageName) {
-        if (!canCallerAccessMockLocation(opPackageName)) {
-            return;
-        }
 
         synchronized (mLock) {
             MockProvider mockProvider = mMockProviders.get(provider);
@@ -3387,10 +3389,11 @@
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
             long identity = Binder.clearCallingIdentity();
-            mEnabledProviders.remove(provider);
-            mDisabledProviders.remove(provider);
-            updateProvidersLocked();
-            Binder.restoreCallingIdentity(identity);
+            try {
+                mockProvider.setEnabled(enabled);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -3450,10 +3453,11 @@
                         + identity.mPackageName + ": " + isThrottlingExemptLocked(identity));
             }
             pw.println("  Overlay Provider Packages:");
-            for (LocationProviderInterface provider : mProviders) {
-                if (provider instanceof LocationProviderProxy) {
+            for (LocationProvider provider : mProviders) {
+                if (provider.mProvider instanceof LocationProviderProxy) {
                     pw.println("    " + provider.getName() + ": "
-                            + ((LocationProviderProxy) provider).getConnectedPackageName());
+                            + ((LocationProviderProxy) provider.mProvider)
+                            .getConnectedPackageName());
                 }
             }
             pw.println("  Historical Records by Provider:");
@@ -3479,25 +3483,12 @@
 
             mGeofenceManager.dump(pw);
 
-            if (mEnabledProviders.size() > 0) {
-                pw.println("  Enabled Providers:");
-                for (String i : mEnabledProviders) {
-                    pw.println("    " + i);
-                }
-
-            }
-            if (mDisabledProviders.size() > 0) {
-                pw.println("  Disabled Providers:");
-                for (String i : mDisabledProviders) {
-                    pw.println("    " + i);
-                }
-            }
             pw.append("  ");
             mBlacklist.dump(pw);
             if (mMockProviders.size() > 0) {
                 pw.println("  Mock Providers:");
                 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
-                    i.getValue().dump(pw, "      ");
+                    i.getValue().dump(fd, pw, args);
                 }
             }
 
@@ -3514,13 +3505,7 @@
             if (args.length > 0 && "short".equals(args[0])) {
                 return;
             }
-            for (LocationProviderInterface provider : mProviders) {
-                pw.print(provider.getName() + " Internal State");
-                if (provider instanceof LocationProviderProxy) {
-                    LocationProviderProxy proxy = (LocationProviderProxy) provider;
-                    pw.print(" (" + proxy.getConnectedPackageName() + ")");
-                }
-                pw.println(":");
+            for (LocationProvider provider : mProviders) {
                 provider.dump(fd, pw, args);
             }
             if (mGnssBatchingInProgress) {
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 574f54a..d09823e 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -16,9 +16,7 @@
 
 package com.android.server;
 
-import android.annotation.MainThread;
 import android.annotation.Nullable;
-import android.annotation.WorkerThread;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -168,13 +166,13 @@
     // called on handler thread
     @GuardedBy("mBindLock")
     protected void onBind() {
-
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
     }
 
     // called on handler thread
     @GuardedBy("mBindLock")
     protected void onUnbind() {
-
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
     }
 
     /**
@@ -205,12 +203,12 @@
 
                 @Override
                 public void onPackageRemoved(String packageName, int uid) {
-                        bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+                    bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
                 }
 
                 @Override
                 public boolean onPackageChanged(String packageName, int uid, String[] components) {
-                        bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+                    bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
                     return super.onPackageChanged(packageName, uid, components);
                 }
             }.register(mContext, UserHandle.ALL, true, mHandler);
@@ -243,20 +241,16 @@
         return true;
     }
 
-    /** Returns thje name of the currently connected package or null. */
+    /** Returns the name of the currently connected package or null. */
     @Nullable
     public String getCurrentPackageName() {
         ComponentName bestComponent = mBestComponent;
         return bestComponent == null ? null : bestComponent.getPackageName();
     }
 
-    public int getCurrentPackageVersion() {
-        return mBestVersion;
-    }
-
     /**
-     * Runs the given BinderRunner if currently connected. Returns true if it was run, and false
-     * otherwise. All invocations to runOnBinder are run serially.
+     * Runs the given BinderRunner if currently connected. All invocations to runOnBinder are run
+     * serially.
      */
     public final void runOnBinder(BinderRunner runner) {
         synchronized (mBindLock) {
@@ -267,7 +261,7 @@
                 } catch (Exception e) {
                     // remote exceptions cannot be allowed to crash system server
                     Log.e(TAG, "exception while while running " + runner + " on " + service
-                            + " from " + mBestComponent.toShortString(), e);
+                            + " from " + this, e);
                 }
             }
         }
@@ -280,14 +274,6 @@
                 UserHandle.USER_SYSTEM).isEmpty();
     }
 
-    /**
-     * Searches and binds to the best package, or do nothing if the best package
-     * is already bound, unless force rebinding is requested.
-     *
-     * @param forceRebind          Force a rebinding to the best package if it's already
-     *                             bound.
-     */
-    @WorkerThread
     private void bindBestPackage(boolean forceRebind) {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
@@ -365,7 +351,6 @@
         }
     }
 
-    @WorkerThread
     private void bind(ComponentName component, int version, int userId) {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
@@ -382,7 +367,6 @@
                 UserHandle.of(userId));
     }
 
-    @WorkerThread
     private void unbind() {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
@@ -396,7 +380,6 @@
         mBestUserId = UserHandle.USER_NULL;
     }
 
-    @MainThread
     @Override
     public final void onServiceConnected(ComponentName component, IBinder binder) {
         mHandler.post(() -> {
@@ -410,7 +393,6 @@
         });
     }
 
-    @MainThread
     @Override
     public final void onServiceDisconnected(ComponentName component) {
         mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a2cbfaa..b04ae17 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -67,7 +67,9 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 
 /**
@@ -196,6 +198,8 @@
 
     private ArrayList<List<PhysicalChannelConfig>> mPhysicalChannelConfigs;
 
+    private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList;
+
     private int[] mSrvccState;
 
     private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -229,8 +233,9 @@
                     | PhoneStateListener.LISTEN_CELL_INFO;
 
     static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
-                PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
-                PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR;
+                PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
+                        | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+                        | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
 
     static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
@@ -357,6 +362,7 @@
         mCellInfo = new ArrayList<List<CellInfo>>();
         mSrvccState = new int[numPhones];
         mPhysicalChannelConfigs = new ArrayList<List<PhysicalChannelConfig>>();
+        mEmergencyNumberList = new HashMap<>();
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
             mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -752,6 +758,13 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
+                        try {
+                            r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                     if ((events & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
                         try {
                             r.callback.onPhoneCapabilityChanged(mPhoneCapability);
@@ -1665,10 +1678,30 @@
     }
 
     @Override
-    public void notifyEmergencyNumberList(List<EmergencyNumber> emergencyNumberList) {
-        // TODO checkPermission, modify Listener constent documentation
-        // TODO implement multisim emergency number list update in listener
-        // TODO implement PhoneStateListenerTest
+    public void notifyEmergencyNumberList() {
+        if (!checkNotifyPermission("notifyEmergencyNumberList()")) {
+            return;
+        }
+
+        synchronized (mRecords) {
+            mEmergencyNumberList = TelephonyManager.getDefault().getCurrentEmergencyNumberList();
+
+            for (Record r : mRecords) {
+                if (r.matchPhoneStateListenerEvent(
+                        PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST)) {
+                    try {
+                        r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
+                        if (VDBG) {
+                            log("notifyEmergencyNumberList: emergencyNumberList= "
+                                    + mEmergencyNumberList);
+                        }
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
     }
 
 
@@ -1710,6 +1743,7 @@
             pw.println("mPhoneCapability=" + mPhoneCapability);
             pw.println("mPreferredDataSubId=" + mPreferredDataSubId);
             pw.println("mRadioPowerState=" + mRadioPowerState);
+            pw.println("mEmergencyNumberList=" + mEmergencyNumberList);
 
             pw.decreaseIndent();
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 23287cf..f7acf7e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1205,10 +1205,20 @@
                                 android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
                                 r.app.pid, r.appInfo.uid, "startForeground");
                 }
-            } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
-                mAm.enforcePermission(
-                        android.Manifest.permission.FOREGROUND_SERVICE,
-                        r.app.pid, r.appInfo.uid, "startForeground");
+            } else {
+                if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
+                    mAm.enforcePermission(
+                            android.Manifest.permission.FOREGROUND_SERVICE,
+                            r.app.pid, r.appInfo.uid, "startForeground");
+                }
+                if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
+                    if (r.serviceInfo.getForegroundServiceType()
+                            == ServiceInfo.FOREGROUND_SERVICE_TYPE_UNSPECIFIED) {
+                        // STOPSHIP(b/120611119): replace log message with SecurityException.
+                        Slog.w(TAG, "missing foregroundServiceType attribute in "
+                                + "service element of manifest file");
+                    }
+                }
             }
             boolean alreadyStartedOp = false;
             boolean stopProcStatsOp = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c16f1db..40da881 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1199,8 +1199,8 @@
         void setProfileProc(ProcessRecord profileProc) {
             mProfileProc = profileProc;
             if (mAtmInternal != null) {
-                mAtmInternal.setProfileProc(
-                        profileProc.getWindowProcessController());
+                mAtmInternal.setProfileProc(profileProc == null ? null
+                        : profileProc.getWindowProcessController());
             }
         }
 
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 9cfd39c..65cd329 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -55,7 +55,12 @@
         // add other system settings here...
 
         sGlobalSettingToTypeMap.put(Settings.Global.DEBUG_VIEW_ATTRIBUTES, int.class);
-        sGlobalSettingToTypeMap.put(Settings.Global.ANGLE_ENABLED_APP, String.class);
+        sGlobalSettingToTypeMap.put(
+                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE, String.class);
+        sGlobalSettingToTypeMap.put(
+                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, String.class);
+        sGlobalSettingToTypeMap.put(
+                Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 3c14393..d75601b 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -944,10 +944,11 @@
     public boolean hasTetherableConfiguration() {
         final TetheringConfiguration cfg = mConfig;
         final boolean hasDownstreamConfiguration =
-                (cfg.tetherableUsbRegexs.length != 0) ||
-                (cfg.tetherableWifiRegexs.length != 0) ||
-                (cfg.tetherableBluetoothRegexs.length != 0);
-        final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty();
+                (cfg.tetherableUsbRegexs.length != 0)
+                || (cfg.tetherableWifiRegexs.length != 0)
+                || (cfg.tetherableBluetoothRegexs.length != 0);
+        final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty()
+                || cfg.chooseUpstreamAutomatically;
 
         return hasDownstreamConfiguration && hasUpstreamConfiguration;
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index b7ed2f9..602aedb 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -206,45 +206,6 @@
     // Handle of the user initiating VPN.
     private final int mUserHandle;
 
-    // Listen to package removal and change events (update/uninstall) for this user
-    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final Uri data = intent.getData();
-            final String packageName = data == null ? null : data.getSchemeSpecificPart();
-            if (packageName == null) {
-                return;
-            }
-
-            synchronized (Vpn.this) {
-                // Avoid race where always-on package has been unset
-                if (!packageName.equals(getAlwaysOnPackage())) {
-                    return;
-                }
-
-                final String action = intent.getAction();
-                Log.i(TAG, "Received broadcast " + action + " for always-on VPN package "
-                        + packageName + " in user " + mUserHandle);
-
-                switch(action) {
-                    case Intent.ACTION_PACKAGE_REPLACED:
-                        // Start vpn after app upgrade
-                        startAlwaysOnVpn();
-                        break;
-                    case Intent.ACTION_PACKAGE_REMOVED:
-                        final boolean isPackageRemoved = !intent.getBooleanExtra(
-                                Intent.EXTRA_REPLACING, false);
-                        if (isPackageRemoved) {
-                            setAlwaysOnPackage(null, false);
-                        }
-                        break;
-                }
-            }
-        }
-    };
-
-    private boolean mIsPackageIntentReceiverRegistered = false;
-
     public Vpn(Looper looper, Context context, INetworkManagementService netService,
             @UserIdInt int userHandle) {
         this(looper, context, netService, userHandle, new SystemServices(context));
@@ -500,7 +461,6 @@
             // Prepare this app. The notification will update as a side-effect of updateState().
             prepareInternal(packageName);
         }
-        maybeRegisterPackageChangeReceiverLocked(packageName);
         setVpnForcedLocked(mLockdown);
         return true;
     }
@@ -509,31 +469,6 @@
         return packageName == null || VpnConfig.LEGACY_VPN.equals(packageName);
     }
 
-    private void unregisterPackageChangeReceiverLocked() {
-        if (mIsPackageIntentReceiverRegistered) {
-            mContext.unregisterReceiver(mPackageIntentReceiver);
-            mIsPackageIntentReceiverRegistered = false;
-        }
-    }
-
-    private void maybeRegisterPackageChangeReceiverLocked(String packageName) {
-        // Unregister IntentFilter listening for previous always-on package change
-        unregisterPackageChangeReceiverLocked();
-
-        if (!isNullOrLegacyVpn(packageName)) {
-            mIsPackageIntentReceiverRegistered = true;
-
-            IntentFilter intentFilter = new IntentFilter();
-            // Protected intent can only be sent by system. No permission required in register.
-            intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
-            intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-            intentFilter.addDataScheme("package");
-            intentFilter.addDataSchemeSpecificPart(packageName, PatternMatcher.PATTERN_LITERAL);
-            mContext.registerReceiverAsUser(
-                    mPackageIntentReceiver, UserHandle.of(mUserHandle), intentFilter, null, null);
-        }
-    }
-
     /**
      * @return the package name of the VPN controller responsible for always-on VPN,
      *         or {@code null} if none is set or always-on VPN is controlled through
@@ -1302,7 +1237,6 @@
         setLockdown(false);
         mAlwaysOn = false;
 
-        unregisterPackageChangeReceiverLocked();
         // Quit any active connections
         agentDisconnect();
     }
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index 5ca1755..4ad26da 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -23,7 +23,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -68,7 +67,7 @@
      * SurfaceFlinger display color (managed, unmanaged, etc.).
      */
     private static final int SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR = 1023;
-    private static final int SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR = 1030;
+    private static final int SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED = 1030;
 
     private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
     private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode";
@@ -270,8 +269,8 @@
     }
 
     /**
-     * Returns whether the screen is wide color gamut via SurfaceFlinger's
-     * {@link #SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR}.
+     * Returns whether the screen is color managed via SurfaceFlinger's
+     * {@link #SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED}.
      */
     public boolean isDeviceColorManaged() {
         final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
@@ -280,10 +279,10 @@
             final Parcel reply = Parcel.obtain();
             data.writeInterfaceToken("android.ui.ISurfaceComposer");
             try {
-                flinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR, data, reply, 0);
+                flinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED, data, reply, 0);
                 return reply.readBoolean();
             } catch (RemoteException ex) {
-                Log.e(TAG, "Failed to query wide color support", ex);
+                Slog.e(TAG, "Failed to query wide color support", ex);
             } finally {
                 data.recycle();
                 reply.recycle();
@@ -305,7 +304,7 @@
             try {
                 flinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0);
             } catch (RemoteException ex) {
-                Log.e(TAG, "Failed to set saturation", ex);
+                Slog.e(TAG, "Failed to set saturation", ex);
             } finally {
                 data.recycle();
             }
@@ -325,7 +324,7 @@
             try {
                 flinger.transact(SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR, data, null, 0);
             } catch (RemoteException ex) {
-                Log.e(TAG, "Failed to set display color", ex);
+                Slog.e(TAG, "Failed to set display color", ex);
             } finally {
                 data.recycle();
             }
@@ -336,7 +335,7 @@
         try {
             ActivityTaskManager.getService().updateConfiguration(null);
         } catch (RemoteException e) {
-            Log.e(TAG, "Could not update configuration", e);
+            Slog.e(TAG, "Could not update configuration", e);
         }
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index de0f298..25ca278 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1082,13 +1082,14 @@
         assertRunOnServiceThread();
 
         if (!canStartArcUpdateAction(message.getSource(), true)) {
-            if (getAvrDeviceInfo() == null) {
+            HdmiDeviceInfo avrDeviceInfo = getAvrDeviceInfo();
+            if (avrDeviceInfo == null) {
                 // AVR may not have been discovered yet. Delay the message processing.
                 mDelayedMessageBuffer.add(message);
                 return true;
             }
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
-            if (!isConnectedToArcPort(message.getSource())) {
+            if (!isConnectedToArcPort(avrDeviceInfo.getPhysicalAddress())) {
                 displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT);
             }
             return true;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d96b6cb..e7c3c7b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1951,6 +1951,11 @@
     }
 
     // Native callback.
+    private int getPointerDisplayId() {
+        return mWindowManagerCallbacks.getPointerDisplayId();
+    }
+
+    // Native callback.
     private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
         if (!mSystemReady) {
             return null;
@@ -2017,6 +2022,8 @@
                 KeyEvent event, int policyFlags);
 
         public int getPointerLayer();
+
+        public int getPointerDisplayId();
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a8da968..28a6ba4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -984,9 +984,10 @@
                 // {@link #canShowInputMethodPickerLocked(IInputMethodClient)}.
                 mHandler.obtainMessage(
                         MSG_SHOW_IM_SUBTYPE_PICKER,
+                        // TODO(b/120076400): Design and implement IME switcher for heterogeneous
+                        // navbar configuration.
                         InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES,
-                        0 /* arg2 */)
-                        .sendToTarget();
+                        DEFAULT_DISPLAY).sendToTarget();
             } else {
                 Slog.w(TAG, "Unexpected intent " + intent);
             }
@@ -1617,7 +1618,8 @@
     // Check whether or not this is a valid IPC. Assumes an IPC is valid when either
     // 1) it comes from the system process
     // 2) the calling process' user id is identical to the current user id IMMS thinks.
-    private boolean calledFromValidUser() {
+    @GuardedBy("mMethodMap")
+    private boolean calledFromValidUserLocked() {
         final int uid = Binder.getCallingUid();
         final int userId = UserHandle.getUserId(uid);
         if (DEBUG) {
@@ -1657,7 +1659,8 @@
      * @param token The window token given to the input method when it was started.
      * @return true if and only if non-null valid token is specified.
      */
-    private boolean calledWithValidToken(@Nullable IBinder token) {
+    @GuardedBy("mMethodMap")
+    private boolean calledWithValidTokenLocked(@Nullable IBinder token) {
         if (token == null && Binder.getCallingPid() == Process.myPid()) {
             if (DEBUG) {
                 // TODO(b/34851776): Basically it's the caller's fault if we reach here.
@@ -1698,11 +1701,11 @@
     }
 
     private List<InputMethodInfo> getInputMethodList(final boolean isVrOnly) {
-        // TODO: Make this work even for non-current users?
-        if (!calledFromValidUser()) {
-            return Collections.emptyList();
-        }
         synchronized (mMethodMap) {
+            // TODO: Make this work even for non-current users?
+            if (!calledFromValidUserLocked()) {
+                return Collections.emptyList();
+            }
             ArrayList<InputMethodInfo> methodList = new ArrayList<>();
             for (InputMethodInfo info : mMethodList) {
 
@@ -1716,11 +1719,11 @@
 
     @Override
     public List<InputMethodInfo> getEnabledInputMethodList() {
-        // TODO: Make this work even for non-current users?
-        if (!calledFromValidUser()) {
-            return Collections.emptyList();
-        }
         synchronized (mMethodMap) {
+            // TODO: Make this work even for non-current users?
+            if (!calledFromValidUserLocked()) {
+                return Collections.emptyList();
+            }
             return mSettings.getEnabledInputMethodListLocked();
         }
     }
@@ -1732,11 +1735,11 @@
     @Override
     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
             boolean allowsImplicitlySelectedSubtypes) {
-        // TODO: Make this work even for non-current users?
-        if (!calledFromValidUser()) {
-            return Collections.emptyList();
-        }
         synchronized (mMethodMap) {
+            // TODO: Make this work even for non-current users?
+            if (!calledFromValidUserLocked()) {
+                return Collections.emptyList();
+            }
             final InputMethodInfo imi;
             if (imiId == null && mCurMethodId != null) {
                 imi = mMethodMap.get(mCurMethodId);
@@ -2238,7 +2241,7 @@
     private void updateStatusIcon(@NonNull IBinder token, String packageName,
             @DrawableRes int iconId) {
         synchronized (mMethodMap) {
-            if (!calledWithValidToken(token)) {
+            if (!calledWithValidTokenLocked(token)) {
                 return;
             }
             final long ident = Binder.clearCallingIdentity();
@@ -2341,11 +2344,10 @@
     @BinderThread
     @SuppressWarnings("deprecation")
     private void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
-        if (!calledWithValidToken(token)) {
-            return;
-        }
-
         synchronized (mMethodMap) {
+            if (!calledWithValidTokenLocked(token)) {
+                return;
+            }
             mImeWindowVis = vis;
             mBackDisposition = backDisposition;
             updateSystemUiLocked(token, vis, backDisposition);
@@ -2376,11 +2378,10 @@
 
     @BinderThread
     private void reportStartInput(IBinder token, IBinder startInputToken) {
-        if (!calledWithValidToken(token)) {
-            return;
-        }
-
         synchronized (mMethodMap) {
+            if (!calledWithValidTokenLocked(token)) {
+                return;
+            }
             final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
             if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
                 mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
@@ -2391,7 +2392,7 @@
 
     // Caution! This method is called in this class. Handle multi-user carefully
     private void updateSystemUiLocked(IBinder token, int vis, int backDisposition) {
-        if (!calledWithValidToken(token)) {
+        if (!calledWithValidTokenLocked(token)) {
             return;
         }
 
@@ -2450,10 +2451,10 @@
 
     @Override
     public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
-        if (!calledFromValidUser()) {
-            return;
-        }
         synchronized (mMethodMap) {
+            if (!calledFromValidUserLocked()) {
+                return;
+            }
             final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
             for (int i = 0; i < spans.length; ++i) {
                 SuggestionSpan ss = spans[i];
@@ -2466,10 +2467,10 @@
 
     @Override
     public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
-        if (!calledFromValidUser()) {
-            return false;
-        }
         synchronized (mMethodMap) {
+            if (!calledFromValidUserLocked()) {
+                return false;
+            }
             final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span);
             // TODO: Do not send the intent if the process of the targetImi is already dead.
             if (targetImi != null) {
@@ -2633,13 +2634,13 @@
     @Override
     public boolean showSoftInput(IInputMethodClient client, int flags,
             ResultReceiver resultReceiver) {
-        if (!calledFromValidUser()) {
-            return false;
-        }
         int uid = Binder.getCallingUid();
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mMethodMap) {
+        synchronized (mMethodMap) {
+            if (!calledFromValidUserLocked()) {
+                return false;
+            }
+            final long ident = Binder.clearCallingIdentity();
+            try {
                 if (mCurClient == null || client == null
                         || mCurClient.client.asBinder() != client.asBinder()) {
                     // We need to check if this is the current client with
@@ -2657,9 +2658,9 @@
                 }
                 if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
                 return showCurrentInputLocked(flags, resultReceiver);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
             }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -2718,13 +2719,13 @@
     @Override
     public boolean hideSoftInput(IInputMethodClient client, int flags,
             ResultReceiver resultReceiver) {
-        if (!calledFromValidUser()) {
-            return false;
-        }
         int uid = Binder.getCallingUid();
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mMethodMap) {
+        synchronized (mMethodMap) {
+            if (!calledFromValidUserLocked()) {
+                return false;
+            }
+            final long ident = Binder.clearCallingIdentity();
+            try {
                 if (mCurClient == null || client == null
                         || mCurClient.client.asBinder() != client.asBinder()) {
                     // We need to check if this is the current client with
@@ -2745,9 +2746,9 @@
 
                 if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
                 return hideCurrentInputLocked(flags, resultReceiver);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
             }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -2827,14 +2828,14 @@
             @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
             IInputContext inputContext, @MissingMethodFlags int missingMethods,
             int unverifiedTargetSdkVersion) {
-        // Needs to check the validity before clearing calling identity
-        final boolean calledFromValidUser = calledFromValidUser();
         InputBindResult res = null;
-        long ident = Binder.clearCallingIdentity();
-        try {
+        synchronized (mMethodMap) {
+            // Needs to check the validity before clearing calling identity
+            final boolean calledFromValidUser = calledFromValidUserLocked();
             final int windowDisplayId =
                     mWindowManagerInternal.getDisplayIdForWindow(windowToken);
-            synchronized (mMethodMap) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
                 if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason="
                         + InputMethodDebug.startInputReasonToString(startInputReason)
                         + " client=" + client.asBinder()
@@ -3023,9 +3024,9 @@
                         res = InputBindResult.NULL_EDITOR_INFO;
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
             }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
         }
 
         return res;
@@ -3034,9 +3035,7 @@
     private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
         // TODO(yukawa): multi-display support.
         final int uid = Binder.getCallingUid();
-        if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
-            return true;
-        } else if (mCurFocusedWindowClient != null && client != null
+        if (mCurFocusedWindowClient != null && client != null
                 && mCurFocusedWindowClient.client.asBinder() == client.asBinder()) {
             return true;
         } else if (mCurIntent != null && InputMethodUtils.checkIfPackageBelongsToUid(
@@ -3044,22 +3043,17 @@
                 uid,
                 mCurIntent.getComponent().getPackageName())) {
             return true;
-        } else if (mContext.checkCallingPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS)
-                == PackageManager.PERMISSION_GRANTED) {
-            return true;
         }
-
         return false;
     }
 
     @Override
     public void showInputMethodPickerFromClient(
             IInputMethodClient client, int auxiliarySubtypeMode) {
-        if (!calledFromValidUser()) {
-            return;
-        }
         synchronized (mMethodMap) {
+            if (!calledFromValidUserLocked()) {
+                return;
+            }
             if(!canShowInputMethodPickerLocked(client)) {
                 Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid "
                         + Binder.getCallingUid() + ": " + client);
@@ -3068,11 +3062,26 @@
 
             // Always call subtype picker, because subtype picker is a superset of input method
             // picker.
-            mHandler.sendMessage(mCaller.obtainMessageI(
-                    MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode));
+            mHandler.sendMessage(mCaller.obtainMessageII(
+                    MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode,
+                    (mCurClient != null) ? mCurClient.selfReportedDisplayId : DEFAULT_DISPLAY));
         }
     }
 
+    @Override
+    public void showInputMethodPickerFromSystem(IInputMethodClient client, int auxiliarySubtypeMode,
+            int displayId) {
+        if (mContext.checkCallingPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "showInputMethodPickerFromSystem requires WRITE_SECURE_SETTINGS permission");
+        }
+        // Always call subtype picker, because subtype picker is a superset of input method
+        // picker.
+        mHandler.sendMessage(mCaller.obtainMessageII(
+                MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode, displayId));
+    }
+
     public boolean isInputMethodPickerShownForTest() {
         synchronized(mMethodMap) {
             if (mSwitchingDialog == null) {
@@ -3084,18 +3093,20 @@
 
     @Override
     public void setInputMethod(IBinder token, String id) {
-        if (!calledFromValidUser()) {
-            return;
+        synchronized (mMethodMap) {
+            if (!calledFromValidUserLocked()) {
+                return;
+            }
+            setInputMethodWithSubtypeIdLocked(token, id, NOT_A_SUBTYPE_ID);
         }
-        setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID);
     }
 
     @Override
     public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
-        if (!calledFromValidUser()) {
-            return;
-        }
         synchronized (mMethodMap) {
+            if (!calledFromValidUserLocked()) {
+                return;
+            }
             if (subtype != null) {
                 setInputMethodWithSubtypeIdLocked(token, id,
                         InputMethodUtils.getSubtypeIdFromHashCode(mMethodMap.get(id),
@@ -3109,22 +3120,22 @@
     @Override
     public void showInputMethodAndSubtypeEnablerFromClient(
             IInputMethodClient client, String inputMethodId) {
-        // TODO(yukawa): Should we verify the display ID?
-        if (!calledFromValidUser()) {
-            return;
-        }
         synchronized (mMethodMap) {
+            // TODO(yukawa): Should we verify the display ID?
+            if (!calledFromValidUserLocked()) {
+                return;
+            }
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
                     MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
         }
     }
 
-    @Override
-    public boolean switchToPreviousInputMethod(IBinder token) {
-        if (!calledFromValidUser()) {
-            return false;
-        }
+    @BinderThread
+    private boolean switchToPreviousInputMethod(IBinder token) {
         synchronized (mMethodMap) {
+            if (!calledWithValidTokenLocked(token)) {
+                return false;
+            }
             final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
             final InputMethodInfo lastImi;
             if (lastIme != null) {
@@ -3191,13 +3202,10 @@
         }
     }
 
-    @Override
-    public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
-        if (!calledFromValidUser()) {
-            return false;
-        }
+    @BinderThread
+    private boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
         synchronized (mMethodMap) {
-            if (!calledWithValidToken(token)) {
+            if (!calledWithValidTokenLocked(token)) {
                 return false;
             }
             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
@@ -3213,11 +3221,8 @@
 
     @BinderThread
     private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) {
-        if (!calledFromValidUser()) {
-            return false;
-        }
         synchronized (mMethodMap) {
-            if (!calledWithValidToken(token)) {
+            if (!calledWithValidTokenLocked(token)) {
                 return false;
             }
             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
@@ -3231,10 +3236,10 @@
 
     @Override
     public InputMethodSubtype getLastInputMethodSubtype() {
-        if (!calledFromValidUser()) {
-            return null;
-        }
         synchronized (mMethodMap) {
+            if (!calledFromValidUserLocked()) {
+                return null;
+            }
             final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
             // TODO: Handle the case of the last IME with no subtypes
             if (lastIme == null || TextUtils.isEmpty(lastIme.first)
@@ -3257,13 +3262,13 @@
 
     @Override
     public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
-        if (!calledFromValidUser()) {
-            return;
-        }
         // By this IPC call, only a process which shares the same uid with the IME can add
         // additional input method subtypes to the IME.
         if (TextUtils.isEmpty(imiId) || subtypes == null) return;
         synchronized (mMethodMap) {
+            if (!calledFromValidUserLocked()) {
+                return;
+            }
             if (!mSystemReady) {
                 return;
             }
@@ -3329,12 +3334,6 @@
         }
     }
 
-    private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
-        synchronized (mMethodMap) {
-            setInputMethodWithSubtypeIdLocked(token, id, subtypeId);
-        }
-    }
-
     private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
         if (token == null) {
             if (mContext.checkCallingOrSelfPermission(
@@ -3360,11 +3359,8 @@
 
     @BinderThread
     private void hideMySoftInput(@NonNull IBinder token, int flags) {
-        if (!calledFromValidUser()) {
-            return;
-        }
         synchronized (mMethodMap) {
-            if (!calledWithValidToken(token)) {
+            if (!calledWithValidTokenLocked(token)) {
                 return;
             }
             long ident = Binder.clearCallingIdentity();
@@ -3378,11 +3374,8 @@
 
     @BinderThread
     private void showMySoftInput(@NonNull IBinder token, int flags) {
-        if (!calledFromValidUser()) {
-            return;
-        }
         synchronized (mMethodMap) {
-            if (!calledWithValidToken(token)) {
+            if (!calledWithValidTokenLocked(token)) {
                 return;
             }
             long ident = Binder.clearCallingIdentity();
@@ -3421,6 +3414,7 @@
         switch (msg.what) {
             case MSG_SHOW_IM_SUBTYPE_PICKER:
                 final boolean showAuxSubtypes;
+                final int displayId = msg.arg2;
                 switch (msg.arg1) {
                     case InputMethodManager.SHOW_IM_PICKER_MODE_AUTO:
                         // This is undocumented so far, but IMM#showInputMethodPicker() has been
@@ -3438,7 +3432,7 @@
                         Slog.e(TAG, "Unknown subtype picker mode = " + msg.arg1);
                         return false;
                 }
-                showInputMethodMenu(showAuxSubtypes);
+                showInputMethodMenu(showAuxSubtypes, displayId);
                 return true;
 
             case MSG_SHOW_IM_SUBTYPE_ENABLER:
@@ -3818,7 +3812,7 @@
                 && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
     }
 
-    private void showInputMethodMenu(boolean showAuxSubtypes) {
+    private void showInputMethodMenu(boolean showAuxSubtypes, int displayId) {
         if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
 
         final boolean isScreenLocked = isScreenLocked();
@@ -3864,8 +3858,10 @@
                 }
             }
 
+            final ActivityThread currentThread = ActivityThread.currentActivityThread();
             final Context settingsContext = new ContextThemeWrapper(
-                    ActivityThread.currentActivityThread().getSystemUiContext(),
+                    displayId == DEFAULT_DISPLAY ? currentThread.getSystemUiContext()
+                            : currentThread.createSystemUiContext(displayId),
                     com.android.internal.R.style.Theme_DeviceDefault_Settings);
 
             mDialogBuilder = new AlertDialog.Builder(settingsContext);
@@ -4195,11 +4191,11 @@
      */
     @Override
     public InputMethodSubtype getCurrentInputMethodSubtype() {
-        // TODO: Make this work even for non-current users?
-        if (!calledFromValidUser()) {
-            return null;
-        }
         synchronized (mMethodMap) {
+            // TODO: Make this work even for non-current users?
+            if (!calledFromValidUserLocked()) {
+                return null;
+            }
             return getCurrentInputMethodSubtypeLocked();
         }
     }
@@ -4274,11 +4270,11 @@
 
     @Override
     public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
-        // TODO: Make this work even for non-current users?
-        if (!calledFromValidUser()) {
-            return false;
-        }
         synchronized (mMethodMap) {
+            // TODO: Make this work even for non-current users?
+            if (!calledFromValidUserLocked()) {
+                return false;
+            }
             if (subtype != null && mCurMethodId != null) {
                 InputMethodInfo imi = mMethodMap.get(mCurMethodId);
                 int subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode());
@@ -4532,10 +4528,6 @@
     @BinderThread
     private IInputContentUriToken createInputContentUriToken(@Nullable IBinder token,
             @Nullable Uri contentUri, @Nullable String packageName) {
-        if (!calledFromValidUser()) {
-            return null;
-        }
-
         if (token == null) {
             throw new NullPointerException("token");
         }
@@ -4589,11 +4581,8 @@
 
     @BinderThread
     private void reportFullscreenMode(IBinder token, boolean fullscreen) {
-        if (!calledFromValidUser()) {
-            return;
-        }
         synchronized (mMethodMap) {
-            if (!calledWithValidToken(token)) {
+            if (!calledWithValidTokenLocked(token)) {
                 return;
             }
             if (mCurClient != null && mCurClient.client != null) {
@@ -4933,11 +4922,6 @@
     @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
     private int handleShellCommandEnableDisableInputMethod(
             @NonNull ShellCommand shellCommand, boolean enabled) {
-        if (!calledFromValidUser()) {
-            shellCommand.getErrPrintWriter().print(
-                    "Must be called from the foreground user or with INTERACT_ACROSS_USERS_FULL");
-            return ShellCommandResult.FAILURE;
-        }
         final String id = shellCommand.getNextArgRequired();
 
         final boolean previouslyEnabled;
@@ -4994,11 +4978,6 @@
     @ShellCommandResult
     @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
     private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) {
-        if (!calledFromValidUser()) {
-            shellCommand.getErrPrintWriter().print(
-                    "Must be called from the foreground user or with INTERACT_ACROSS_USERS_FULL");
-            return ShellCommandResult.FAILURE;
-        }
         synchronized (mMethodMap) {
             if (mContext.checkCallingOrSelfPermission(
                     android.Manifest.permission.WRITE_SECURE_SETTINGS)
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 2b67fe7..5edb5c8 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1541,6 +1541,13 @@
 
         @BinderThread
         @Override
+        public void showInputMethodPickerFromSystem(
+                IInputMethodClient client, int auxiliarySubtypeMode, int displayId) {
+            reportNotSupported();
+        }
+
+        @BinderThread
+        @Override
         public void showInputMethodAndSubtypeEnablerFromClient(
                 IInputMethodClient client, String inputMethodId) {
             reportNotSupported();
@@ -1595,20 +1602,6 @@
 
         @BinderThread
         @Override
-        public boolean switchToPreviousInputMethod(IBinder token) {
-            reportNotSupported();
-            return false;
-        }
-
-        @BinderThread
-        @Override
-        public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
-            reportNotSupported();
-            return false;
-        }
-
-        @BinderThread
-        @Override
         public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
             reportNotSupported();
         }
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 173f074..611c8b7 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -54,6 +54,8 @@
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.IThermalService;
+import android.os.IThermalStatusListener;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
@@ -62,6 +64,7 @@
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.SystemClock;
+import android.os.Temperature;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
@@ -75,6 +78,7 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.ArrayUtils;
@@ -179,6 +183,11 @@
     private final StorageController mStorageController;
     /** Need directly for sending uid state changes */
     private final DeviceIdleJobsController mDeviceIdleJobsController;
+    /** Need directly for receiving thermal events */
+    private IThermalService mThermalService;
+    /** Thermal constraint. */
+    @GuardedBy("mLock")
+    private boolean mThermalConstraint = false;
 
     /**
      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
@@ -310,6 +319,19 @@
     }
 
     /**
+     *  Thermal event received from Thermal Service
+     */
+    private final class ThermalStatusListener extends IThermalStatusListener.Stub {
+        @Override public void onStatusChange(int status) {
+            // Throttle for Temperature.THROTTLING_SEVERE and above
+            synchronized (mLock) {
+                mThermalConstraint = status >= Temperature.THROTTLING_SEVERE;
+            }
+            onControllerStateChanged();
+        }
+    }
+
+    /**
      * All times are in milliseconds. These constants are kept synchronized with the system
      * global Settings. Any access to this class or its fields should be done while
      * holding the JobSchedulerService.mLock lock.
@@ -1159,8 +1181,10 @@
                 // with just the foreground priority.  This means that persistent processes
                 // can never be the top app priority...  that is fine.
                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
+            } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_SERVICE);
             } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
-                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
+                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE);
             } else {
                 mUidPriorityOverride.delete(uid);
             }
@@ -1366,6 +1390,16 @@
             }
             // Remove any jobs that are not associated with any of the current users.
             cancelJobsForNonExistentUsers();
+            // Register thermal callback
+            mThermalService = IThermalService.Stub.asInterface(
+                    ServiceManager.getService(Context.THERMAL_SERVICE));
+            if (mThermalService != null) {
+                try {
+                    mThermalService.registerThermalStatusListener(new ThermalStatusListener());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to register thermal callback.", e);
+                }
+            }
         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
             synchronized (mLock) {
                 // Let's go!
@@ -1789,14 +1823,26 @@
         }
     }
 
+    private boolean isJobThermalConstrainedLocked(JobStatus job) {
+        return mThermalConstraint && job.hasConnectivityConstraint()
+                && (evaluateJobPriorityLocked(job) < JobInfo.PRIORITY_FOREGROUND_APP);
+    }
+
     private void stopNonReadyActiveJobsLocked() {
         for (int i=0; i<mActiveServices.size(); i++) {
             JobServiceContext serviceContext = mActiveServices.get(i);
             final JobStatus running = serviceContext.getRunningJobLocked();
-            if (running != null && !running.isReady()) {
+            if (running == null) {
+                continue;
+            }
+            if (!running.isReady()) {
                 serviceContext.cancelExecutingJobLocked(
                         JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
                         "cancelled due to unsatisfied constraints");
+            } else if (isJobThermalConstrainedLocked(running)) {
+                serviceContext.cancelExecutingJobLocked(
+                        JobParameters.REASON_DEVICE_THERMAL,
+                        "cancelled due to thermal condition");
             }
         }
     }
@@ -2084,6 +2130,10 @@
             return false;
         }
 
+        if (isJobThermalConstrainedLocked(job)) {
+            return false;
+        }
+
         final boolean jobPending = mPendingJobs.contains(job);
         final boolean jobActive = isCurrentlyActiveLocked(job);
 
@@ -2196,7 +2246,7 @@
 
     int evaluateJobPriorityLocked(JobStatus job) {
         int priority = job.getPriority();
-        if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
+        if (priority >= JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE) {
             return adjustJobPriority(priority, job);
         }
         int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
@@ -3033,6 +3083,9 @@
             pw.print("    In parole?: ");
             pw.print(mInParole);
             pw.println();
+            pw.print("    In thermal throttling?: ");
+            pw.print(mThermalConstraint);
+            pw.println();
             pw.println();
 
             pw.println("Started users: " + Arrays.toString(mStartedUsers));
@@ -3130,9 +3183,9 @@
                 pw.println(job.toShortString());
                 job.dump(pw, "    ", false, nowElapsed);
                 int priority = evaluateJobPriorityLocked(job);
-                if (priority != JobInfo.PRIORITY_DEFAULT) {
-                    pw.print("    Evaluated priority: "); pw.println(priority);
-                }
+                pw.print("    Evaluated priority: ");
+                pw.println(JobInfo.getPriorityString(priority));
+
                 pw.print("    Tag: "); pw.println(job.getTag());
                 pw.print("    Enq: ");
                 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
@@ -3163,9 +3216,9 @@
                     pw.println();
                     job.dump(pw, "    ", false, nowElapsed);
                     int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
-                    if (priority != JobInfo.PRIORITY_DEFAULT) {
-                        pw.print("    Evaluated priority: "); pw.println(priority);
-                    }
+                    pw.print("    Evaluated priority: ");
+                    pw.println(JobInfo.getPriorityString(priority));
+
                     pw.print("    Active at ");
                     TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
                     pw.print(", pending for ");
@@ -3208,6 +3261,7 @@
             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
                     mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
             proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
+            proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mThermalConstraint);
 
             for (int u : mStartedUsers) {
                 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
@@ -3283,10 +3337,7 @@
 
                 job.writeToShortProto(proto, PendingJob.INFO);
                 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
-                int priority = evaluateJobPriorityLocked(job);
-                if (priority != JobInfo.PRIORITY_DEFAULT) {
-                    proto.write(PendingJob.EVALUATED_PRIORITY, priority);
-                }
+                proto.write(PendingJob.EVALUATED_PRIORITY, evaluateJobPriorityLocked(job));
                 proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
 
                 proto.end(pjToken);
@@ -3318,10 +3369,8 @@
 
                     job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
 
-                    int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
-                    if (priority != JobInfo.PRIORITY_DEFAULT) {
-                        proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, priority);
-                    }
+                    proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY,
+                            evaluateJobPriorityLocked(jsc.getRunningJobLocked()));
 
                     proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
                             nowUptime - job.madeActive);
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 6deecbd..4341589 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -1321,7 +1321,8 @@
                 pw.print(prefix); pw.println("  PERSISTED");
             }
             if (job.getPriority() != 0) {
-                pw.print(prefix); pw.print("  Priority: "); pw.println(job.getPriority());
+                pw.print(prefix); pw.print("  Priority: ");
+                pw.println(JobInfo.getPriorityString(job.getPriority()));
             }
             if (job.getFlags() != 0) {
                 pw.print(prefix); pw.print("  Flags: ");
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
new file mode 100644
index 0000000..4c7c420
--- /dev/null
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.location.Location;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.os.WorkSource;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Location Manager's interface for location providers.
+ *
+ * @hide
+ */
+public abstract class AbstractLocationProvider {
+
+    /**
+     * Interface for communicating from a location provider back to the location service.
+     */
+    public interface LocationProviderManager {
+
+        /**
+         * Called on location provider construction to make the location service aware of this
+         * provider and what it's initial enabled/disabled state should be.
+         */
+        void onAttachProvider(AbstractLocationProvider locationProvider, boolean initiallyEnabled);
+
+        /**
+         * May be called to inform the location service of a change in this location provider's
+         * enabled/disabled state.
+         */
+        void onSetEnabled(boolean enabled);
+
+        /**
+         * May be called to inform the location service of a change in this location provider's
+         * properties.
+         */
+        void onSetProperties(ProviderProperties properties);
+
+        /**
+         * May be called to inform the location service that this provider has a new location
+         * available.
+         */
+        void onReportLocation(Location location);
+
+        /**
+         * May be called to inform the location service that this provider has a new location
+         * available.
+         */
+        void onReportLocation(List<Location> locations);
+    }
+
+    private final LocationProviderManager mLocationProviderManager;
+
+    protected AbstractLocationProvider(LocationProviderManager locationProviderManager) {
+        this(locationProviderManager, true);
+    }
+
+    protected AbstractLocationProvider(LocationProviderManager locationProviderManager,
+            boolean initiallyEnabled) {
+        mLocationProviderManager = locationProviderManager;
+        mLocationProviderManager.onAttachProvider(this, initiallyEnabled);
+    }
+
+    /**
+     * Call this method to report a new location. May be called from any thread.
+     */
+    protected void reportLocation(Location location) {
+        mLocationProviderManager.onReportLocation(location);
+    }
+
+    /**
+     * Call this method to report a new location. May be called from any thread.
+     */
+    protected void reportLocation(List<Location> locations) {
+        mLocationProviderManager.onReportLocation(locations);
+    }
+
+    /**
+     * Call this method to report a change in provider enabled/disabled status. May be called from
+     * any thread.
+     */
+    protected void setEnabled(boolean enabled) {
+        mLocationProviderManager.onSetEnabled(enabled);
+    }
+
+    /**
+     * Call this method to report a change in provider properties. May be called from
+     * any thread.
+     */
+    protected void setProperties(ProviderProperties properties) {
+        mLocationProviderManager.onSetProperties(properties);
+    }
+
+    /**
+     * Called when the location service delivers a new request for fulfillment to the provider.
+     * Replaces any previous requests completely.
+     */
+    public abstract void setRequest(ProviderRequest request, WorkSource source);
+
+    /**
+     * Called to dump debug or log information.
+     */
+    public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+    /**
+     * Retrieves the current status of the provider.
+     *
+     * @deprecated Will be removed in a future release.
+     */
+    @Deprecated
+    public int getStatus(Bundle extras) {
+        return LocationProvider.AVAILABLE;
+    }
+
+    /**
+     * Retrieves the last update time of the status of the provider.
+     *
+     * @deprecated Will be removed in a future release.
+     */
+    @Deprecated
+    public long getStatusUpdateTime() {
+        return 0;
+    }
+
+    /** Sends a custom command to this provider. */
+    public abstract void sendExtraCommand(String command, Bundle extras);
+}
diff --git a/services/core/java/com/android/server/location/GnssGeofenceProvider.java b/services/core/java/com/android/server/location/GnssGeofenceProvider.java
index 6ac4aeb..a84b0b1 100644
--- a/services/core/java/com/android/server/location/GnssGeofenceProvider.java
+++ b/services/core/java/com/android/server/location/GnssGeofenceProvider.java
@@ -1,18 +1,12 @@
 package com.android.server.location;
 
 import android.location.IGpsGeofenceHardware;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
-
 /**
  * Manages GNSS Geofence operations.
  */
@@ -34,26 +28,26 @@
         public boolean paused;
     }
 
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
     private final GnssGeofenceProviderNative mNative;
+    @GuardedBy("mLock")
     private final SparseArray<GeofenceEntry> mGeofenceEntries = new SparseArray<>();
-    private final Handler mHandler;
 
-    GnssGeofenceProvider(Looper looper) {
-        this(looper, new GnssGeofenceProviderNative());
+    GnssGeofenceProvider() {
+        this(new GnssGeofenceProviderNative());
     }
 
     @VisibleForTesting
-    GnssGeofenceProvider(Looper looper, GnssGeofenceProviderNative gnssGeofenceProviderNative) {
-        mHandler = new Handler(looper);
+    GnssGeofenceProvider(GnssGeofenceProviderNative gnssGeofenceProviderNative) {
         mNative = gnssGeofenceProviderNative;
     }
 
-    // TODO(b/37460011): use this method in HAL death recovery.
     void resumeIfStarted() {
         if (DEBUG) {
             Log.d(TAG, "resumeIfStarted");
         }
-        mHandler.post(() -> {
+        synchronized (mLock) {
             for (int i = 0; i < mGeofenceEntries.size(); i++) {
                 GeofenceEntry entry = mGeofenceEntries.valueAt(i);
                 boolean added = mNative.addGeofence(entry.geofenceId, entry.latitude,
@@ -65,30 +59,21 @@
                     mNative.pauseGeofence(entry.geofenceId);
                 }
             }
-        });
-    }
-
-    private boolean runOnHandlerThread(Callable<Boolean> callable) {
-        FutureTask<Boolean> futureTask = new FutureTask<>(callable);
-        mHandler.post(futureTask);
-        try {
-            return futureTask.get();
-        } catch (InterruptedException | ExecutionException e) {
-            Log.e(TAG, "Failed running callable.", e);
         }
-        return false;
     }
 
     @Override
     public boolean isHardwareGeofenceSupported() {
-        return runOnHandlerThread(mNative::isGeofenceSupported);
+        synchronized (mLock) {
+            return mNative.isGeofenceSupported();
+        }
     }
 
     @Override
     public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
             double longitude, double radius, int lastTransition, int monitorTransitions,
             int notificationResponsiveness, int unknownTimer) {
-        return runOnHandlerThread(() -> {
+        synchronized (mLock) {
             boolean added = mNative.addGeofence(geofenceId, latitude, longitude, radius,
                     lastTransition, monitorTransitions, notificationResponsiveness,
                     unknownTimer);
@@ -105,23 +90,23 @@
                 mGeofenceEntries.put(geofenceId, entry);
             }
             return added;
-        });
+        }
     }
 
     @Override
     public boolean removeHardwareGeofence(int geofenceId) {
-        return runOnHandlerThread(() -> {
+        synchronized (mLock) {
             boolean removed = mNative.removeGeofence(geofenceId);
             if (removed) {
                 mGeofenceEntries.remove(geofenceId);
             }
             return removed;
-        });
+        }
     }
 
     @Override
     public boolean pauseHardwareGeofence(int geofenceId) {
-        return runOnHandlerThread(() -> {
+        synchronized (mLock) {
             boolean paused = mNative.pauseGeofence(geofenceId);
             if (paused) {
                 GeofenceEntry entry = mGeofenceEntries.get(geofenceId);
@@ -130,12 +115,12 @@
                 }
             }
             return paused;
-        });
+        }
     }
 
     @Override
     public boolean resumeHardwareGeofence(int geofenceId, int monitorTransitions) {
-        return runOnHandlerThread(() -> {
+        synchronized (mLock) {
             boolean resumed = mNative.resumeGeofence(geofenceId, monitorTransitions);
             if (resumed) {
                 GeofenceEntry entry = mGeofenceEntries.get(geofenceId);
@@ -145,7 +130,7 @@
                 }
             }
             return resumed;
-        });
+        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index d5e4681..29e1878 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -31,10 +31,7 @@
 import android.location.GnssMeasurementsEvent;
 import android.location.GnssNavigationMessage;
 import android.location.GnssStatus;
-import android.location.IGnssStatusListener;
-import android.location.IGnssStatusProvider;
 import android.location.IGpsGeofenceHardware;
-import android.location.ILocationManager;
 import android.location.INetInitiatedListener;
 import android.location.Location;
 import android.location.LocationListener;
@@ -84,6 +81,10 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -97,8 +98,18 @@
  *
  * {@hide}
  */
-public class GnssLocationProvider extends LocationProviderInterface
-        implements InjectNtpTimeCallback, GnssSatelliteBlacklistCallback {
+public class GnssLocationProvider extends AbstractLocationProvider implements
+        InjectNtpTimeCallback,
+        GnssSatelliteBlacklistCallback {
+
+    /**
+     * Indicates that this method is a native entry point. Useful purely for IDEs which can
+     * understand entry points, and thus eliminate incorrect warnings about methods not used.
+     */
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface NativeEntryPoint {
+    }
 
     private static final String TAG = "GnssLocationProvider";
 
@@ -249,7 +260,7 @@
         }
 
         public void set(int svCount, int meanCn0, int maxCn0) {
-            synchronized(this) {
+            synchronized (this) {
                 mSvCount = svCount;
                 mMeanCn0 = meanCn0;
                 mMaxCn0 = maxCn0;
@@ -258,7 +269,7 @@
         }
 
         public void reset() {
-            set(0,0,0);
+            set(0, 0, 0);
         }
 
         // Also used by outside methods to add to other bundles
@@ -314,7 +325,7 @@
             MAX_RETRY_INTERVAL);
 
     // true if we are enabled, protected by this
-    private boolean mEnabled;
+    private boolean mEnabled = true;
 
     // states for injecting ntp and downloading xtra data
     private static final int STATE_PENDING_NETWORK = 0;
@@ -328,9 +339,6 @@
     // true if GPS is navigating
     private boolean mNavigating;
 
-    // true if GPS engine is on
-    private boolean mEngineOn;
-
     // requested frequency of fixes, in milliseconds
     private int mFixInterval = 1000;
 
@@ -380,9 +388,8 @@
     private boolean mSuplEsEnabled = false;
 
     private final Context mContext;
-    private final ILocationManager mILocationManager;
     private final LocationExtras mLocationExtras = new LocationExtras();
-    private final GnssStatusListenerHelper mListenerHelper;
+    private final GnssStatusListenerHelper mGnssStatusListenerHelper;
     private final GnssSatelliteBlacklistHelper mGnssSatelliteBlacklistHelper;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
     private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
@@ -443,20 +450,8 @@
     // GNSS Metrics
     private GnssMetrics mGnssMetrics;
 
-    private final IGnssStatusProvider mGnssStatusProvider = new IGnssStatusProvider.Stub() {
-        @Override
-        public void registerGnssStatusCallback(IGnssStatusListener callback) {
-            mListenerHelper.addListener(callback);
-        }
-
-        @Override
-        public void unregisterGnssStatusCallback(IGnssStatusListener callback) {
-            mListenerHelper.removeListener(callback);
-        }
-    };
-
-    public IGnssStatusProvider getGnssStatusProvider() {
-        return mGnssStatusProvider;
+    public GnssStatusListenerHelper getGnssStatusProvider() {
+        return mGnssStatusListenerHelper;
     }
 
     public IGpsGeofenceHardware getGpsGeofenceProxy() {
@@ -479,17 +474,22 @@
                 return;
             }
 
-            if (action.equals(ALARM_WAKEUP)) {
-                startNavigating(false);
-            } else if (action.equals(ALARM_TIMEOUT)) {
-                hibernate();
-            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)
-                    || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)
-                    || Intent.ACTION_SCREEN_OFF.equals(action)
-                    || Intent.ACTION_SCREEN_ON.equals(action)) {
-                updateLowPowerMode();
-            } else if (action.equals(SIM_STATE_CHANGED)) {
-                subscriptionOrSimChanged(context);
+            switch (action) {
+                case ALARM_WAKEUP:
+                    startNavigating(false);
+                    break;
+                case ALARM_TIMEOUT:
+                    hibernate();
+                    break;
+                case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
+                case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+                case Intent.ACTION_SCREEN_OFF:
+                case Intent.ACTION_SCREEN_ON:
+                    updateLowPowerMode();
+                    break;
+                case SIM_STATE_CHANGED:
+                    subscriptionOrSimChanged(context);
+                    break;
             }
         }
     };
@@ -507,9 +507,7 @@
      */
     @Override
     public void onUpdateSatelliteBlacklist(int[] constellations, int[] svids) {
-        mHandler.post(()->{
-            native_set_satellite_blacklist(constellations, svids);
-        });
+        mHandler.post(() -> native_set_satellite_blacklist(constellations, svids));
     }
 
     private void subscriptionOrSimChanged(Context context) {
@@ -572,7 +570,7 @@
     }
 
     interface SetCarrierProperty {
-        public boolean set(int value);
+        boolean set(int value);
     }
 
     private void reloadGpsProperties(Context context, Properties properties) {
@@ -587,7 +585,7 @@
         /*
          * Overlay carrier properties from a debug configuration file.
          */
-        loadPropertiesFromFile(DEBUG_PROPERTIES_FILE, properties);
+        loadPropertiesFromFile(properties);
         // TODO: we should get rid of C2K specific setting.
         setSuplHostPort(properties.getProperty("SUPL_HOST"),
                 properties.getProperty("SUPL_PORT"));
@@ -603,15 +601,15 @@
         if (native_is_gnss_configuration_supported()) {
             Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
                 {
-                    put("SUPL_VER", (val) -> native_set_supl_version(val));
-                    put("SUPL_MODE", (val) -> native_set_supl_mode(val));
-                    put("SUPL_ES", (val) -> native_set_supl_es(val));
-                    put("LPP_PROFILE", (val) -> native_set_lpp_profile(val));
+                    put("SUPL_VER", GnssLocationProvider::native_set_supl_version);
+                    put("SUPL_MODE", GnssLocationProvider::native_set_supl_mode);
+                    put("SUPL_ES", GnssLocationProvider::native_set_supl_es);
+                    put("LPP_PROFILE", GnssLocationProvider::native_set_lpp_profile);
                     put("A_GLONASS_POS_PROTOCOL_SELECT",
-                            (val) -> native_set_gnss_pos_protocol_select(val));
+                            GnssLocationProvider::native_set_gnss_pos_protocol_select);
                     put("USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL",
-                            (val) -> native_set_emergency_supl_pdn(val));
-                    put("GPS_LOCK", (val) -> native_set_gps_lock(val));
+                            GnssLocationProvider::native_set_emergency_supl_pdn);
+                    put("GPS_LOCK", GnssLocationProvider::native_set_gps_lock);
                 }
             };
 
@@ -622,7 +620,7 @@
                     try {
                         int propertyValueInt = Integer.decode(propertyValueString);
                         boolean result = entry.getValue().set(propertyValueInt);
-                        if (result == false) {
+                        if (!result) {
                             Log.e(TAG, "Unable to set " + propertyName);
                         }
                     } catch (NumberFormatException e) {
@@ -664,10 +662,9 @@
         }
     }
 
-    private boolean loadPropertiesFromFile(String filename,
-            Properties properties) {
+    private void loadPropertiesFromFile(Properties properties) {
         try {
-            File file = new File(filename);
+            File file = new File(DEBUG_PROPERTIES_FILE);
             FileInputStream stream = null;
             try {
                 stream = new FileInputStream(file);
@@ -677,16 +674,15 @@
             }
 
         } catch (IOException e) {
-            if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + filename);
-            return false;
+            if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE);
         }
-        return true;
     }
 
-    public GnssLocationProvider(Context context, ILocationManager ilocationManager,
+    public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
             Looper looper) {
+        super(locationProviderManager, true);
+
         mContext = context;
-        mILocationManager = ilocationManager;
 
         // Create a wake lock
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -730,7 +726,7 @@
                 mNetInitiatedListener,
                 mSuplEsEnabled);
 
-        mListenerHelper = new GnssStatusListenerHelper(mHandler) {
+        mGnssStatusListenerHelper = new GnssStatusListenerHelper(mContext, mHandler) {
             @Override
             protected boolean isAvailableInPlatform() {
                 return isSupported();
@@ -749,7 +745,7 @@
             }
         };
 
-        mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mHandler) {
+        mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mContext, mHandler) {
             @Override
             protected boolean isGpsEnabled() {
                 return isEnabled();
@@ -762,20 +758,21 @@
                 looper, this);
         mHandler.post(mGnssSatelliteBlacklistHelper::updateSatelliteBlacklist);
         mGnssBatchingProvider = new GnssBatchingProvider();
-        mGnssGeofenceProvider = new GnssGeofenceProvider(looper);
-    }
+        mGnssGeofenceProvider = new GnssGeofenceProvider();
 
-    /**
-     * Returns the name of this provider.
-     */
-    @Override
-    public String getName() {
-        return LocationManager.GPS_PROVIDER;
-    }
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_SHUTDOWN);
+        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (getSendingUserId() == UserHandle.USER_ALL) {
+                    mEnabled = false;
+                    handleDisable();
+                }
+            }
+        }, UserHandle.ALL, intentFilter, null, mHandler);
 
-    @Override
-    public ProviderProperties getProperties() {
-        return PROPERTIES;
+        setProperties(PROPERTIES);
     }
 
     /**
@@ -840,9 +837,9 @@
             locationManager.requestLocationUpdates(provider,
                     LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS, /*minDistance=*/ 0,
                     locationListener, mHandler.getLooper());
-            locationListener.numLocationUpdateRequest++;
+            locationListener.mNumLocationUpdateRequest++;
             mHandler.postDelayed(() -> {
-                if (--locationListener.numLocationUpdateRequest == 0) {
+                if (--locationListener.mNumLocationUpdateRequest == 0) {
                     Log.i(TAG,
                             String.format("Removing location updates from %s provider.", provider));
                     locationManager.removeUpdates(locationListener);
@@ -905,43 +902,40 @@
         // hold wake lock while task runs
         mDownloadXtraWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
         Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
-        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
-            @Override
-            public void run() {
-                GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
-                byte[] data = xtraDownloader.downloadXtraData();
-                if (data != null) {
-                    if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
-                    native_inject_xtra_data(data, data.length);
-                    mXtraBackOff.reset();
-                }
+        AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+            GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
+            byte[] data = xtraDownloader.downloadXtraData();
+            if (data != null) {
+                if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
+                native_inject_xtra_data(data, data.length);
+                mXtraBackOff.reset();
+            }
 
-                sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
+            sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
 
-                if (data == null) {
-                    // try again later
-                    // since this is delayed and not urgent we do not hold a wake lock here
-                    mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
-                            mXtraBackOff.nextBackoffMillis());
-                }
+            if (data == null) {
+                // try again later
+                // since this is delayed and not urgent we do not hold a wake lock here
+                mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
+                        mXtraBackOff.nextBackoffMillis());
+            }
 
-                // Release wake lock held by task, synchronize on mLock in case multiple
-                // download tasks overrun.
-                synchronized (mLock) {
-                    if (mDownloadXtraWakeLock.isHeld()) {
-                        // This wakelock may have time-out, if a timeout was specified.
-                        // Catch (and ignore) any timeout exceptions.
-                        try {
-                            mDownloadXtraWakeLock.release();
-                            if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
-                        } catch (Exception e) {
-                            Log.i(TAG, "Wakelock timeout & release race exception in "
-                                    + "handleDownloadXtraData()", e);
-                        }
-                    } else {
-                        Log.e(TAG, "WakeLock expired before release in "
-                                + "handleDownloadXtraData()");
+            // Release wake lock held by task, synchronize on mLock in case multiple
+            // download tasks overrun.
+            synchronized (mLock) {
+                if (mDownloadXtraWakeLock.isHeld()) {
+                    // This wakelock may have time-out, if a timeout was specified.
+                    // Catch (and ignore) any timeout exceptions.
+                    try {
+                        mDownloadXtraWakeLock.release();
+                        if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
+                    } catch (Exception e) {
+                        Log.i(TAG, "Wakelock timeout & release race exception in "
+                                + "handleDownloadXtraData()", e);
                     }
+                } else {
+                    Log.e(TAG, "WakeLock expired before release in "
+                            + "handleDownloadXtraData()");
                 }
             }
         });
@@ -954,21 +948,6 @@
         }
     }
 
-    /**
-     * Enables this provider.  When enabled, calls to getStatus()
-     * must be handled.  Hardware may be started up
-     * when the provider is enabled.
-     */
-    @Override
-    public void enable() {
-        synchronized (mLock) {
-            if (mEnabled) return;
-            mEnabled = true;
-        }
-
-        sendMessage(ENABLE, 1, null);
-    }
-
     private void setSuplHostPort(String hostString, String portString) {
         if (hostString != null) {
             mSuplServerHost = hostString;
@@ -1052,21 +1031,6 @@
         }
     }
 
-    /**
-     * Disables this provider.  When disabled, calls to getStatus()
-     * need not be handled.  Hardware may be shut
-     * down while the provider is disabled.
-     */
-    @Override
-    public void disable() {
-        synchronized (mLock) {
-            if (!mEnabled) return;
-            mEnabled = false;
-        }
-
-        sendMessage(ENABLE, 0, null);
-    }
-
     private void handleDisable() {
         if (DEBUG) Log.d(TAG, "handleDisable");
 
@@ -1083,7 +1047,6 @@
         mGnssNavigationMessageProvider.onGpsEnabledChanged();
     }
 
-    @Override
     public boolean isEnabled() {
         synchronized (mLock) {
             return mEnabled;
@@ -1147,7 +1110,7 @@
             updateClientUids(mWorkSource);
 
             mFixInterval = (int) mProviderRequest.interval;
-            mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
+            mLowPowerMode = mProviderRequest.lowPowerMode;
             // check for overflow
             if (mFixInterval != mProviderRequest.interval) {
                 Log.w(TAG, "interval overflow: " + mProviderRequest.interval);
@@ -1171,7 +1134,8 @@
                     // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
                     // and our fix interval is not short
                     mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                            SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);                }
+                            SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
+                }
             }
         } else {
             updateClientUids(new WorkSource());
@@ -1220,16 +1184,14 @@
             List<WorkChain> goneChains = diffs[1];
 
             if (newChains != null) {
-                for (int i = 0; i < newChains.size(); ++i) {
-                    final WorkChain newChain = newChains.get(i);
+                for (WorkChain newChain : newChains) {
                     mAppOps.startOpNoThrow(AppOpsManager.OP_GPS, newChain.getAttributionUid(),
                             newChain.getAttributionTag());
                 }
             }
 
             if (goneChains != null) {
-                for (int i = 0; i < goneChains.size(); i++) {
-                    final WorkChain goneChain = goneChains.get(i);
+                for (WorkChain goneChain : goneChains) {
                     mAppOps.finishOp(AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
                             goneChain.getAttributionTag());
                 }
@@ -1262,32 +1224,27 @@
     }
 
     @Override
-    public boolean sendExtraCommand(String command, Bundle extras) {
+    public void sendExtraCommand(String command, Bundle extras) {
 
         long identity = Binder.clearCallingIdentity();
         try {
-            boolean result = false;
-
             if ("delete_aiding_data".equals(command)) {
-                result = deleteAidingData(extras);
+                deleteAidingData(extras);
             } else if ("force_time_injection".equals(command)) {
                 requestUtcTime();
-                result = true;
             } else if ("force_xtra_injection".equals(command)) {
                 if (mSupportsXtra) {
                     xtraDownloadRequest();
-                    result = true;
                 }
             } else {
                 Log.w(TAG, "sendExtraCommand: unknown command " + command);
             }
-            return result;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
-    private boolean deleteAidingData(Bundle extras) {
+    private void deleteAidingData(Bundle extras) {
         int flags;
 
         if (extras == null) {
@@ -1311,10 +1268,7 @@
 
         if (flags != 0) {
             native_delete_aiding_data(flags);
-            return true;
         }
-
-        return false;
     }
 
     private void startNavigating(boolean singleShot) {
@@ -1358,7 +1312,7 @@
             }
 
             int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
-            mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
+            mLowPowerMode = mProviderRequest.lowPowerMode;
             if (!setPositionMode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
                     interval, 0, 0, mLowPowerMode)) {
                 mStarted = false;
@@ -1415,10 +1369,7 @@
         return ((mEngineCapabilities & capability) != 0);
     }
 
-
-    /**
-     * called from native code to update our position.
-     */
+    @NativeEntryPoint
     private void reportLocation(boolean hasLatLong, Location location) {
         sendMessage(REPORT_LOCATION, hasLatLong ? 1 : 0, location);
     }
@@ -1444,11 +1395,7 @@
         location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
         location.setExtras(mLocationExtras.getBundle());
 
-        try {
-            mILocationManager.reportLocation(location, false);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException calling reportLocation");
-        }
+        reportLocation(location);
 
         if (mStarted) {
             mGnssMetrics.logReceivedLocationStatus(hasLatLong);
@@ -1473,7 +1420,7 @@
             }
 
             // notify status listeners
-            mListenerHelper.onFirstFix(mTimeToFirstFix);
+            mGnssStatusListenerHelper.onFirstFix(mTimeToFirstFix);
         }
 
         if (mSingleShot) {
@@ -1482,7 +1429,8 @@
 
         if (mStarted && mStatus != LocationProvider.AVAILABLE) {
             // For devices that use framework scheduling, a timer may be set to ensure we don't
-            // spend too much power searching for a location, when the requested update rate is slow.
+            // spend too much power searching for a location, when the requested update rate is
+            // slow.
             // As we just recievied a location, we'll cancel that timer.
             if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
                 mAlarmManager.cancel(mTimeoutIntent);
@@ -1502,9 +1450,7 @@
         }
     }
 
-    /**
-     * called from native code to update our status
-     */
+    @NativeEntryPoint
     private void reportStatus(int status) {
         if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
 
@@ -1512,22 +1458,19 @@
         switch (status) {
             case GPS_STATUS_SESSION_BEGIN:
                 mNavigating = true;
-                mEngineOn = true;
                 break;
             case GPS_STATUS_SESSION_END:
                 mNavigating = false;
                 break;
             case GPS_STATUS_ENGINE_ON:
-                mEngineOn = true;
                 break;
             case GPS_STATUS_ENGINE_OFF:
-                mEngineOn = false;
                 mNavigating = false;
                 break;
         }
 
         if (wasNavigating != mNavigating) {
-            mListenerHelper.onStatusChanged(mNavigating);
+            mGnssStatusListenerHelper.onStatusChanged(mNavigating);
 
             // send an intent to notify that the GPS has been enabled or disabled
             Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
@@ -1538,17 +1481,15 @@
 
     // Helper class to carry data to handler for reportSvStatus
     private static class SvStatusInfo {
-        public int mSvCount;
-        public int[] mSvidWithFlags;
-        public float[] mCn0s;
-        public float[] mSvElevations;
-        public float[] mSvAzimuths;
-        public float[] mSvCarrierFreqs;
+        private int mSvCount;
+        private int[] mSvidWithFlags;
+        private float[] mCn0s;
+        private float[] mSvElevations;
+        private float[] mSvAzimuths;
+        private float[] mSvCarrierFreqs;
     }
 
-    /**
-     * called from native code to update SV info
-     */
+    @NativeEntryPoint
     private void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0s,
             float[] svElevations, float[] svAzimuths, float[] svCarrierFreqs) {
         SvStatusInfo svStatusInfo = new SvStatusInfo();
@@ -1563,7 +1504,7 @@
     }
 
     private void handleReportSvStatus(SvStatusInfo info) {
-        mListenerHelper.onSvStatusChanged(
+        mGnssStatusListenerHelper.onSvStatusChanged(
                 info.mSvCount,
                 info.mSvidWithFlags,
                 info.mCn0s,
@@ -1622,75 +1563,52 @@
         }
     }
 
-    /**
-     * called from native code to update AGPS status
-     */
+    @NativeEntryPoint
     private void reportAGpsStatus(int type, int status, byte[] ipaddr) {
         mNetworkConnectivityHandler.onReportAGpsStatus(type, status, ipaddr);
     }
 
-    /**
-     * called from native code to report NMEA data received
-     */
+    @NativeEntryPoint
     private void reportNmea(long timestamp) {
         if (!mItarSpeedLimitExceeded) {
             int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
             String nmea = new String(mNmeaBuffer, 0 /* offset */, length);
-            mListenerHelper.onNmeaReceived(timestamp, nmea);
+            mGnssStatusListenerHelper.onNmeaReceived(timestamp, nmea);
         }
     }
 
-    /**
-     * called from native code - GNSS measurements callback
-     */
+    @NativeEntryPoint
     private void reportMeasurementData(GnssMeasurementsEvent event) {
         if (!mItarSpeedLimitExceeded) {
             // send to handler to allow native to return quickly
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mGnssMeasurementsProvider.onMeasurementsAvailable(event);
-                }
-            });
+            mHandler.post(() -> mGnssMeasurementsProvider.onMeasurementsAvailable(event));
         }
     }
 
-    /**
-     * called from native code - GNSS navigation message callback
-     */
+    @NativeEntryPoint
     private void reportNavigationMessage(GnssNavigationMessage event) {
         if (!mItarSpeedLimitExceeded) {
             // send to handler to allow native to return quickly
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mGnssNavigationMessageProvider.onNavigationMessageAvailable(event);
-                }
-            });
+            mHandler.post(() -> mGnssNavigationMessageProvider.onNavigationMessageAvailable(event));
         }
     }
 
-    /**
-     * called from native code to inform us what the GPS engine capabilities are
-     */
+    @NativeEntryPoint
     private void setEngineCapabilities(final int capabilities) {
         // send to handler thread for fast native return, and in-order handling
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mEngineCapabilities = capabilities;
+        mHandler.post(() -> {
+            mEngineCapabilities = capabilities;
 
-                if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
-                    mNtpTimeHelper.enablePeriodicTimeInjection();
-                    requestUtcTime();
-                }
-
-                mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
-                        GPS_CAPABILITY_MEASUREMENTS));
-                mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
-                        GPS_CAPABILITY_NAV_MESSAGES));
-                restartRequests();
+            if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
+                mNtpTimeHelper.enablePeriodicTimeInjection();
+                requestUtcTime();
             }
+
+            mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
+                    GPS_CAPABILITY_MEASUREMENTS));
+            mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
+                    GPS_CAPABILITY_NAV_MESSAGES));
+            restartRequests();
         });
     }
 
@@ -1710,27 +1628,21 @@
         updateRequirements();
     }
 
-    /**
-     * Called from native code to inform us the hardware year.
-     */
+    @NativeEntryPoint
     private void setGnssYearOfHardware(final int yearOfHardware) {
         // mHardwareYear is simply set here, to be read elsewhere, and is volatile for safe sync
         if (DEBUG) Log.d(TAG, "setGnssYearOfHardware called with " + yearOfHardware);
         mHardwareYear = yearOfHardware;
     }
 
-    /**
-     * Called from native code to inform us the hardware model name.
-     */
+    @NativeEntryPoint
     private void setGnssHardwareModelName(final String modelName) {
         // mHardwareModelName is simply set here, to be read elsewhere, and volatile for safe sync
         if (DEBUG) Log.d(TAG, "setGnssModelName called with " + modelName);
         mHardwareModelName = modelName;
     }
 
-    /**
-     * Called from native code to inform us GNSS HAL service died.
-     */
+    @NativeEntryPoint
     private void reportGnssServiceDied() {
         if (DEBUG) Log.d(TAG, "reportGnssServiceDied");
         mHandler.post(() -> {
@@ -1750,6 +1662,7 @@
          * Returns the year of underlying GPS hardware.
          */
         int getGnssYearOfHardware();
+
         /**
          * Returns the model name of underlying GPS hardware.
          */
@@ -1765,6 +1678,7 @@
             public int getGnssYearOfHardware() {
                 return mHardwareYear;
             }
+
             @Override
             public String getGnssHardwareModelName() {
                 return mHardwareModelName;
@@ -1790,32 +1704,19 @@
      * @hide
      */
     public GnssMetricsProvider getGnssMetricsProvider() {
-        return new GnssMetricsProvider() {
-            @Override
-            public String getGnssMetricsAsProtoString() {
-                return mGnssMetrics.dumpGnssMetricsAsProtoString();
-            }
-        };
+        return () -> mGnssMetrics.dumpGnssMetricsAsProtoString();
     }
 
-    /**
-     * called from native code - GNSS location batch callback
-     */
+    @NativeEntryPoint
     private void reportLocationBatch(Location[] locationArray) {
         List<Location> locations = new ArrayList<>(Arrays.asList(locationArray));
         if (DEBUG) {
             Log.d(TAG, "Location batch of size " + locationArray.length + " reported");
         }
-        try {
-            mILocationManager.reportLocationBatch(locations);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException calling reportLocationBatch");
-        }
+        reportLocation(locations);
     }
 
-    /**
-     * called from native code to request XTRA data
-     */
+    @NativeEntryPoint
     private void xtraDownloadRequest() {
         if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
         sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
@@ -1824,7 +1725,7 @@
     /**
      * Converts the GPS HAL status to the internal Geofence Hardware status.
      */
-    private int getGeofenceStatus(int status) {
+    private static int getGeofenceStatus(int status) {
         switch (status) {
             case GPS_GEOFENCE_OPERATION_SUCCESS:
                 return GeofenceHardware.GEOFENCE_SUCCESS;
@@ -1843,81 +1744,80 @@
         }
     }
 
-    /**
-     * Called from native to report GPS Geofence transition
-     * All geofence callbacks are called on the same thread
-     */
+    @NativeEntryPoint
     private void reportGeofenceTransition(int geofenceId, Location location, int transition,
             long transitionTimestamp) {
-        if (mGeofenceHardwareImpl == null) {
-            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-        }
+        mHandler.post(() -> {
+            if (mGeofenceHardwareImpl == null) {
+                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+            }
 
-        mGeofenceHardwareImpl.reportGeofenceTransition(
-                geofenceId,
-                location,
-                transition,
-                transitionTimestamp,
-                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
-                FusedBatchOptions.SourceTechnologies.GNSS);
+            mGeofenceHardwareImpl.reportGeofenceTransition(
+                    geofenceId,
+                    location,
+                    transition,
+                    transitionTimestamp,
+                    GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+                    FusedBatchOptions.SourceTechnologies.GNSS);
+        });
     }
 
-    /**
-     * called from native code to report GPS status change.
-     */
+    @NativeEntryPoint
     private void reportGeofenceStatus(int status, Location location) {
-        if (mGeofenceHardwareImpl == null) {
-            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-        }
-        int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
-        if (status == GPS_GEOFENCE_AVAILABLE) {
-            monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
-        }
-        mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
-                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
-                monitorStatus,
-                location,
-                FusedBatchOptions.SourceTechnologies.GNSS);
+        mHandler.post(() -> {
+            if (mGeofenceHardwareImpl == null) {
+                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+            }
+            int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
+            if (status == GPS_GEOFENCE_AVAILABLE) {
+                monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
+            }
+            mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
+                    GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+                    monitorStatus,
+                    location,
+                    FusedBatchOptions.SourceTechnologies.GNSS);
+        });
     }
 
-    /**
-     * called from native code - Geofence Add callback
-     */
+    @NativeEntryPoint
     private void reportGeofenceAddStatus(int geofenceId, int status) {
-        if (mGeofenceHardwareImpl == null) {
-            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-        }
-        mGeofenceHardwareImpl.reportGeofenceAddStatus(geofenceId, getGeofenceStatus(status));
+        mHandler.post(() -> {
+            if (mGeofenceHardwareImpl == null) {
+                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+            }
+            mGeofenceHardwareImpl.reportGeofenceAddStatus(geofenceId, getGeofenceStatus(status));
+        });
     }
 
-    /**
-     * called from native code - Geofence Remove callback
-     */
+    @NativeEntryPoint
     private void reportGeofenceRemoveStatus(int geofenceId, int status) {
-        if (mGeofenceHardwareImpl == null) {
-            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-        }
-        mGeofenceHardwareImpl.reportGeofenceRemoveStatus(geofenceId, getGeofenceStatus(status));
+        mHandler.post(() -> {
+            if (mGeofenceHardwareImpl == null) {
+                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+            }
+            mGeofenceHardwareImpl.reportGeofenceRemoveStatus(geofenceId, getGeofenceStatus(status));
+        });
     }
 
-    /**
-     * called from native code - Geofence Pause callback
-     */
+    @NativeEntryPoint
     private void reportGeofencePauseStatus(int geofenceId, int status) {
-        if (mGeofenceHardwareImpl == null) {
-            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-        }
-        mGeofenceHardwareImpl.reportGeofencePauseStatus(geofenceId, getGeofenceStatus(status));
+        mHandler.post(() -> {
+            if (mGeofenceHardwareImpl == null) {
+                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+            }
+            mGeofenceHardwareImpl.reportGeofencePauseStatus(geofenceId, getGeofenceStatus(status));
+        });
     }
 
-    /**
-     * called from native code - Geofence Resume callback
-     */
+    @NativeEntryPoint
     private void reportGeofenceResumeStatus(int geofenceId, int status) {
-        if (mGeofenceHardwareImpl == null) {
-            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-        }
-        mGeofenceHardwareImpl.reportGeofenceResumeStatus(geofenceId, getGeofenceStatus(status));
+        mHandler.post(() -> {
+            if (mGeofenceHardwareImpl == null) {
+                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+            }
+            mGeofenceHardwareImpl.reportGeofenceResumeStatus(geofenceId, getGeofenceStatus(status));
+        });
     }
 
     //=============================================================
@@ -1942,7 +1842,8 @@
         return mNetInitiatedListener;
     }
 
-    // Called by JNI function to report an NI request.
+    /** Reports a NI notification. */
+    @NativeEntryPoint
     public void reportNiNotification(
             int notificationId,
             int niType,
@@ -1985,11 +1886,10 @@
     }
 
     /**
-     * Called from native code to request set id info.
      * We should be careful about receiving null string from the TelephonyManager,
      * because sending null String to JNI function would cause a crash.
      */
-
+    @NativeEntryPoint
     private void requestSetID(int flags) {
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2018,9 +1918,7 @@
         native_agps_set_id(type, data);
     }
 
-    /**
-     * Called from native code to request location info.
-     */
+    @NativeEntryPoint
     private void requestLocation(boolean independentFromGnss) {
         if (DEBUG) {
             Log.d(TAG, "requestLocation. independentFromGnss: " + independentFromGnss);
@@ -2028,17 +1926,13 @@
         sendMessage(REQUEST_LOCATION, 0, independentFromGnss);
     }
 
-    /**
-     * Called from native code to request utc time info
-     */
+    @NativeEntryPoint
     private void requestUtcTime() {
         if (DEBUG) Log.d(TAG, "utcTimeRequest");
         sendMessage(INJECT_NTP_TIME, 0, null);
     }
 
-    /**
-     * Called from native code to request reference location info
-     */
+    @NativeEntryPoint
     private void requestRefLocation() {
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2092,11 +1986,7 @@
             int message = msg.what;
             switch (message) {
                 case ENABLE:
-                    if (msg.arg1 == 1) {
-                        handleEnable();
-                    } else {
-                        handleDisable();
-                    }
+                    handleEnable();
                     break;
                 case SET_REQUEST:
                     GpsRequest gpsRequest = (GpsRequest) msg.obj;
@@ -2141,7 +2031,8 @@
         }
 
         /**
-         * This method is bound to {@link #GnssLocationProvider(Context, ILocationManager, Looper)}.
+         * This method is bound to {@link #GnssLocationProvider(Context, LocationProviderManager,
+         * Looper)}.
          * It is in charge of loading properties and registering for events that will be posted to
          * this handler.
          */
@@ -2194,12 +2085,11 @@
                     (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
             long minTime = 0;
             float minDistance = 0;
-            boolean oneShot = false;
             LocationRequest request = LocationRequest.createFromDeprecatedProvider(
                     LocationManager.PASSIVE_PROVIDER,
                     minTime,
                     minDistance,
-                    oneShot);
+                    false);
             // Don't keep track of this request since it's done on behalf of other clients
             // (which are kept track of separately).
             request.setHideFromAppOps(true);
@@ -2207,11 +2097,14 @@
                     request,
                     new NetworkLocationListener(),
                     getLooper());
+
+            // enable gps provider, it will never be disabled (legacy behavior)
+            sendEmptyMessage(ENABLE);
         }
     }
 
     private abstract class LocationChangeListener implements LocationListener {
-        int numLocationUpdateRequest;
+        private int mNumLocationUpdateRequest;
 
         @Override
         public void onStatusChanged(String provider, int status, Bundle extras) {
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
index 0add863..3e2ba87 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
@@ -38,7 +38,6 @@
     private static final String TAG = "GnssMeasurementsProvider";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private final Context mContext;
     private final GnssMeasurementProviderNative mNative;
 
     private boolean mIsCollectionStarted;
@@ -51,8 +50,7 @@
     @VisibleForTesting
     GnssMeasurementsProvider(Context context, Handler handler,
             GnssMeasurementProviderNative aNative) {
-        super(handler, TAG);
-        mContext = context;
+        super(context, handler, TAG);
         mNative = aNative;
     }
 
@@ -98,9 +96,13 @@
     }
 
     public void onMeasurementsAvailable(final GnssMeasurementsEvent event) {
-        ListenerOperation<IGnssMeasurementsListener> operation =
-                listener -> listener.onGnssMeasurementsReceived(event);
-        foreach(operation);
+        foreach((IGnssMeasurementsListener listener, int uid, String packageName) -> {
+            if (!hasPermission(uid, packageName)) {
+                logPermissionDisabledEventNotReported(TAG, packageName, "GNSS measurements");
+                return;
+            }
+            listener.onGnssMeasurementsReceived(event);
+        });
     }
 
     public void onCapabilitiesUpdated(boolean isGnssMeasurementsSupported) {
@@ -149,7 +151,8 @@
         }
 
         @Override
-        public void execute(IGnssMeasurementsListener listener) throws RemoteException {
+        public void execute(IGnssMeasurementsListener listener,
+                int uid, String packageName) throws RemoteException {
             listener.onStatusChanged(mStatus);
         }
     }
diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
index 1b4fd18..679919f 100644
--- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location;
 
+import android.content.Context;
 import android.location.GnssNavigationMessage;
 import android.location.IGnssNavigationMessageListener;
 import android.os.Handler;
@@ -39,13 +40,14 @@
     private final GnssNavigationMessageProviderNative mNative;
     private boolean mCollectionStarted;
 
-    protected GnssNavigationMessageProvider(Handler handler) {
-        this(handler, new GnssNavigationMessageProviderNative());
+    protected GnssNavigationMessageProvider(Context context, Handler handler) {
+        this(context, handler, new GnssNavigationMessageProviderNative());
     }
 
     @VisibleForTesting
-    GnssNavigationMessageProvider(Handler handler, GnssNavigationMessageProviderNative aNative) {
-        super(handler, TAG);
+    GnssNavigationMessageProvider(Context context, Handler handler,
+            GnssNavigationMessageProviderNative aNative) {
+        super(context, handler, TAG);
         mNative = aNative;
     }
 
@@ -84,15 +86,10 @@
     }
 
     public void onNavigationMessageAvailable(final GnssNavigationMessage event) {
-        ListenerOperation<IGnssNavigationMessageListener> operation =
-                new ListenerOperation<IGnssNavigationMessageListener>() {
-                    @Override
-                    public void execute(IGnssNavigationMessageListener listener)
-                            throws RemoteException {
-                        listener.onGnssNavigationMessageReceived(event);
-                    }
-                };
-        foreach(operation);
+        foreach((IGnssNavigationMessageListener listener, int uid, String packageName) -> {
+                    listener.onGnssNavigationMessageReceived(event);
+                }
+        );
     }
 
     public void onCapabilitiesUpdated(boolean isGnssNavigationMessageSupported) {
@@ -138,7 +135,8 @@
         }
 
         @Override
-        public void execute(IGnssNavigationMessageListener listener) throws RemoteException {
+        public void execute(IGnssNavigationMessageListener listener,
+                int uid, String packageName) throws RemoteException {
             listener.onStatusChanged(mStatus);
         }
     }
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index 124220f..454dbdd 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -16,16 +16,20 @@
 
 package com.android.server.location;
 
+import android.content.Context;
 import android.location.IGnssStatusListener;
 import android.os.Handler;
-import android.os.RemoteException;
+import android.util.Log;
 
 /**
  * Implementation of a handler for {@link IGnssStatusListener}.
  */
-abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatusListener> {
-    protected GnssStatusListenerHelper(Handler handler) {
-        super(handler, "GnssStatusListenerHelper");
+public abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatusListener> {
+    private static final String TAG = "GnssStatusListenerHelper";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    protected GnssStatusListenerHelper(Context context, Handler handler) {
+        super(context, handler, TAG);
         setSupported(GnssLocationProvider.isSupported());
     }
 
@@ -43,33 +47,22 @@
     }
 
     public void onStatusChanged(boolean isNavigating) {
-        Operation operation;
         if (isNavigating) {
-            operation = new Operation() {
-                @Override
-                public void execute(IGnssStatusListener listener) throws RemoteException {
-                    listener.onGnssStarted();
-                }
-            };
+            foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+                listener.onGnssStarted();
+            });
         } else {
-            operation = new Operation() {
-                @Override
-                public void execute(IGnssStatusListener listener) throws RemoteException {
-                    listener.onGnssStopped();
-                }
-            };
+            foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+                listener.onGnssStopped();
+            });
         }
-        foreach(operation);
     }
 
     public void onFirstFix(final int timeToFirstFix) {
-        Operation operation = new Operation() {
-            @Override
-            public void execute(IGnssStatusListener listener) throws RemoteException {
-                listener.onFirstFix(timeToFirstFix);
-            }
-        };
-        foreach(operation);
+        foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+                    listener.onFirstFix(timeToFirstFix);
+                }
+        );
     }
 
     public void onSvStatusChanged(
@@ -79,30 +72,23 @@
             final float[] elevations,
             final float[] azimuths,
             final float[] carrierFreqs) {
-        Operation operation = new Operation() {
-            @Override
-            public void execute(IGnssStatusListener listener) throws RemoteException {
-                listener.onSvStatusChanged(
-                        svCount,
-                        prnWithFlags,
-                        cn0s,
-                        elevations,
-                        azimuths,
-                        carrierFreqs);
+        foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+            if (!hasPermission(uid, packageName)) {
+                logPermissionDisabledEventNotReported(TAG, packageName, "GNSS status");
+                return;
             }
-        };
-        foreach(operation);
+            listener.onSvStatusChanged(svCount, prnWithFlags, cn0s, elevations, azimuths,
+                    carrierFreqs);
+        });
     }
 
     public void onNmeaReceived(final long timestamp, final String nmea) {
-        Operation operation = new Operation() {
-            @Override
-            public void execute(IGnssStatusListener listener) throws RemoteException {
-                listener.onNmeaReceived(timestamp, nmea);
+        foreach((IGnssStatusListener listener, int uid, String packageName) -> {
+            if (!hasPermission(uid, packageName)) {
+                logPermissionDisabledEventNotReported(TAG, packageName, "NMEA");
+                return;
             }
-        };
-        foreach(operation);
+            listener.onNmeaReceived(timestamp, nmea);
+        });
     }
-
-    private interface Operation extends ListenerOperation<IGnssStatusListener> {}
 }
diff --git a/services/core/java/com/android/server/location/LocationProviderInterface.java b/services/core/java/com/android/server/location/LocationProviderInterface.java
deleted file mode 100644
index 6785964..0000000
--- a/services/core/java/com/android/server/location/LocationProviderInterface.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.location;
-
-import android.location.LocationProvider;
-import android.os.Bundle;
-import android.os.WorkSource;
-
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Location Manager's interface for location providers.
- * @hide
- */
-public abstract class LocationProviderInterface {
-
-    /** Get name. */
-    public abstract String getName();
-
-    /** Enable. */
-    public abstract void enable();
-
-    /** Disable. */
-    public abstract void disable();
-
-    /** Is enabled. */
-    public abstract boolean isEnabled();
-
-    /** Set request. */
-    public abstract void setRequest(ProviderRequest request, WorkSource source);
-
-    /** dump. */
-    public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
-
-    /** Get properties. */
-    public abstract ProviderProperties getProperties();
-
-    /**
-     * Get status.
-     *
-     * @deprecated Will be removed in a future release.
-     */
-    @Deprecated
-    public int getStatus(Bundle extras) {
-        return LocationProvider.AVAILABLE;
-    }
-
-    /**
-     * Get status update time.
-     *
-     * @deprecated Will be removed in a future release.
-     */
-    @Deprecated
-    public long getStatusUpdateTime() {
-        return 0;
-    }
-
-    /** Send extra command. */
-    public abstract boolean sendExtraCommand(String command, Bundle extras);
-}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index b408414..dfcef70 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -18,6 +18,7 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.location.Location;
 import android.location.LocationProvider;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -27,6 +28,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ILocationProviderManager;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.os.BackgroundThread;
@@ -41,21 +43,35 @@
 /**
  * Proxy for ILocationProvider implementations.
  */
-public class LocationProviderProxy extends LocationProviderInterface {
+public class LocationProviderProxy extends AbstractLocationProvider {
+
     private static final String TAG = "LocationProviderProxy";
     private static final boolean D = LocationManagerService.D;
 
-    private final ServiceWatcher mServiceWatcher;
-
-    private final String mName;
-
     // used to ensure that updates to mRequest and mWorkSource are atomic
     private final Object mRequestLock = new Object();
 
+    private final ServiceWatcher mServiceWatcher;
 
-    private volatile boolean mEnabled = false;
-    @Nullable
-    private volatile ProviderProperties mProperties;
+    private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() {
+        // executed on binder thread
+        @Override
+        public void onSetEnabled(boolean enabled) {
+            LocationProviderProxy.this.setEnabled(enabled);
+        }
+
+        // executed on binder thread
+        @Override
+        public void onSetProperties(ProviderProperties properties) {
+            LocationProviderProxy.this.setProperties(properties);
+        }
+
+        // executed on binder thread
+        @Override
+        public void onReportLocation(Location location) {
+            LocationProviderProxy.this.reportLocation(location);
+        }
+    };
 
     @GuardedBy("mRequestLock")
     @Nullable
@@ -69,10 +85,10 @@
      */
     @Nullable
     public static LocationProviderProxy createAndBind(
-            Context context, String name, String action,
+            Context context, LocationProviderManager locationProviderManager, String action,
             int overlaySwitchResId, int defaultServicePackageNameResId,
             int initialPackageNamesResId) {
-        LocationProviderProxy proxy = new LocationProviderProxy(context, name,
+        LocationProviderProxy proxy = new LocationProviderProxy(context, locationProviderManager,
                 action, overlaySwitchResId, defaultServicePackageNameResId,
                 initialPackageNamesResId);
         if (proxy.bind()) {
@@ -82,21 +98,27 @@
         }
     }
 
-    private LocationProviderProxy(Context context, String name,
+    private LocationProviderProxy(Context context, LocationProviderManager locationProviderManager,
             String action, int overlaySwitchResId, int defaultServicePackageNameResId,
             int initialPackageNamesResId) {
+        super(locationProviderManager, false);
 
         mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
                 defaultServicePackageNameResId, initialPackageNamesResId,
                 BackgroundThread.getHandler()) {
+
             @Override
             protected void onBind() {
                 runOnBinder(LocationProviderProxy.this::initializeService);
             }
-        };
-        mName = name;
 
-        mProperties = null;
+            @Override
+            protected void onUnbind() {
+                setEnabled(false);
+                setProperties(null);
+            }
+        };
+
         mRequest = null;
         mWorkSource = new WorkSource();
     }
@@ -107,84 +129,27 @@
 
     private void initializeService(IBinder binder) {
         ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-        if (D) Log.d(TAG, "applying state to connected service");
-
-        ProviderProperties[] properties = new ProviderProperties[1];
-        ProviderRequest request;
-        WorkSource source;
-        synchronized (mRequestLock) {
-            request = mRequest;
-            source = mWorkSource;
-        }
+        if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher);
 
         try {
-            // load properties from provider
-            properties[0] = service.getProperties();
-            if (properties[0] == null) {
-                Log.e(TAG, mServiceWatcher.getCurrentPackageName()
-                        + " has invalid location provider properties");
-            }
+            service.setLocationProviderManager(mManager);
 
-            // apply current state to new service
-            if (mEnabled) {
-                service.enable();
-                if (request != null) {
-                    service.setRequest(request, source);
+            synchronized (mRequestLock) {
+                if (mRequest != null) {
+                    service.setRequest(mRequest, mWorkSource);
                 }
             }
         } catch (RemoteException e) {
             Log.w(TAG, e);
         }
-
-        mProperties = properties[0];
     }
 
+    @Nullable
     public String getConnectedPackageName() {
         return mServiceWatcher.getCurrentPackageName();
     }
 
     @Override
-    public String getName() {
-        return mName;
-    }
-
-    @Override
-    public ProviderProperties getProperties() {
-        return mProperties;
-    }
-
-    @Override
-    public void enable() {
-        mEnabled = true;
-        mServiceWatcher.runOnBinder(binder -> {
-            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-            try {
-                service.enable();
-            } catch (RemoteException e) {
-                Log.w(TAG, e);
-            }
-        });
-    }
-
-    @Override
-    public void disable() {
-        mEnabled = false;
-        mServiceWatcher.runOnBinder(binder -> {
-            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-            try {
-                service.disable();
-            } catch (RemoteException e) {
-                Log.w(TAG, e);
-            }
-        });
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return mEnabled;
-    }
-
-    @Override
     public void setRequest(ProviderRequest request, WorkSource source) {
         synchronized (mRequestLock) {
             mRequest = request;
@@ -202,60 +167,53 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.append("REMOTE SERVICE");
-        pw.append(" name=").append(mName);
-        pw.append(" pkg=").append(mServiceWatcher.getCurrentPackageName());
-        pw.append(" version=").append(Integer.toString(mServiceWatcher.getCurrentPackageVersion()));
-        pw.append('\n');
+        pw.println(" service=" + mServiceWatcher);
         mServiceWatcher.runOnBinder(binder -> {
-            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
             try {
-                TransferPipe.dumpAsync(service.asBinder(), fd, args);
+                TransferPipe.dumpAsync(binder, fd, args);
             } catch (IOException | RemoteException e) {
-                pw.println("Failed to dump location provider: " + e);
+                pw.println(" failed to dump location provider: " + e);
             }
         });
     }
 
     @Override
     public int getStatus(Bundle extras) {
-        int[] result = new int[]{LocationProvider.TEMPORARILY_UNAVAILABLE};
+        int[] status = new int[] {LocationProvider.TEMPORARILY_UNAVAILABLE};
         mServiceWatcher.runOnBinder(binder -> {
             ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
             try {
-                result[0] = service.getStatus(extras);
+                status[0] = service.getStatus(extras);
             } catch (RemoteException e) {
                 Log.w(TAG, e);
             }
         });
-        return result[0];
+        return status[0];
     }
 
     @Override
     public long getStatusUpdateTime() {
-        long[] result = new long[]{0L};
+        long[] updateTime = new long[] {0L};
         mServiceWatcher.runOnBinder(binder -> {
             ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
             try {
-                result[0] = service.getStatusUpdateTime();
+                updateTime[0] = service.getStatusUpdateTime();
             } catch (RemoteException e) {
                 Log.w(TAG, e);
             }
         });
-        return result[0];
+        return updateTime[0];
     }
 
     @Override
-    public boolean sendExtraCommand(String command, Bundle extras) {
-        boolean[] result = new boolean[]{false};
+    public void sendExtraCommand(String command, Bundle extras) {
         mServiceWatcher.runOnBinder(binder -> {
             ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
             try {
-                result[0] = service.sendExtraCommand(command, extras);
+                service.sendExtraCommand(command, extras);
             } catch (RemoteException e) {
                 Log.w(TAG, e);
             }
         });
-        return result[0];
     }
 }
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 145aee3..bfbebf7 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -16,14 +16,11 @@
 
 package com.android.server.location;
 
-import android.location.ILocationManager;
+import android.annotation.Nullable;
 import android.location.Location;
 import android.location.LocationProvider;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.WorkSource;
-import android.util.Log;
-import android.util.PrintWriterPrinter;
 
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
@@ -36,63 +33,57 @@
  *
  * {@hide}
  */
-public class MockProvider extends LocationProviderInterface {
-    private final String mName;
-    private final ProviderProperties mProperties;
-    private final ILocationManager mLocationManager;
+public class MockProvider extends AbstractLocationProvider {
 
-    private final Location mLocation;
-
-    private boolean mHasLocation;
     private boolean mEnabled;
-
-
+    @Nullable private Location mLocation;
     private int mStatus;
     private long mStatusUpdateTime;
     private Bundle mExtras;
 
-    private static final String TAG = "MockProvider";
+    public MockProvider(
+            LocationProviderManager locationProviderManager, ProviderProperties properties) {
+        super(locationProviderManager, true);
 
-    public MockProvider(String name, ILocationManager locationManager,
-            ProviderProperties properties) {
-        if (properties == null) throw new NullPointerException("properties is null");
-
-        mName = name;
-        mLocationManager = locationManager;
-        mProperties = properties;
-        mLocation = new Location(name);
-
-        mStatus = LocationProvider.AVAILABLE;
-        mStatusUpdateTime = 0L;
-        mExtras = null;
-    }
-
-    @Override
-    public String getName() {
-        return mName;
-    }
-
-    @Override
-    public ProviderProperties getProperties() {
-        return mProperties;
-    }
-
-    @Override
-    public void disable() {
-        mEnabled = false;
-    }
-
-    @Override
-    public void enable() {
         mEnabled = true;
+        mLocation = null;
+        mStatus = LocationProvider.AVAILABLE;
+        mStatusUpdateTime = 0;
+        mExtras = null;
+
+        setProperties(properties);
+    }
+
+    /** Sets the enabled state of this mock provider. */
+    public void setEnabled(boolean enabled) {
+        mEnabled = enabled;
+        super.setEnabled(enabled);
+    }
+
+    /** Sets the location to report for this mock provider. */
+    public void setLocation(Location l) {
+        mLocation = new Location(l);
+        if (mEnabled) {
+            reportLocation(l);
+        }
+    }
+
+    /** Sets the status for this mock provider. */
+    public void setStatus(int status, Bundle extras, long updateTime) {
+        mStatus = status;
+        mStatusUpdateTime = updateTime;
+        mExtras = extras;
     }
 
     @Override
-    public boolean isEnabled() {
-        return mEnabled;
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(" last location=" + mLocation);
     }
 
     @Override
+    public void setRequest(ProviderRequest request, WorkSource source) {}
+
+    @Override
     public int getStatus(Bundle extras) {
         if (mExtras != null) {
             extras.clear();
@@ -107,50 +98,6 @@
         return mStatusUpdateTime;
     }
 
-    public void setLocation(Location l) {
-        mLocation.set(l);
-        mHasLocation = true;
-        if (mEnabled) {
-            try {
-                mLocationManager.reportLocation(mLocation, false);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException calling reportLocation");
-            }
-        }
-    }
-
-    public void clearLocation() {
-        mHasLocation = false;
-    }
-
-    /**
-     * @deprecated Will be removed in a future release.
-     */
-    @Deprecated
-    public void setStatus(int status, Bundle extras, long updateTime) {
-        mStatus = status;
-        mStatusUpdateTime = updateTime;
-        mExtras = extras;
-    }
-
     @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        dump(pw, "");
-    }
-
-    public void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + mName);
-        pw.println(prefix + "mHasLocation=" + mHasLocation);
-        pw.println(prefix + "mLocation:");
-        mLocation.dump(new PrintWriterPrinter(pw), prefix + "  ");
-        pw.println(prefix + "mExtras=" + mExtras);
-    }
-
-    @Override
-    public void setRequest(ProviderRequest request, WorkSource source) { }
-
-    @Override
-    public boolean sendExtraCommand(String command, Bundle extras) {
-        return false;
-    }
+    public void sendExtraCommand(String command, Bundle extras) {}
 }
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 99c9214..70d64b0 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -17,13 +17,9 @@
 package com.android.server.location;
 
 import android.location.Criteria;
-import android.location.ILocationManager;
 import android.location.Location;
-import android.location.LocationManager;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.WorkSource;
-import android.util.Log;
 
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
@@ -38,41 +34,20 @@
  *
  * {@hide}
  */
-public class PassiveProvider extends LocationProviderInterface {
-    private static final String TAG = "PassiveProvider";
+public class PassiveProvider extends AbstractLocationProvider {
 
     private static final ProviderProperties PROPERTIES = new ProviderProperties(
             false, false, false, false, false, false, false,
             Criteria.POWER_LOW, Criteria.ACCURACY_COARSE);
 
-    private final ILocationManager mLocationManager;
     private boolean mReportLocation;
 
-    public PassiveProvider(ILocationManager locationManager) {
-        mLocationManager = locationManager;
-    }
+    public PassiveProvider(LocationProviderManager locationProviderManager) {
+        super(locationProviderManager, true);
 
-    @Override
-    public String getName() {
-        return LocationManager.PASSIVE_PROVIDER;
-    }
+        mReportLocation = false;
 
-    @Override
-    public ProviderProperties getProperties() {
-        return PROPERTIES;
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return true;
-    }
-
-    @Override
-    public void enable() {
-    }
-
-    @Override
-    public void disable() {
+        setProperties(PROPERTIES);
     }
 
     @Override
@@ -82,22 +57,15 @@
 
     public void updateLocation(Location location) {
         if (mReportLocation) {
-            try {
-                // pass the location back to the location manager
-                mLocationManager.reportLocation(location, true);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException calling reportLocation");
-            }
+            reportLocation(location);
         }
     }
 
     @Override
-    public boolean sendExtraCommand(String command, Bundle extras) {
-        return false;
-    }
+    public void sendExtraCommand(String command, Bundle extras) {}
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("mReportLocation=" + mReportLocation);
+        pw.println(" report location=" + mReportLocation);
     }
 }
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index fcdb9d1..37d43fc 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -17,6 +17,8 @@
 package com.android.server.location;
 
 import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
@@ -46,6 +48,9 @@
 
     private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
 
+    protected final Context mContext;
+    protected final AppOpsManager mAppOps;
+
     private volatile boolean mIsRegistered;  // must access only on handler thread, or read-only
 
     private boolean mHasIsSupported;
@@ -53,10 +58,12 @@
 
     private int mLastReportedResult = RESULT_UNKNOWN;
 
-    protected RemoteListenerHelper(Handler handler, String name) {
+    protected RemoteListenerHelper(Context context, Handler handler, String name) {
         Preconditions.checkNotNull(name);
         mHandler = handler;
         mTag = name;
+        mContext = context;
+        mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
     }
 
     // read-only access for a dump() thread assured via volatile
@@ -64,10 +71,10 @@
         return mIsRegistered;
     }
 
-    public boolean addListener(@NonNull TListener listener) {
+    public boolean addListener(@NonNull TListener listener, int uid, String packageName) {
         Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
         IBinder binder = listener.asBinder();
-        LinkedListener deathListener = new LinkedListener(listener);
+        LinkedListener deathListener = new LinkedListener(listener, uid, packageName);
         synchronized (mListenerMap) {
             if (mListenerMap.containsKey(binder)) {
                 // listener already added
@@ -102,7 +109,7 @@
                 // asynchronously in the future
                 return true;
             }
-            post(listener, getHandlerOperation(result));
+            post(deathListener, getHandlerOperation(result));
         }
         return true;
     }
@@ -130,7 +137,7 @@
     protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
 
     protected interface ListenerOperation<TListener extends IInterface> {
-        void execute(TListener listener) throws RemoteException;
+        void execute(TListener listener, int uid, String packageName) throws RemoteException;
     }
 
     protected void foreach(ListenerOperation<TListener> operation) {
@@ -170,15 +177,28 @@
         }
     }
 
-    private void foreachUnsafe(ListenerOperation<TListener> operation) {
-        for (LinkedListener linkedListener : mListenerMap.values()) {
-            post(linkedListener.getUnderlyingListener(), operation);
+    protected boolean hasPermission(int uid, String packageName) {
+        return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, uid, packageName)
+                == AppOpsManager.MODE_ALLOWED;
+    }
+
+    protected void logPermissionDisabledEventNotReported(String tag, String packageName,
+            String event) {
+        if (Log.isLoggable(tag, Log.DEBUG)) {
+            Log.d(tag, "Location permission disabled. Skipping " + event + " reporting for app: "
+                    + packageName);
         }
     }
 
-    private void post(TListener listener, ListenerOperation<TListener> operation) {
+    private void foreachUnsafe(ListenerOperation<TListener> operation) {
+        for (LinkedListener linkedListener : mListenerMap.values()) {
+            post(linkedListener, operation);
+        }
+    }
+
+    private void post(LinkedListener linkedListener, ListenerOperation<TListener> operation) {
         if (operation != null) {
-            mHandler.post(new HandlerRunnable(listener, operation));
+            mHandler.post(new HandlerRunnable(linkedListener, operation));
         }
     }
 
@@ -193,13 +213,9 @@
                 }
                 if (!mIsRegistered) {
                     // post back a failure
-                    mHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            synchronized (mListenerMap) {
-                                ListenerOperation<TListener> operation = getHandlerOperation(registrationState);
-                                foreachUnsafe(operation);
-                            }
+                    mHandler.post(() -> {
+                        synchronized (mListenerMap) {
+                            foreachUnsafe(getHandlerOperation(registrationState));
                         }
                     });
                 }
@@ -208,16 +224,14 @@
     }
 
     private void tryUnregister() {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (!mIsRegistered) {
-                    return;
+        mHandler.post(() -> {
+                    if (!mIsRegistered) {
+                        return;
+                    }
+                    unregisterFromService();
+                    mIsRegistered = false;
                 }
-                unregisterFromService();
-                mIsRegistered = false;
-            }
-        });
+        );
     }
 
     private int calculateCurrentResultUnsafe() {
@@ -240,14 +254,13 @@
 
     private class LinkedListener implements IBinder.DeathRecipient {
         private final TListener mListener;
+        private final int mUid;
+        private final String mPackageName;
 
-        public LinkedListener(@NonNull TListener listener) {
+        LinkedListener(@NonNull TListener listener, int uid, String packageName) {
             mListener = listener;
-        }
-
-        @NonNull
-        public TListener getUnderlyingListener() {
-            return mListener;
+            mUid = uid;
+            mPackageName = packageName;
         }
 
         @Override
@@ -258,18 +271,19 @@
     }
 
     private class HandlerRunnable implements Runnable {
-        private final TListener mListener;
+        private final LinkedListener mLinkedListener;
         private final ListenerOperation<TListener> mOperation;
 
-        public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) {
-            mListener = listener;
+        HandlerRunnable(LinkedListener linkedListener, ListenerOperation<TListener> operation) {
+            mLinkedListener = linkedListener;
             mOperation = operation;
         }
 
         @Override
         public void run() {
             try {
-                mOperation.execute(mListener);
+                mOperation.execute(mLinkedListener.mListener, mLinkedListener.mUid,
+                        mLinkedListener.mPackageName);
             } catch (RemoteException e) {
                 Log.v(mTag, "Error in monitored listener.", e);
             }
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index d32c299..0e195bc 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -29,6 +29,7 @@
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.security.GateKeeper;
+import android.security.Scrypt;
 import android.service.gatekeeper.GateKeeperResponse;
 import android.service.gatekeeper.IGateKeeperService;
 import android.util.ArrayMap;
@@ -1173,11 +1174,10 @@
     }
 
     protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) {
-        return nativeScrypt(password.getBytes(), salt, N, r, p, outLen);
+        return new Scrypt().scrypt(password.getBytes(), salt, N, r, p, outLen);
     }
 
     native long nativeSidFromPasswordHandle(byte[] handle);
-    native byte[] nativeScrypt(byte[] password, byte[] salt, int N, int r, int p, int outLen);
 
     protected static ArrayList<Byte> toByteArrayList(byte[] data) {
         ArrayList<Byte> result = new ArrayList<Byte>(data.length);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
index 6e08949..26e8270 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
@@ -16,13 +16,17 @@
 
 package com.android.server.locksettings.recoverablekeystore.certificate;
 
-import static javax.xml.xpath.XPathConstants.NODESET;
-
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -40,7 +44,6 @@
 import java.security.cert.CertPathValidator;
 import java.security.cert.CertPathValidatorException;
 import java.security.cert.CertStore;
-import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.CollectionCertStoreParameters;
@@ -58,15 +61,6 @@
 
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
 
 /** Utility functions related to parsing and validating public-key certificates. */
 public final class CertUtils {
@@ -167,50 +161,63 @@
     static List<String> getXmlNodeContents(@MustExist int mustExist, Element rootNode,
             String... nodeTags)
             throws CertParsingException {
-        String expression = String.join("/", nodeTags);
-
-        XPath xPath = XPathFactory.newInstance().newXPath();
-        NodeList nodeList;
-        try {
-            nodeList = (NodeList) xPath.compile(expression).evaluate(rootNode, NODESET);
-        } catch (XPathExpressionException e) {
-            throw new CertParsingException(e);
+        if (nodeTags.length == 0) {
+            throw new CertParsingException("The tag list must not be empty");
         }
 
-        switch (mustExist) {
-            case MUST_EXIST_UNENFORCED:
-                break;
-
-            case MUST_EXIST_EXACTLY_ONE:
-                if (nodeList.getLength() != 1) {
-                    throw new CertParsingException(
-                            "The XML file must contain exactly one node with the path "
-                                    + expression);
-                }
-                break;
-
-            case MUST_EXIST_AT_LEAST_ONE:
-                if (nodeList.getLength() == 0) {
-                    throw new CertParsingException(
-                            "The XML file must contain at least one node with the path "
-                                    + expression);
-                }
-                break;
-
-            default:
-                throw new UnsupportedOperationException(
-                        "This value of MustExist is not supported: " + mustExist);
+        // Go down through all the intermediate node tags (except the last tag for the leaf nodes).
+        // Note that this implementation requires that at most one path exists for the given
+        // intermediate node tags.
+        Element parent = rootNode;
+        for (int i = 0; i < nodeTags.length - 1; i++) {
+            String tag = nodeTags[i];
+            List<Element> children = getXmlDirectChildren(parent, tag);
+            if ((children.size() == 0 && mustExist != MUST_EXIST_UNENFORCED)
+                    || children.size() > 1) {
+                throw new CertParsingException(
+                        "The XML file must contain exactly one path with the tag " + tag);
+            }
+            if (children.size() == 0) {
+                return new ArrayList<>();
+            }
+            parent = children.get(0);
         }
 
+        // Then collect the contents of the leaf nodes.
+        List<Element> leafs = getXmlDirectChildren(parent, nodeTags[nodeTags.length - 1]);
+        if (mustExist == MUST_EXIST_EXACTLY_ONE && leafs.size() != 1) {
+            throw new CertParsingException(
+                    "The XML file must contain exactly one node with the path "
+                            + String.join("/", nodeTags));
+        }
+        if (mustExist == MUST_EXIST_AT_LEAST_ONE && leafs.size() == 0) {
+            throw new CertParsingException(
+                    "The XML file must contain at least one node with the path "
+                            + String.join("/", nodeTags));
+        }
         List<String> result = new ArrayList<>();
-        for (int i = 0; i < nodeList.getLength(); i++) {
-            Node node = nodeList.item(i);
+        for (Element leaf : leafs) {
             // Remove whitespaces and newlines.
-            result.add(node.getTextContent().replaceAll("\\s", ""));
+            result.add(leaf.getTextContent().replaceAll("\\s", ""));
         }
         return result;
     }
 
+    /** Get the direct child nodes with a given tag. */
+    private static List<Element> getXmlDirectChildren(Element parent, String tag) {
+        // Cannot use Element.getElementsByTagName because it will return all descendant elements
+        // with the tag name, i.e. not only the direct child nodes.
+        List<Element> children = new ArrayList<>();
+        NodeList childNodes = parent.getChildNodes();
+        for (int i = 0; i < childNodes.getLength(); i++) {
+            Node node = childNodes.item(i);
+            if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(tag)) {
+                children.add((Element) node);
+            }
+        }
+        return children;
+    }
+
     /**
      * Decodes a base64-encoded string.
      *
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index a16dcf3..a2e7e0c 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -352,7 +352,7 @@
 
         // Clear UID from current stats snapshot
         if (mLastSnapshot != null) {
-            mLastSnapshot = mLastSnapshot.withoutUids(uids);
+            mLastSnapshot.removeUids(uids);
         }
 
         final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 95e1962..f19ecf3 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5511,7 +5511,12 @@
     {
         mHandler.removeCallbacksAndMessages(r);
         Message m = Message.obtain(mHandler, MESSAGE_DURATION_REACHED, r);
-        long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
+        int delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
+        // Accessibility users may need longer timeout duration. This api compares original delay
+        // with user's preference and return longer one. It returns original delay if there's no
+        // preference.
+        delay = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
+                AccessibilityManager.FLAG_CONTENT_TEXT);
         mHandler.sendMessageDelayed(m, delay);
     }
 
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 65ccecd..7ae2271 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
@@ -77,18 +78,20 @@
             IApplicationThread caller,
             String callingPackage,
             ComponentName component,
-            UserHandle user) throws RemoteException {
+            @UserIdInt int userId,
+            boolean launchMainActivity) throws RemoteException {
         Preconditions.checkNotNull(callingPackage);
         Preconditions.checkNotNull(component);
-        Preconditions.checkNotNull(user);
 
         verifyCallingPackage(callingPackage);
 
+        final int callerUserId = mInjector.getCallingUserId();
+        final int callingUid = mInjector.getCallingUid();
+
         List<UserHandle> allowedTargetUsers = getTargetUserProfilesUnchecked(
-                callingPackage, mInjector.getCallingUserId());
-        if (!allowedTargetUsers.contains(user)) {
-            throw new SecurityException(
-                    callingPackage + " cannot access unrelated user " + user.getIdentifier());
+                callingPackage, callerUserId);
+        if (!allowedTargetUsers.contains(UserHandle.of(userId))) {
+            throw new SecurityException(callingPackage + " cannot access unrelated user " + userId);
         }
 
         // Verify that caller package is starting activity in its own package.
@@ -98,25 +101,43 @@
                             + component.getPackageName());
         }
 
-        final int callingUid = mInjector.getCallingUid();
-
-        // Verify that target activity does handle the intent with ACTION_MAIN and
-        // CATEGORY_LAUNCHER as calling startActivityAsUser ignore them if component is present.
-        final Intent launchIntent = new Intent(Intent.ACTION_MAIN);
-        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-        // Only package name is set here, as opposed to component name, because intent action and
-        // category are ignored if component name is present while we are resolving intent.
-        launchIntent.setPackage(component.getPackageName());
-        verifyActivityCanHandleIntentAndExported(launchIntent, component, callingUid, user);
+        // Verify that target activity does handle the intent correctly.
+        final Intent launchIntent = new Intent();
+        if (launchMainActivity) {
+            launchIntent.setAction(Intent.ACTION_MAIN);
+            launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+            // Only package name is set here, as opposed to component name, because intent action
+            // and category are ignored if component name is present while we are resolving intent.
+            launchIntent.setPackage(component.getPackageName());
+        } else {
+            // If the main activity is not being launched and the users are different, the caller
+            // must have the required permission and the users must be in the same profile group
+            // in order to launch any of its own activities.
+            if (callerUserId != userId) {
+                final int permissionFlag = ActivityManager.checkComponentPermission(
+                        android.Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid,
+                        -1, true);
+                if (permissionFlag != PackageManager.PERMISSION_GRANTED
+                        || !mInjector.getUserManager().isSameProfileGroup(callerUserId, userId)) {
+                    throw new SecurityException("Attempt to launch activity without required "
+                            + android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
+                            + " or target user is not in the same profile group.");
+                }
+            }
+            launchIntent.setComponent(component);
+        }
+        verifyActivityCanHandleIntentAndExported(launchIntent, component, callingUid, userId);
 
         launchIntent.setPackage(null);
         launchIntent.setComponent(component);
         mInjector.getActivityTaskManagerInternal().startActivityAsUser(
                 caller, callingPackage, launchIntent,
-                ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(),
-                user.getIdentifier());
+                launchMainActivity
+                        ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle()
+                        : null,
+                userId);
     }
 
     private List<UserHandle> getTargetUserProfilesUnchecked(
@@ -163,7 +184,7 @@
      * activity is exported.
      */
     private void verifyActivityCanHandleIntentAndExported(
-            Intent launchIntent, ComponentName component, int callingUid, UserHandle user) {
+            Intent launchIntent, ComponentName component, int callingUid, @UserIdInt int userId) {
         final long ident = mInjector.clearCallingIdentity();
         try {
             final List<ResolveInfo> apps =
@@ -171,7 +192,7 @@
                             launchIntent,
                             MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                             callingUid,
-                            user.getIdentifier());
+                            userId);
             final int size = apps.size();
             for (int i = 0; i < size; ++i) {
                 final ActivityInfo activityInfo = apps.get(i).activityInfo;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index dca92ea..b36ac98 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -86,11 +86,6 @@
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.PackageParser.isApkFile;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
@@ -1301,6 +1296,7 @@
     final @Nullable String mSetupWizardPackage;
     final @Nullable String mStorageManagerPackage;
     final @Nullable String mSystemTextClassifierPackage;
+    final @Nullable String mWellbeingPackage;
     final @NonNull String mServicesSystemSharedLibraryPackageName;
     final @NonNull String mSharedSystemSharedLibraryPackageName;
 
@@ -2093,28 +2089,6 @@
         }
     }
 
-    @GuardedBy("mPackages")
-    private void setupBuiltinSharedLibraryDependenciesLocked() {
-        // Builtin libraries don't have versions.
-        long version = SharedLibraryInfo.VERSION_UNDEFINED;
-
-        SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ANDROID_HIDL_MANAGER, version);
-        if (libraryInfo != null) {
-            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_HIDL_BASE, version));
-        }
-
-        libraryInfo = getSharedLibraryInfoLPr(ANDROID_TEST_RUNNER, version);
-        if (libraryInfo != null) {
-            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_MOCK, version));
-            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_BASE, version));
-        }
-
-        libraryInfo = getSharedLibraryInfoLPr(ANDROID_TEST_MOCK, version);
-        if (libraryInfo != null) {
-            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_BASE, version));
-        }
-    }
-
     public PackageManagerService(Context context, Installer installer,
             boolean factoryTest, boolean onlyCore) {
         LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
@@ -2222,17 +2196,32 @@
             Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
             mInstantAppRegistry = new InstantAppRegistry(this);
 
-            ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
+            ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig
+                    = systemConfig.getSharedLibraries();
             final int builtInLibCount = libConfig.size();
             for (int i = 0; i < builtInLibCount; i++) {
                 String name = libConfig.keyAt(i);
-                String path = libConfig.valueAt(i);
-                addSharedLibraryLPw(path, null, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
-                        SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
+                SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
+                addSharedLibraryLPw(entry.filename, null, null, name,
+                        SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_BUILTIN,
+                        PLATFORM_PACKAGE_NAME, 0);
             }
-            // Builtin libraries cannot encode their dependency where they are
-            // defined, so fix that now.
-            setupBuiltinSharedLibraryDependenciesLocked();
+
+            // Now that we have added all the libraries, iterate again to add dependency
+            // information IFF their dependencies are added.
+            long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED;
+            for (int i = 0; i < builtInLibCount; i++) {
+                String name = libConfig.keyAt(i);
+                SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
+                final int dependencyCount = entry.dependencies.length;
+                for (int j = 0; j < dependencyCount; j++) {
+                    final SharedLibraryInfo dependency =
+                        getSharedLibraryInfoLPr(entry.dependencies[j], undefinedVersion);
+                    if (dependency != null) {
+                        getSharedLibraryInfoLPr(name, undefinedVersion).addDependency(dependency);
+                    }
+                }
+            }
 
             SELinuxMMAC.readInstallPolicy();
 
@@ -2790,6 +2779,8 @@
 
             mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
 
+            mWellbeingPackage = getWellbeingPackageName();
+
             // Now that we know all of the shared libraries, update all clients to have
             // the correct library paths.
             updateAllSharedLibrariesLPw(null);
@@ -11258,7 +11249,7 @@
                                 == UsesPermissionInfo.USAGE_UNDEFINED
                             || upi.getDataRetention() == UsesPermissionInfo.RETENTION_UNDEFINED) {
                         // STOPSHIP: Make this throw
-                        Slog.wtf(TAG, "Package " + pkg.packageName + " does not provide usage "
+                        Slog.e(TAG, "Package " + pkg.packageName + " does not provide usage "
                                 + "information for permission " + upi.getPermission()
                                 + ". This will be a fatal error in Q.");
                     }
@@ -12836,16 +12827,17 @@
         final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
         final long callingId = Binder.clearCallingIdentity();
         try {
-            synchronized (mPackages) {
-                for (int i = 0; i < packageNames.length; i++) {
-                    final String packageName = packageNames[i];
-                    if (callingPackage.equals(packageName)) {
-                        Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
-                                + (suspended ? "" : "un") + "suspend itself. Ignoring");
-                        unactionedPackages.add(packageName);
-                        continue;
-                    }
-                    final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+            for (int i = 0; i < packageNames.length; i++) {
+                final String packageName = packageNames[i];
+                if (callingPackage.equals(packageName)) {
+                    Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+                            + (suspended ? "" : "un") + "suspend itself. Ignoring");
+                    unactionedPackages.add(packageName);
+                    continue;
+                }
+                PackageSetting pkgSetting;
+                synchronized (mPackages) {
+                    pkgSetting = mSettings.mPackages.get(packageName);
                     if (pkgSetting == null
                             || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
                         Slog.w(TAG, "Could not find package setting for package: " + packageName
@@ -12853,15 +12845,20 @@
                         unactionedPackages.add(packageName);
                         continue;
                     }
-                    if (suspended && !canSuspendPackageForUserLocked(packageName, userId)) {
-                        unactionedPackages.add(packageName);
-                        continue;
-                    }
-                    pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
-                            launcherExtras, userId);
-                    changedPackagesList.add(packageName);
-                    changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
                 }
+                if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) {
+                    unactionedPackages.add(packageName);
+                    continue;
+                }
+                synchronized (mPackages) {
+                    pkgSetting = mSettings.mPackages.get(packageName);
+                    if (pkgSetting != null) {
+                        pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
+                                launcherExtras, userId);
+                    }
+                }
+                changedPackagesList.add(packageName);
+                changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -13018,16 +13015,13 @@
         }
         final long identity = Binder.clearCallingIdentity();
         try {
-            synchronized (mPackages) {
-                return canSuspendPackageForUserLocked(packageName, userId);
-            }
+            return canSuspendPackageForUserInternal(packageName, userId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
-    @GuardedBy("mPackages")
-    private boolean canSuspendPackageForUserLocked(String packageName, int userId) {
+    private boolean canSuspendPackageForUserInternal(String packageName, int userId) {
         if (isPackageDeviceAdmin(packageName, userId)) {
             Slog.w(TAG, "Cannot suspend package \"" + packageName
                     + "\": has an active device admin");
@@ -13071,21 +13065,23 @@
             return false;
         }
 
-        if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
-            Slog.w(TAG, "Cannot suspend package \"" + packageName
-                    + "\": protected package");
-            return false;
-        }
+        synchronized (mPackages) {
+            if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+                Slog.w(TAG, "Cannot suspend package \"" + packageName
+                        + "\": protected package");
+                return false;
+            }
 
-        // Cannot suspend static shared libs as they are considered
-        // a part of the using app (emulating static linking). Also
-        // static libs are installed always on internal storage.
-        PackageParser.Package pkg = mPackages.get(packageName);
-        if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
-            Slog.w(TAG, "Cannot suspend package: " + packageName
-                    + " providing static shared library: "
-                    + pkg.staticSharedLibName);
-            return false;
+            // Cannot suspend static shared libs as they are considered
+            // a part of the using app (emulating static linking). Also
+            // static libs are installed always on internal storage.
+            PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
+                Slog.w(TAG, "Cannot suspend package: " + packageName
+                        + " providing static shared library: "
+                        + pkg.staticSharedLibName);
+                return false;
+            }
         }
 
         if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
@@ -18302,6 +18298,10 @@
                 continue;
             }
 
+            if (bp.isRemoved()) {
+                continue;
+            }
+
             // If shared user we just reset the state to which only this app contributed.
             if (ps.sharedUser != null) {
                 boolean used = false;
@@ -19559,6 +19559,11 @@
     }
 
     @Override
+    public String getWellbeingPackageName() {
+        return mContext.getString(R.string.config_defaultWellbeingPackage);
+    }
+
+    @Override
     public void setApplicationEnabledSetting(String appPackageName,
             int newState, int flags, int userId, String callingPackage) {
         if (!sUserManager.exists(userId)) return;
@@ -22714,6 +22719,8 @@
                     return mSystemTextClassifierPackage;
                 case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
                     return mRequiredPermissionControllerPackage;
+                case PackageManagerInternal.PACKAGE_WELLBEING:
+                    return mWellbeingPackage;
             }
             return null;
         }
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 3a74ab5..36b7269 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -16,6 +16,11 @@
 
 package com.android.server.pm.dex;
 
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
+
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -26,9 +31,9 @@
 import android.os.Build;
 import android.os.FileUtils;
 import android.os.RemoteException;
-import android.os.storage.StorageManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.storage.StorageManager;
 import android.provider.Settings.Global;
 import android.util.Log;
 import android.util.Slog;
@@ -48,18 +53,14 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.zip.ZipEntry;
 
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
-import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
-
 /**
  * This class keeps track of how dex files are used.
  * Every time it gets a notification about a dex file being loaded it tracks
@@ -89,6 +90,12 @@
     // encode and save the dex usage data.
     private final PackageDexUsage mPackageDexUsage;
 
+    // PackageDynamicCodeLoading handles recording of dynamic code loading -
+    // which is similar to PackageDexUsage but records a different aspect of the data.
+    // (It additionally includes DEX files loaded with unsupported class loaders, and doesn't
+    // record class loaders or ISAs.)
+    private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
+
     private final IPackageManager mPackageManager;
     private final PackageDexOptimizer mPackageDexOptimizer;
     private final Object mInstallLock;
@@ -126,14 +133,15 @@
 
     public DexManager(Context context, IPackageManager pms, PackageDexOptimizer pdo,
             Installer installer, Object installLock, Listener listener) {
-      mContext = context;
-      mPackageCodeLocationsCache = new HashMap<>();
-      mPackageDexUsage = new PackageDexUsage();
-      mPackageManager = pms;
-      mPackageDexOptimizer = pdo;
-      mInstaller = installer;
-      mInstallLock = installLock;
-      mListener = listener;
+        mContext = context;
+        mPackageCodeLocationsCache = new HashMap<>();
+        mPackageDexUsage = new PackageDexUsage();
+        mPackageDynamicCodeLoading = new PackageDynamicCodeLoading();
+        mPackageManager = pms;
+        mPackageDexOptimizer = pdo;
+        mInstaller = installer;
+        mInstallLock = installLock;
+        mListener = listener;
     }
 
     public void systemReady() {
@@ -207,7 +215,6 @@
                 Slog.i(TAG, loadingAppInfo.packageName +
                         " uses unsupported class loader in " + classLoaderNames);
             }
-            return;
         }
 
         int dexPathIndex = 0;
@@ -236,15 +243,24 @@
                     continue;
                 }
 
-                // Record dex file usage. If the current usage is a new pattern (e.g. new secondary,
-                // or UsedByOtherApps), record will return true and we trigger an async write
-                // to disk to make sure we don't loose the data in case of a reboot.
+                if (mPackageDynamicCodeLoading.record(searchResult.mOwningPackageName, dexPath,
+                        PackageDynamicCodeLoading.FILE_TYPE_DEX, loaderUserId,
+                        loadingAppInfo.packageName)) {
+                    mPackageDynamicCodeLoading.maybeWriteAsync();
+                }
 
-                String classLoaderContext = classLoaderContexts[dexPathIndex];
-                if (mPackageDexUsage.record(searchResult.mOwningPackageName,
-                        dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
-                        loadingAppInfo.packageName, classLoaderContext)) {
-                    mPackageDexUsage.maybeWriteAsync();
+                if (classLoaderContexts != null) {
+
+                    // Record dex file usage. If the current usage is a new pattern (e.g. new
+                    // secondary, or UsedByOtherApps), record will return true and we trigger an
+                    // async write to disk to make sure we don't loose the data in case of a reboot.
+
+                    String classLoaderContext = classLoaderContexts[dexPathIndex];
+                    if (mPackageDexUsage.record(searchResult.mOwningPackageName,
+                            dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
+                            loadingAppInfo.packageName, classLoaderContext)) {
+                        mPackageDexUsage.maybeWriteAsync();
+                    }
                 }
             } else {
                 // If we can't find the owner of the dex we simply do not track it. The impact is
@@ -268,8 +284,8 @@
             loadInternal(existingPackages);
         } catch (Exception e) {
             mPackageDexUsage.clear();
-            Slog.w(TAG, "Exception while loading package dex usage. " +
-                    "Starting with a fresh state.", e);
+            mPackageDynamicCodeLoading.clear();
+            Slog.w(TAG, "Exception while loading. Starting with a fresh state.", e);
         }
     }
 
@@ -311,15 +327,24 @@
      * all usage information for the package will be removed.
      */
     public void notifyPackageDataDestroyed(String packageName, int userId) {
-        boolean updated = userId == UserHandle.USER_ALL
-            ? mPackageDexUsage.removePackage(packageName)
-            : mPackageDexUsage.removeUserPackage(packageName, userId);
         // In case there was an update, write the package use info to disk async.
-        // Note that we do the writing here and not in PackageDexUsage in order to be
+        // Note that we do the writing here and not in the lower level classes in order to be
         // consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs
         // multiple updates in PackageDexUsage before writing it).
-        if (updated) {
-            mPackageDexUsage.maybeWriteAsync();
+        if (userId == UserHandle.USER_ALL) {
+            if (mPackageDexUsage.removePackage(packageName)) {
+                mPackageDexUsage.maybeWriteAsync();
+            }
+            if (mPackageDynamicCodeLoading.removePackage(packageName)) {
+                mPackageDynamicCodeLoading.maybeWriteAsync();
+            }
+        } else {
+            if (mPackageDexUsage.removeUserPackage(packageName, userId)) {
+                mPackageDexUsage.maybeWriteAsync();
+            }
+            if (mPackageDynamicCodeLoading.removeUserPackage(packageName, userId)) {
+                mPackageDynamicCodeLoading.maybeWriteAsync();
+            }
         }
     }
 
@@ -388,8 +413,23 @@
             }
         }
 
-        mPackageDexUsage.read();
-        mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+        try {
+            mPackageDexUsage.read();
+            mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+        } catch (Exception e) {
+            mPackageDexUsage.clear();
+            Slog.w(TAG, "Exception while loading package dex usage. "
+                    + "Starting with a fresh state.", e);
+        }
+
+        try {
+            mPackageDynamicCodeLoading.read();
+            mPackageDynamicCodeLoading.syncData(packageToUsersMap);
+        } catch (Exception e) {
+            mPackageDynamicCodeLoading.clear();
+            Slog.w(TAG, "Exception while loading package dynamic code usage. "
+                    + "Starting with a fresh state.", e);
+        }
     }
 
     /**
@@ -415,10 +455,16 @@
      * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
      * to access the package use.
      */
+    @VisibleForTesting
     /*package*/ boolean hasInfoOnPackage(String packageName) {
         return mPackageDexUsage.getPackageUseInfo(packageName) != null;
     }
 
+    @VisibleForTesting
+    /*package*/ PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
+        return mPackageDynamicCodeLoading.getPackageDynamicCodeInfo(packageName);
+    }
+
     /**
      * Perform dexopt on with the given {@code options} on the secondary dex files.
      * @return true if all secondary dex files were processed successfully (compiled or skipped
@@ -652,7 +698,7 @@
             // to load dex files through it.
             try {
                 String dexPathReal = PackageManagerServiceUtils.realpath(new File(dexPath));
-                if (dexPathReal != dexPath) {
+                if (!dexPath.equals(dexPathReal)) {
                     Slog.d(TAG, "Dex loaded with symlink. dexPath=" +
                             dexPath + " dexPathReal=" + dexPathReal);
                 }
@@ -675,6 +721,7 @@
      */
     public void writePackageDexUsageNow() {
         mPackageDexUsage.writeNow();
+        mPackageDynamicCodeLoading.writeNow();
     }
 
     private void registerSettingObserver() {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
new file mode 100644
index 0000000..f74aa1d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
@@ -0,0 +1,612 @@
+/*
+ * 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 com.android.server.pm.dex;
+
+import android.util.AtomicFile;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastPrintWriter;
+import com.android.server.pm.AbstractStatsBase;
+
+import libcore.io.IoUtils;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Stats file which stores information about secondary code files that are dynamically loaded.
+ */
+class PackageDynamicCodeLoading extends AbstractStatsBase<Void> {
+    // Type code to indicate a secondary file containing DEX code. (The char value is how it
+    // is represented in the text file format.)
+    static final int FILE_TYPE_DEX = 'D';
+
+    private static final String TAG = "PackageDynamicCodeLoading";
+
+    private static final String FILE_VERSION_HEADER = "DCL1";
+    private static final String PACKAGE_PREFIX = "P:";
+
+    private static final char FIELD_SEPARATOR = ':';
+    private static final String PACKAGE_SEPARATOR = ",";
+
+    /**
+     * Regular expression to match the expected format of an input line describing one file.
+     * <p>Example: {@code D:10:package.name1,package.name2:/escaped/path}
+     * <p>The capturing groups are the file type, user ID, loading packages and escaped file path
+     * (in that order).
+     * <p>See {@link #write(OutputStream, Map)} below for more details of the format.
+     */
+    private static final Pattern PACKAGE_LINE_PATTERN =
+            Pattern.compile("([A-Z]):([0-9]+):([^:]*):(.*)");
+
+    private final Object mLock = new Object();
+
+    // Map from package name to data about loading of dynamic code files owned by that package.
+    // (Apps may load code files owned by other packages, subject to various access
+    // constraints.)
+    // Any PackageDynamicCode in this map will be non-empty.
+    @GuardedBy("mLock")
+    private Map<String, PackageDynamicCode> mPackageMap = new HashMap<>();
+
+    PackageDynamicCodeLoading() {
+        super("package-dcl.list", "PackageDynamicCodeLoading_DiskWriter", false);
+    }
+
+    /**
+     * Record dynamic code loading from a file.
+     *
+     * Note this is called when an app loads dex files and as such it should return
+     * as fast as possible.
+     *
+     * @param owningPackageName the package owning the file path
+     * @param filePath the path of the dex files being loaded
+     * @param fileType the type of code loading
+     * @param ownerUserId the user id which runs the code loading the file
+     * @param loadingPackageName the package performing the load
+     * @return whether new information has been recorded
+     * @throws IllegalArgumentException if clearly invalid information is detected
+     */
+    boolean record(String owningPackageName, String filePath, int fileType, int ownerUserId,
+            String loadingPackageName) {
+        if (fileType != FILE_TYPE_DEX) {
+            throw new IllegalArgumentException("Bad file type: " + fileType);
+        }
+        synchronized (mLock) {
+            PackageDynamicCode packageInfo = mPackageMap.get(owningPackageName);
+            if (packageInfo == null) {
+                packageInfo = new PackageDynamicCode();
+                mPackageMap.put(owningPackageName, packageInfo);
+            }
+            return packageInfo.add(filePath, (char) fileType, ownerUserId, loadingPackageName);
+        }
+    }
+
+    /**
+     * Return all packages that contain records of secondary dex files. (Note that data updates
+     * asynchronously, so {@link #getPackageDynamicCodeInfo} may still return null if passed
+     * one of these package names.)
+     */
+    Set<String> getAllPackagesWithDynamicCodeLoading() {
+        synchronized (mLock) {
+            return new HashSet<>(mPackageMap.keySet());
+        }
+    }
+
+    /**
+     * Return information about the dynamic code file usage of the specified package,
+     * or null if there is currently no usage information. The object returned is a copy of the
+     * live information that is not updated.
+     */
+    PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
+        synchronized (mLock) {
+            PackageDynamicCode info = mPackageMap.get(packageName);
+            return info == null ? null : new PackageDynamicCode(info);
+        }
+    }
+
+    /**
+     * Remove all information about all packages.
+     */
+    void clear() {
+        synchronized (mLock) {
+            mPackageMap.clear();
+        }
+    }
+
+    /**
+     * Remove the data associated with package {@code packageName}. Affects all users.
+     * @return true if the package usage was found and removed successfully
+     */
+    boolean removePackage(String packageName) {
+        synchronized (mLock) {
+            return mPackageMap.remove(packageName) != null;
+        }
+    }
+
+    /**
+     * Remove all the records about package {@code packageName} belonging to user {@code userId}.
+     * @return whether any data was actually removed
+     */
+    boolean removeUserPackage(String packageName, int userId) {
+        synchronized (mLock) {
+            PackageDynamicCode packageDynamicCode = mPackageMap.get(packageName);
+            if (packageDynamicCode == null) {
+                return false;
+            }
+            if (packageDynamicCode.removeUser(userId)) {
+                if (packageDynamicCode.mFileUsageMap.isEmpty()) {
+                    mPackageMap.remove(packageName);
+                }
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Remove the specified dynamic code file record belonging to the package {@code packageName}
+     * and user {@code userId}.
+     * @return whether data was actually removed
+     */
+    boolean removeFile(String packageName, String filePath, int userId) {
+        synchronized (mLock) {
+            PackageDynamicCode packageDynamicCode = mPackageMap.get(packageName);
+            if (packageDynamicCode == null) {
+                return false;
+            }
+            if (packageDynamicCode.removeFile(filePath, userId)) {
+                if (packageDynamicCode.mFileUsageMap.isEmpty()) {
+                    mPackageMap.remove(packageName);
+                }
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Syncs data with the set of installed packages. Data about packages that are no longer
+     * installed is removed.
+     * @param packageToUsersMap a map from all existing package names to the users who have the
+     *                          package installed
+     */
+    void syncData(Map<String, Set<Integer>> packageToUsersMap) {
+        synchronized (mLock) {
+            Iterator<Entry<String, PackageDynamicCode>> it = mPackageMap.entrySet().iterator();
+            while (it.hasNext()) {
+                Entry<String, PackageDynamicCode> entry = it.next();
+                Set<Integer> packageUsers = packageToUsersMap.get(entry.getKey());
+                if (packageUsers == null) {
+                    it.remove();
+                } else {
+                    PackageDynamicCode packageDynamicCode = entry.getValue();
+                    packageDynamicCode.syncData(packageToUsersMap, packageUsers);
+                    if (packageDynamicCode.mFileUsageMap.isEmpty()) {
+                        it.remove();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Request that data be written to persistent file at the next time allowed by write-limiting.
+     */
+    void maybeWriteAsync() {
+        super.maybeWriteAsync(null);
+    }
+
+    /**
+     * Writes data to persistent file immediately.
+     */
+    void writeNow() {
+        super.writeNow(null);
+    }
+
+    @Override
+    protected final void writeInternal(Void data) {
+        AtomicFile file = getFile();
+        FileOutputStream output = null;
+        try {
+            output = file.startWrite();
+            write(output);
+            file.finishWrite(output);
+        } catch (IOException e) {
+            file.failWrite(output);
+            Slog.e(TAG, "Failed to write dynamic usage for secondary code files.", e);
+        }
+    }
+
+    @VisibleForTesting
+    void write(OutputStream output) throws IOException {
+        // Make a deep copy to avoid holding the lock while writing to disk.
+        Map<String, PackageDynamicCode> copiedMap;
+        synchronized (mLock) {
+            copiedMap = new HashMap<>(mPackageMap.size());
+            for (Entry<String, PackageDynamicCode> entry : mPackageMap.entrySet()) {
+                PackageDynamicCode copiedValue = new PackageDynamicCode(entry.getValue());
+                copiedMap.put(entry.getKey(), copiedValue);
+            }
+        }
+
+        write(output, copiedMap);
+    }
+
+    /**
+     * Write the dynamic code loading data as a text file to {@code output}. The file format begins
+     * with a line indicating the file type and version - {@link #FILE_VERSION_HEADER}.
+     * <p>There is then one section for each owning package, introduced by a line beginning "P:".
+     * This is followed by a line for each file owned by the package this is dynamically loaded,
+     * containing the file type, user ID, loading package names and full path (with newlines and
+     * backslashes escaped - see {@link #escape}).
+     * <p>For example:
+     * <pre>{@code
+     * DCL1
+     * P:first.owning.package
+     * D:0:loading.package_1,loading.package_2:/path/to/file
+     * D:10:loading.package_1:/another/file
+     * P:second.owning.package
+     * D:0:loading.package:/third/file
+     * }</pre>
+     */
+    private static void write(OutputStream output, Map<String, PackageDynamicCode> packageMap)
+            throws IOException {
+        PrintWriter writer = new FastPrintWriter(output);
+
+        writer.println(FILE_VERSION_HEADER);
+        for (Entry<String, PackageDynamicCode> packageEntry : packageMap.entrySet()) {
+            writer.print(PACKAGE_PREFIX);
+            writer.println(packageEntry.getKey());
+
+            Map<String, DynamicCodeFile> mFileUsageMap = packageEntry.getValue().mFileUsageMap;
+            for (Entry<String, DynamicCodeFile> fileEntry : mFileUsageMap.entrySet()) {
+                String path = fileEntry.getKey();
+                DynamicCodeFile dynamicCodeFile = fileEntry.getValue();
+
+                writer.print(dynamicCodeFile.mFileType);
+                writer.print(FIELD_SEPARATOR);
+                writer.print(dynamicCodeFile.mUserId);
+                writer.print(FIELD_SEPARATOR);
+
+                String prefix = "";
+                for (String packageName : dynamicCodeFile.mLoadingPackages) {
+                    writer.print(prefix);
+                    writer.print(packageName);
+                    prefix = PACKAGE_SEPARATOR;
+                }
+
+                writer.print(FIELD_SEPARATOR);
+                writer.println(escape(path));
+            }
+        }
+
+        writer.flush();
+        if (writer.checkError()) {
+            throw new IOException("Writer failed");
+        }
+    }
+
+    /**
+     * Read data from the persistent file. Replaces existing data completely if successful.
+     */
+    void read() {
+        super.read(null);
+    }
+
+    @Override
+    protected final void readInternal(Void data) {
+        AtomicFile file = getFile();
+
+        FileInputStream stream = null;
+        try {
+            stream = file.openRead();
+            read(stream);
+        } catch (FileNotFoundException expected) {
+            // The file may not be there. E.g. When we first take the OTA with this feature.
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to parse dynamic usage for secondary code files.", e);
+        } finally {
+            IoUtils.closeQuietly(stream);
+        }
+    }
+
+    @VisibleForTesting
+    void read(InputStream stream) throws IOException {
+        Map<String, PackageDynamicCode> newPackageMap = new HashMap<>();
+        read(stream, newPackageMap);
+        synchronized (mLock) {
+            mPackageMap = newPackageMap;
+        }
+    }
+
+    private static void read(InputStream stream, Map<String, PackageDynamicCode> packageMap)
+            throws IOException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+
+        String versionLine = reader.readLine();
+        if (!FILE_VERSION_HEADER.equals(versionLine)) {
+            throw new IOException("Incorrect version line: " + versionLine);
+        }
+
+        String line = reader.readLine();
+        if (line != null && !line.startsWith(PACKAGE_PREFIX)) {
+            throw new IOException("Malformed line: " + line);
+        }
+
+        while (line != null) {
+            String packageName = line.substring(PACKAGE_PREFIX.length());
+
+            PackageDynamicCode packageInfo = new PackageDynamicCode();
+            while (true) {
+                line = reader.readLine();
+                if (line == null || line.startsWith(PACKAGE_PREFIX)) {
+                    break;
+                }
+                readFileInfo(line, packageInfo);
+            }
+
+            if (!packageInfo.mFileUsageMap.isEmpty()) {
+                packageMap.put(packageName, packageInfo);
+            }
+        }
+    }
+
+    private static void readFileInfo(String line, PackageDynamicCode output) throws IOException {
+        try {
+            Matcher matcher = PACKAGE_LINE_PATTERN.matcher(line);
+            if (!matcher.matches()) {
+                throw new IOException("Malformed line: " + line);
+            }
+
+            char type = matcher.group(1).charAt(0);
+            int user = Integer.parseInt(matcher.group(2));
+            String[] packages = matcher.group(3).split(PACKAGE_SEPARATOR);
+            String path = unescape(matcher.group(4));
+
+            if (packages.length == 0) {
+                throw new IOException("Malformed line: " + line);
+            }
+            if (type != FILE_TYPE_DEX) {
+                throw new IOException("Unknown file type: " + line);
+            }
+
+            output.mFileUsageMap.put(path, new DynamicCodeFile(type, user, packages));
+        } catch (RuntimeException e) {
+            // Just in case we get NumberFormatException, or various
+            // impossible out of bounds errors happen.
+            throw new IOException("Unable to parse line: " + line, e);
+        }
+    }
+
+    /**
+     * Escape any newline and backslash characters in path. A newline in a path is legal if unusual,
+     * and it would break our line-based file parsing.
+     */
+    @VisibleForTesting
+    static String escape(String path) {
+        if (path.indexOf('\\') == -1 && path.indexOf('\n') == -1 && path.indexOf('\r') == -1) {
+            return path;
+        }
+
+        StringBuilder result = new StringBuilder(path.length() + 10);
+        for (int i = 0; i < path.length(); i++) {
+            // Surrogates will never match the characters we care about, so it's ok to use chars
+            // not code points here.
+            char c = path.charAt(i);
+            switch (c) {
+                case '\\':
+                    result.append("\\\\");
+                    break;
+                case '\n':
+                    result.append("\\n");
+                    break;
+                case '\r':
+                    result.append("\\r");
+                    break;
+                default:
+                    result.append(c);
+                    break;
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * Reverse the effect of {@link #escape}.
+     * @throws IOException if the input string is malformed
+     */
+    @VisibleForTesting
+    static String unescape(String escaped) throws IOException {
+        // As we move through the input string, start is the position of the first character
+        // after the previous escape sequence and finish is the position of the following backslash.
+        int start = 0;
+        int finish = escaped.indexOf('\\');
+        if (finish == -1) {
+            return escaped;
+        }
+
+        StringBuilder result = new StringBuilder(escaped.length());
+        while (true) {
+            if (finish >= escaped.length() - 1) {
+                // Backslash mustn't be the last character
+                throw new IOException("Unexpected \\ in: " + escaped);
+            }
+            result.append(escaped, start, finish);
+            switch (escaped.charAt(finish + 1)) {
+                case '\\':
+                    result.append('\\');
+                    break;
+                case 'r':
+                    result.append('\r');
+                    break;
+                case 'n':
+                    result.append('\n');
+                    break;
+                default:
+                    throw new IOException("Bad escape in: " + escaped);
+            }
+
+            start = finish + 2;
+            finish = escaped.indexOf('\\', start);
+            if (finish == -1) {
+                result.append(escaped, start, escaped.length());
+                break;
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * Represents the dynamic code usage of a single package.
+     */
+    static class PackageDynamicCode {
+        /**
+         * Map from secondary code file path to information about which packages dynamically load
+         * that file.
+         */
+        final Map<String, DynamicCodeFile> mFileUsageMap;
+
+        private PackageDynamicCode() {
+            mFileUsageMap = new HashMap<>();
+        }
+
+        private PackageDynamicCode(PackageDynamicCode original) {
+            mFileUsageMap = new HashMap<>(original.mFileUsageMap.size());
+            for (Entry<String, DynamicCodeFile> entry : original.mFileUsageMap.entrySet()) {
+                DynamicCodeFile newValue = new DynamicCodeFile(entry.getValue());
+                mFileUsageMap.put(entry.getKey(), newValue);
+            }
+        }
+
+        private boolean add(String path, char fileType, int userId, String loadingPackage) {
+            DynamicCodeFile fileInfo = mFileUsageMap.get(path);
+            if (fileInfo == null) {
+                fileInfo = new DynamicCodeFile(fileType, userId, loadingPackage);
+                mFileUsageMap.put(path, fileInfo);
+                return true;
+            } else {
+                if (fileInfo.mUserId != userId) {
+                    // This should be impossible: private app files are always user-specific and
+                    // can't be accessed from different users.
+                    throw new IllegalArgumentException("Cannot change userId for '" + path
+                            + "' from " + fileInfo.mUserId + " to " + userId);
+                }
+                // Changing file type (i.e. loading the same file in different ways is possible if
+                // unlikely. We allow it but ignore it.
+                return fileInfo.mLoadingPackages.add(loadingPackage);
+            }
+        }
+
+        private boolean removeUser(int userId) {
+            boolean updated = false;
+            Iterator<DynamicCodeFile> it = mFileUsageMap.values().iterator();
+            while (it.hasNext()) {
+                DynamicCodeFile fileInfo = it.next();
+                if (fileInfo.mUserId == userId) {
+                    it.remove();
+                    updated = true;
+                }
+            }
+            return updated;
+        }
+
+        private boolean removeFile(String filePath, int userId) {
+            DynamicCodeFile fileInfo = mFileUsageMap.get(filePath);
+            if (fileInfo == null || fileInfo.mUserId != userId) {
+                return false;
+            } else {
+                mFileUsageMap.remove(filePath);
+                return true;
+            }
+        }
+
+        private void syncData(Map<String, Set<Integer>> packageToUsersMap,
+                Set<Integer> owningPackageUsers) {
+            Iterator<DynamicCodeFile> fileIt = mFileUsageMap.values().iterator();
+            while (fileIt.hasNext()) {
+                DynamicCodeFile fileInfo = fileIt.next();
+                int fileUserId = fileInfo.mUserId;
+                if (!owningPackageUsers.contains(fileUserId)) {
+                    fileIt.remove();
+                } else {
+                    // Also remove information about any loading packages that are no longer
+                    // installed for this user.
+                    Iterator<String> loaderIt = fileInfo.mLoadingPackages.iterator();
+                    while (loaderIt.hasNext()) {
+                        String loader = loaderIt.next();
+                        Set<Integer> loadingPackageUsers = packageToUsersMap.get(loader);
+                        if (loadingPackageUsers == null
+                                || !loadingPackageUsers.contains(fileUserId)) {
+                            loaderIt.remove();
+                        }
+                    }
+                    if (fileInfo.mLoadingPackages.isEmpty()) {
+                        fileIt.remove();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Represents a single dynamic code file loaded by one or more packages. Note that it is
+     * possible for one app to dynamically load code from a different app's home dir, if the
+     * owning app:
+     * <ul>
+     *     <li>Targets API 27 or lower and has shared its home dir.
+     *     <li>Is a system app.
+     *     <li>Has a shared UID with the loading app.
+     * </ul>
+     */
+    static class DynamicCodeFile {
+        final char mFileType;
+        final int mUserId;
+        final Set<String> mLoadingPackages;
+
+        private DynamicCodeFile(char type, int user, String... packages) {
+            mFileType = type;
+            mUserId = user;
+            mLoadingPackages = new HashSet<>(Arrays.asList(packages));
+        }
+
+        private DynamicCodeFile(DynamicCodeFile original) {
+            mFileType = original.mFileType;
+            mUserId = original.mUserId;
+            mLoadingPackages = new HashSet<>(original.mLoadingPackages);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 2d583ca3..996f42b 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -189,6 +189,11 @@
         return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                 == PermissionInfo.PROTECTION_DANGEROUS;
     }
+
+    public boolean isRemoved() {
+        return perm.info != null && (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0;
+    }
+
     public boolean isSignature() {
         return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) ==
                 PermissionInfo.PROTECTION_SIGNATURE;
@@ -235,6 +240,9 @@
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
                 != 0;
     }
+    public boolean isWellbeing() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0;
+    }
 
     public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
         if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 4406fdd..bc3c18d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -799,6 +799,10 @@
                     continue;
                 }
 
+                if (bp.isRemoved()) {
+                    continue;
+                }
+
                 // Limit ephemeral apps to ephemeral allowed permissions.
                 if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
                     if (DEBUG_PERMISSIONS) {
@@ -1637,6 +1641,12 @@
                 // Special permissions for the system default text classifier.
                 allowed = true;
             }
+            if (!allowed && bp.isWellbeing()
+                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) {
+                // Special permission granted only to the OEM specified wellbeing app
+                allowed = true;
+            }
         }
         return allowed;
     }
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 07bebad..2eea3a4 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -250,14 +250,29 @@
         }
     }
 
+    private void shutdownIfNeededLocked(Temperature temperature) {
+        if (temperature.getStatus() != Temperature.THROTTLING_SHUTDOWN) {
+            return;
+        }
+        switch (temperature.getType()) {
+            case Temperature.TYPE_CPU:
+                // Fall through
+            case Temperature.TYPE_GPU:
+                // Fall through
+            case Temperature.TYPE_NPU:
+                // Fall through
+            case Temperature.TYPE_SKIN:
+                mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
+                break;
+            case Temperature.TYPE_BATTERY:
+                mPowerManager.shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false);
+                break;
+        }
+    }
+
     private void onTemperatureChanged(Temperature temperature, boolean sendStatus) {
         synchronized (mLock) {
-            // Thermal Shutdown for Skin temperature
-            if (temperature.getStatus() == Temperature.THROTTLING_SHUTDOWN
-                    && temperature.getType() == Temperature.TYPE_SKIN) {
-                mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
-            }
-
+            shutdownIfNeededLocked(temperature);
             Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
             if (old != null) {
                 if (old.getStatus() != temperature.getStatus()) {
@@ -300,6 +315,8 @@
     final IThermalService.Stub mService = new IThermalService.Stub() {
         @Override
         public boolean registerThermalEventListener(IThermalEventListener listener) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, null);
             synchronized (mLock) {
                 final long token = Binder.clearCallingIdentity();
                 try {
@@ -320,6 +337,8 @@
         @Override
         public boolean registerThermalEventListenerWithType(IThermalEventListener listener,
                 int type) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, null);
             synchronized (mLock) {
                 final long token = Binder.clearCallingIdentity();
                 try {
@@ -339,6 +358,8 @@
 
         @Override
         public boolean unregisterThermalEventListener(IThermalEventListener listener) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, null);
             synchronized (mLock) {
                 final long token = Binder.clearCallingIdentity();
                 try {
@@ -351,6 +372,8 @@
 
         @Override
         public List<Temperature> getCurrentTemperatures() {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, null);
             final long token = Binder.clearCallingIdentity();
             try {
                 if (!mHalReady) {
@@ -364,6 +387,8 @@
 
         @Override
         public List<Temperature> getCurrentTemperaturesWithType(int type) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, null);
             final long token = Binder.clearCallingIdentity();
             try {
                 if (!mHalReady) {
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 8ce3838..8711ddf 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -156,21 +156,16 @@
     @MainThread
     private void performInitialGrantsIfNecessary(@UserIdInt int userId) {
         RoleUserState userState;
-        synchronized (mLock) {
-            userState = getUserStateLocked(userId);
-        }
+        userState = getOrCreateUserState(userId);
         String packagesHash = computeComponentStateHash(userId);
-        String oldPackagesHash;
-        synchronized (mLock) {
-            oldPackagesHash = userState.getPackagesHashLocked();
-        }
+        String oldPackagesHash = userState.getPackagesHash();
         boolean needGrant = !Objects.equals(packagesHash, oldPackagesHash);
         if (needGrant) {
             // Some vital packages state has changed since last role grant
             // Run grants again
             Slog.i(LOG_TAG, "Granting default permissions...");
             CompletableFuture<Void> result = new CompletableFuture<>();
-            getControllerService(userId).onGrantDefaultRoles(
+            getOrCreateControllerService(userId).onGrantDefaultRoles(
                     new IRoleManagerCallback.Stub() {
                         @Override
                         public void onSuccess() {
@@ -183,9 +178,7 @@
                     });
             try {
                 result.get(5, TimeUnit.SECONDS);
-                synchronized (mLock) {
-                    userState.setPackagesHashLocked(packagesHash);
-                }
+                userState.setPackagesHash(packagesHash);
             } catch (InterruptedException | ExecutionException | TimeoutException e) {
                 Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e);
             }
@@ -225,35 +218,38 @@
         return PackageUtils.computeSha256Digest(out.toByteArray());
     }
 
-    @GuardedBy("mLock")
     @NonNull
-    private RoleUserState getUserStateLocked(@UserIdInt int userId) {
-        RoleUserState userState = mUserStates.get(userId);
-        if (userState == null) {
-            userState = RoleUserState.newInstanceLocked(userId);
-            mUserStates.put(userId, userState);
+    private RoleUserState getOrCreateUserState(@UserIdInt int userId) {
+        synchronized (mLock) {
+            RoleUserState userState = mUserStates.get(userId);
+            if (userState == null) {
+                userState = new RoleUserState(userId);
+                mUserStates.put(userId, userState);
+            }
+            return userState;
         }
-        return userState;
     }
 
-    @GuardedBy("mLock")
     @NonNull
-    private RemoteRoleControllerService getControllerService(@UserIdInt int userId) {
-        RemoteRoleControllerService controllerService = mControllerServices.get(userId);
-        if (controllerService == null) {
-            controllerService = new RemoteRoleControllerService(userId, getContext());
-            mControllerServices.put(userId, controllerService);
+    private RemoteRoleControllerService getOrCreateControllerService(@UserIdInt int userId) {
+        synchronized (mLock) {
+            RemoteRoleControllerService controllerService = mControllerServices.get(userId);
+            if (controllerService == null) {
+                controllerService = new RemoteRoleControllerService(userId, getContext());
+                mControllerServices.put(userId, controllerService);
+            }
+            return controllerService;
         }
-        return controllerService;
     }
 
     private void onRemoveUser(@UserIdInt int userId) {
+        RoleUserState userState;
         synchronized (mLock) {
             mControllerServices.remove(userId);
-            RoleUserState userState = mUserStates.removeReturnOld(userId);
-            if (userState != null) {
-                userState.destroyLocked();
-            }
+            userState = mUserStates.removeReturnOld(userId);
+        }
+        if (userState != null) {
+            userState.destroy();
         }
     }
 
@@ -264,10 +260,8 @@
             Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
 
             int userId = UserHandle.getUserId(getCallingUid());
-            synchronized (mLock) {
-                RoleUserState userState = getUserStateLocked(userId);
-                return userState.isRoleAvailableLocked(roleName);
-            }
+            RoleUserState userState = getOrCreateUserState(userId);
+            return userState.isRoleAvailable(roleName);
         }
 
         @Override
@@ -307,10 +301,8 @@
         @Nullable
         private ArraySet<String> getRoleHoldersInternal(@NonNull String roleName,
                 @UserIdInt int userId) {
-            synchronized (mLock) {
-                RoleUserState userState = getUserStateLocked(userId);
-                return userState.getRoleHoldersLocked(roleName);
-            }
+            RoleUserState userState = getOrCreateUserState(userId);
+            return userState.getRoleHolders(roleName);
         }
 
         @Override
@@ -327,7 +319,7 @@
             getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
                     "addRoleHolderAsUser");
 
-            getControllerService(userId).onAddRoleHolder(roleName, packageName, callback);
+            getOrCreateControllerService(userId).onAddRoleHolder(roleName, packageName, callback);
         }
 
         @Override
@@ -344,7 +336,7 @@
             getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
                     "removeRoleHolderAsUser");
 
-            getControllerService(userId).onRemoveRoleHolder(roleName, packageName,
+            getOrCreateControllerService(userId).onRemoveRoleHolder(roleName, packageName,
                     callback);
         }
 
@@ -361,7 +353,7 @@
             getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
                     "clearRoleHoldersAsUser");
 
-            getControllerService(userId).onClearRoleHolders(roleName, callback);
+            getOrCreateControllerService(userId).onClearRoleHolders(roleName, callback);
         }
 
         @Override
@@ -372,10 +364,8 @@
                     "setRoleNamesFromController");
 
             int userId = UserHandle.getCallingUserId();
-            synchronized (mLock) {
-                RoleUserState userState = getUserStateLocked(userId);
-                userState.setRoleNamesLocked(roleNames);
-            }
+            RoleUserState userState = getOrCreateUserState(userId);
+            userState.setRoleNames(roleNames);
         }
 
         @Override
@@ -388,10 +378,8 @@
                     "addRoleHolderFromController");
 
             int userId = UserHandle.getCallingUserId();
-            synchronized (mLock) {
-                RoleUserState userState = getUserStateLocked(userId);
-                return userState.addRoleHolderLocked(roleName, packageName);
-            }
+            RoleUserState userState = getOrCreateUserState(userId);
+            return userState.addRoleHolder(roleName, packageName);
         }
 
         @Override
@@ -404,10 +392,8 @@
                     "removeRoleHolderFromController");
 
             int userId = UserHandle.getCallingUserId();
-            synchronized (mLock) {
-                RoleUserState userState = getUserStateLocked(userId);
-                return userState.removeRoleHolderLocked(roleName, packageName);
-            }
+            RoleUserState userState = getOrCreateUserState(userId);
+            return userState.removeRoleHolder(roleName, packageName);
         }
 
         @CheckResult
@@ -440,16 +426,14 @@
                 dumpOutputStream = new DualDumpOutputStream(new IndentingPrintWriter(fout, "  "));
             }
 
-            synchronized (mLock) {
-                int[] userIds = mUserManagerInternal.getUserIds();
-                int userIdsLength = userIds.length;
-                for (int i = 0; i < userIdsLength; i++) {
-                    int userId = userIds[i];
+            int[] userIds = mUserManagerInternal.getUserIds();
+            int userIdsLength = userIds.length;
+            for (int i = 0; i < userIdsLength; i++) {
+                int userId = userIds[i];
 
-                    RoleUserState userState = getUserStateLocked(userId);
-                    userState.dumpLocked(dumpOutputStream, "user_states",
-                            RoleManagerServiceDumpProto.USER_STATES);
-                }
+                RoleUserState userState = getOrCreateUserState(userId);
+                userState.dump(dumpOutputStream, "user_states",
+                        RoleManagerServiceDumpProto.USER_STATES);
             }
 
             dumpOutputStream.flush();
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 327debf..ec614a4 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -62,7 +62,6 @@
     private static final String ROLES_FILE_NAME = "roles.xml";
 
     private static final long WRITE_DELAY_MILLIS = 200;
-    private static final long MAX_WRITE_DELAY_MILLIS = 2000;
 
     private static final String TAG_ROLES = "roles";
     private static final String TAG_ROLE = "role";
@@ -74,54 +73,51 @@
     @UserIdInt
     private final int mUserId;
 
-    @GuardedBy("RoleManagerService.mLock")
+    @NonNull
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
     private int mVersion = VERSION_UNDEFINED;
 
-    @GuardedBy("RoleManagerService.mLock")
+    @GuardedBy("mLock")
     @Nullable
     private String mPackagesHash;
 
     /**
      * Maps role names to its holders' package names. The values should never be null.
      */
-    @GuardedBy("RoleManagerService.mLock")
+    @GuardedBy("mLock")
     @NonNull
     private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
 
-    @GuardedBy("RoleManagerService.mLock")
-    private long mWritePendingSinceMillis;
+    @GuardedBy("mLock")
+    private boolean mWriteScheduled;
 
-    @GuardedBy("RoleManagerService.mLock")
+    @GuardedBy("mLock")
     private boolean mDestroyed;
 
     @NonNull
     private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
 
-    private RoleUserState(@UserIdInt int userId) {
-        mUserId = userId;
-
-        readLocked();
-    }
-
     /**
      * Create a new instance of user state, and read its state from disk if previously persisted.
      *
      * @param userId the user id for the new user state
-     *
-     * @return the new user state
      */
-    @GuardedBy("RoleManagerService.mLock")
-    public static RoleUserState newInstanceLocked(@UserIdInt int userId) {
-        return new RoleUserState(userId);
+    public RoleUserState(@UserIdInt int userId) {
+        mUserId = userId;
+
+        readFile();
     }
 
     /**
      * Get the version of this user state.
      */
-    @GuardedBy("RoleManagerService.mLock")
-    public int getVersionLocked() {
-        throwIfDestroyedLocked();
-        return mVersion;
+    public int getVersion() {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            return mVersion;
+        }
     }
 
     /**
@@ -129,14 +125,15 @@
      *
      * @param version the version to set
      */
-    @GuardedBy("RoleManagerService.mLock")
-    public void setVersionLocked(int version) {
-        throwIfDestroyedLocked();
-        if (mVersion == version) {
-            return;
+    public void setVersion(int version) {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            if (mVersion == version) {
+                return;
+            }
+            mVersion = version;
+            scheduleWriteFileLocked();
         }
-        mVersion = version;
-        writeAsyncLocked();
     }
 
     /**
@@ -144,9 +141,11 @@
      *
      * @return the hash representing the state of packages
      */
-    @GuardedBy("RoleManagerService.mLock")
-    public String getPackagesHashLocked() {
-        return mPackagesHash;
+    @Nullable
+    public String getPackagesHash() {
+        synchronized (mLock) {
+            return mPackagesHash;
+        }
     }
 
     /**
@@ -154,14 +153,15 @@
      *
      * @param packagesHash the hash representing the state of packages
      */
-    @GuardedBy("RoleManagerService.mLock")
-    public void setPackagesHashLocked(@Nullable String packagesHash) {
-        throwIfDestroyedLocked();
-        if (Objects.equals(mPackagesHash, packagesHash)) {
-            return;
+    public void setPackagesHash(@Nullable String packagesHash) {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            if (Objects.equals(mPackagesHash, packagesHash)) {
+                return;
+            }
+            mPackagesHash = packagesHash;
+            scheduleWriteFileLocked();
         }
-        mPackagesHash = packagesHash;
-        writeAsyncLocked();
     }
 
     /**
@@ -171,10 +171,11 @@
      *
      * @return whether the role is available
      */
-    @GuardedBy("RoleManagerService.mLock")
-    public boolean isRoleAvailableLocked(@NonNull String roleName) {
-        throwIfDestroyedLocked();
-        return mRoles.containsKey(roleName);
+    public boolean isRoleAvailable(@NonNull String roleName) {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            return mRoles.containsKey(roleName);
+        }
     }
 
     /**
@@ -184,11 +185,12 @@
      *
      * @return the set of role holders. {@code null} should not be returned and indicates an issue.
      */
-    @GuardedBy("RoleManagerService.mLock")
     @Nullable
-    public ArraySet<String> getRoleHoldersLocked(@NonNull String roleName) {
-        throwIfDestroyedLocked();
-        return mRoles.get(roleName);
+    public ArraySet<String> getRoleHolders(@NonNull String roleName) {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            return new ArraySet<>(mRoles.get(roleName));
+        }
     }
 
     /**
@@ -196,33 +198,35 @@
      *
      * @param roleNames the names of all the available roles
      */
-    @GuardedBy("RoleManagerService.mLock")
-    public void setRoleNamesLocked(@NonNull List<String> roleNames) {
-        throwIfDestroyedLocked();
-        boolean changed = false;
-        for (int i = mRoles.size() - 1; i >= 0; i--) {
-            String roleName = mRoles.keyAt(i);
-            if (!roleNames.contains(roleName)) {
-                ArraySet<String> packageNames = mRoles.valueAt(i);
-                if (!packageNames.isEmpty()) {
-                    Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up, role: "
-                            + roleName + ", holders: " + packageNames);
+    public void setRoleNames(@NonNull List<String> roleNames) {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            boolean changed = false;
+            for (int i = mRoles.size() - 1; i >= 0; i--) {
+                String roleName = mRoles.keyAt(i);
+                if (!roleNames.contains(roleName)) {
+                    ArraySet<String> packageNames = mRoles.valueAt(i);
+                    if (!packageNames.isEmpty()) {
+                        Slog.e(LOG_TAG,
+                                "Holders of a removed role should have been cleaned up, role: "
+                                        + roleName + ", holders: " + packageNames);
+                    }
+                    mRoles.removeAt(i);
+                    changed = true;
                 }
-                mRoles.removeAt(i);
-                changed = true;
             }
-        }
-        int roleNamesSize = roleNames.size();
-        for (int i = 0; i < roleNamesSize; i++) {
-            String roleName = roleNames.get(i);
-            if (!mRoles.containsKey(roleName)) {
-                mRoles.put(roleName, new ArraySet<>());
-                Slog.i(LOG_TAG, "Added new role: " + roleName);
-                changed = true;
+            int roleNamesSize = roleNames.size();
+            for (int i = 0; i < roleNamesSize; i++) {
+                String roleName = roleNames.get(i);
+                if (!mRoles.containsKey(roleName)) {
+                    mRoles.put(roleName, new ArraySet<>());
+                    Slog.i(LOG_TAG, "Added new role: " + roleName);
+                    changed = true;
+                }
             }
-        }
-        if (changed) {
-            writeAsyncLocked();
+            if (changed) {
+                scheduleWriteFileLocked();
+            }
         }
     }
 
@@ -236,20 +240,21 @@
      *         indicates an issue.
      */
     @CheckResult
-    @GuardedBy("RoleManagerService.mLock")
-    public boolean addRoleHolderLocked(@NonNull String roleName, @NonNull String packageName) {
-        throwIfDestroyedLocked();
-        ArraySet<String> roleHolders = mRoles.get(roleName);
-        if (roleHolders == null) {
-            Slog.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
-                    + ", package: " + packageName);
-            return false;
+    public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            ArraySet<String> roleHolders = mRoles.get(roleName);
+            if (roleHolders == null) {
+                Slog.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
+                        + ", package: " + packageName);
+                return false;
+            }
+            boolean changed = roleHolders.add(packageName);
+            if (changed) {
+                scheduleWriteFileLocked();
+            }
+            return true;
         }
-        boolean changed = roleHolders.add(packageName);
-        if (changed) {
-            writeAsyncLocked();
-        }
-        return true;
     }
 
     /**
@@ -262,63 +267,54 @@
      *         indicates an issue.
      */
     @CheckResult
-    @GuardedBy("RoleManagerService.mLock")
-    public boolean removeRoleHolderLocked(@NonNull String roleName, @NonNull String packageName) {
-        throwIfDestroyedLocked();
-        ArraySet<String> roleHolders = mRoles.get(roleName);
-        if (roleHolders == null) {
-            Slog.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName
-                    + ", package: " + packageName);
-            return false;
+    public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            ArraySet<String> roleHolders = mRoles.get(roleName);
+            if (roleHolders == null) {
+                Slog.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName
+                        + ", package: " + packageName);
+                return false;
+            }
+            boolean changed = roleHolders.remove(packageName);
+            if (changed) {
+                scheduleWriteFileLocked();
+            }
+            return true;
         }
-        boolean changed = roleHolders.remove(packageName);
-        if (changed) {
-            writeAsyncLocked();
-        }
-        return true;
     }
 
     /**
      * Schedule writing the state to file.
      */
-    @GuardedBy("RoleManagerService.mLock")
-    private void writeAsyncLocked() {
+    @GuardedBy("mLock")
+    private void scheduleWriteFileLocked() {
         throwIfDestroyedLocked();
 
-        ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
-        for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
-            String roleName = mRoles.keyAt(i);
-            ArraySet<String> roleHolders = mRoles.valueAt(i);
-
-            roleHolders = new ArraySet<>(roleHolders);
-            roles.put(roleName, roleHolders);
+        if (!mWriteScheduled) {
+            mWriteHandler.sendMessageDelayed(PooledLambda.obtainMessage(RoleUserState::writeFile,
+                    this), WRITE_DELAY_MILLIS);
+            mWriteScheduled = true;
         }
-
-        long currentTimeMillis = System.currentTimeMillis();
-        long writeDelayMillis;
-        if (!mWriteHandler.hasMessagesOrCallbacks()) {
-            mWritePendingSinceMillis = currentTimeMillis;
-            writeDelayMillis = WRITE_DELAY_MILLIS;
-        } else {
-            mWriteHandler.removeCallbacksAndMessages(null);
-            long writePendingDurationMillis = currentTimeMillis - mWritePendingSinceMillis;
-            if (writePendingDurationMillis >= MAX_WRITE_DELAY_MILLIS) {
-                writeDelayMillis = 0;
-            } else {
-                long maxWriteDelayMillis = Math.max(MAX_WRITE_DELAY_MILLIS
-                        - writePendingDurationMillis, 0);
-                writeDelayMillis = Math.min(WRITE_DELAY_MILLIS, maxWriteDelayMillis);
-            }
-        }
-
-        mWriteHandler.sendMessageDelayed(PooledLambda.obtainMessage(RoleUserState::writeSync, this,
-                mVersion, mPackagesHash, roles), writeDelayMillis);
-        Slog.i(LOG_TAG, "Scheduled writing roles.xml");
     }
 
     @WorkerThread
-    private void writeSync(int version, @Nullable String packagesHash,
-            @NonNull ArrayMap<String, ArraySet<String>> roles) {
+    private void writeFile() {
+        int version;
+        String packagesHash;
+        ArrayMap<String, ArraySet<String>> roles;
+        synchronized (mLock) {
+            if (mDestroyed) {
+                return;
+            }
+
+            mWriteScheduled = false;
+
+            version = mVersion;
+            packagesHash = mPackagesHash;
+            roles = snapshotRolesLocked();
+        }
+
         AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
         FileOutputStream out = null;
         try {
@@ -385,18 +381,19 @@
     /**
      * Read the state from file.
      */
-    @GuardedBy("RoleManagerService.mLock")
-    private void readLocked() {
-        File file = getFile(mUserId);
-        try (FileInputStream in = new AtomicFile(file).openRead()) {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(in, null);
-            parseXmlLocked(parser);
-            Slog.i(LOG_TAG, "Read roles.xml successfully");
-        } catch (FileNotFoundException e) {
-            Slog.i(LOG_TAG, "roles.xml not found");
-        } catch (XmlPullParserException | IOException e) {
-            throw new IllegalStateException("Failed to parse roles.xml: " + file, e);
+    private void readFile() {
+        synchronized (mLock) {
+            File file = getFile(mUserId);
+            try (FileInputStream in = new AtomicFile(file).openRead()) {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(in, null);
+                parseXmlLocked(parser);
+                Slog.i(LOG_TAG, "Read roles.xml successfully");
+            } catch (FileNotFoundException e) {
+                Slog.i(LOG_TAG, "roles.xml not found");
+            } catch (XmlPullParserException | IOException e) {
+                throw new IllegalStateException("Failed to parse roles.xml: " + file, e);
+            }
         }
     }
 
@@ -470,20 +467,28 @@
      *
      * @param dumpOutputStream the output stream to dump to
      */
-    @GuardedBy("RoleManagerService.mLock")
-    public void dumpLocked(@NonNull DualDumpOutputStream dumpOutputStream,
-            @NonNull String fieldName, long fieldId) {
-        throwIfDestroyedLocked();
+    public void dump(@NonNull DualDumpOutputStream dumpOutputStream, @NonNull String fieldName,
+            long fieldId) {
+        int version;
+        String packagesHash;
+        ArrayMap<String, ArraySet<String>> roles;
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+
+            version = mVersion;
+            packagesHash = mPackagesHash;
+            roles = snapshotRolesLocked();
+        }
 
         long fieldToken = dumpOutputStream.start(fieldName, fieldId);
         dumpOutputStream.write("user_id", RoleUserStateProto.USER_ID, mUserId);
-        dumpOutputStream.write("version", RoleUserStateProto.VERSION, mVersion);
-        dumpOutputStream.write("packages_hash", RoleUserStateProto.PACKAGES_HASH, mPackagesHash);
+        dumpOutputStream.write("version", RoleUserStateProto.VERSION, version);
+        dumpOutputStream.write("packages_hash", RoleUserStateProto.PACKAGES_HASH, packagesHash);
 
-        int rolesSize = mRoles.size();
+        int rolesSize = roles.size();
         for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
-            String roleName = mRoles.keyAt(rolesIndex);
-            ArraySet<String> roleHolders = mRoles.valueAt(rolesIndex);
+            String roleName = roles.keyAt(rolesIndex);
+            ArraySet<String> roleHolders = roles.valueAt(rolesIndex);
 
             long rolesToken = dumpOutputStream.start("roles", RoleUserStateProto.ROLES);
             dumpOutputStream.write("name", RoleProto.NAME, roleName);
@@ -501,19 +506,33 @@
         dumpOutputStream.end(fieldToken);
     }
 
+    @GuardedBy("mLock")
+    private ArrayMap<String, ArraySet<String>> snapshotRolesLocked() {
+        ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
+        for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
+            String roleName = mRoles.keyAt(i);
+            ArraySet<String> roleHolders = mRoles.valueAt(i);
+
+            roleHolders = new ArraySet<>(roleHolders);
+            roles.put(roleName, roleHolders);
+        }
+        return roles;
+    }
+
     /**
      * Destroy this state and delete the corresponding file. Any pending writes to the file will be
      * cancelled and any future interaction with this state will throw an exception.
      */
-    @GuardedBy("RoleManagerService.mLock")
-    public void destroyLocked() {
-        throwIfDestroyedLocked();
-        mWriteHandler.removeCallbacksAndMessages(null);
-        getFile(mUserId).delete();
-        mDestroyed = true;
+    public void destroy() {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            mWriteHandler.removeCallbacksAndMessages(null);
+            getFile(mUserId).delete();
+            mDestroyed = true;
+        }
     }
 
-    @GuardedBy("RoleManagerService.mLock")
+    @GuardedBy("mLock")
     private void throwIfDestroyedLocked() {
         if (mDestroyed) {
             throw new IllegalStateException("This RoleUserState has already been destroyed");
diff --git a/services/core/java/com/android/server/signedconfig/InvalidConfigException.java b/services/core/java/com/android/server/signedconfig/InvalidConfigException.java
new file mode 100644
index 0000000..f01baa4
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/InvalidConfigException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.signedconfig;
+
+/**
+ * Thrown when there is a problem parsing the config embedded in an APK.
+ */
+public class InvalidConfigException extends Exception {
+
+    public InvalidConfigException(String message) {
+        super(message);
+    }
+
+    public InvalidConfigException(String message, Exception cause) {
+        super(message, cause);
+    }
+
+
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfig.java b/services/core/java/com/android/server/signedconfig/SignedConfig.java
new file mode 100644
index 0000000..a3f452c
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfig.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.signedconfig;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents signed configuration.
+ *
+ * <p>This configuration should only be used if the signature has already been verified.
+ */
+public class SignedConfig {
+
+    private static final String KEY_VERSION = "version";
+    private static final String KEY_CONFIG = "config";
+
+    private static final String CONFIG_KEY_MIN_SDK = "minSdk";
+    private static final String CONFIG_KEY_MAX_SDK = "maxSdk";
+    private static final String CONFIG_KEY_VALUES = "values";
+    // TODO it may be better to use regular key/value pairs in a JSON object, rather than an array
+    // of objects with the 2 keys below.
+    private static final String CONFIG_KEY_KEY = "key";
+    private static final String CONFIG_KEY_VALUE = "value";
+
+    /**
+     * Represents config values targetting to an SDK range.
+     */
+    public static class PerSdkConfig {
+        public final int minSdk;
+        public final int maxSdk;
+        public final Map<String, String> values;
+
+        public PerSdkConfig(int minSdk, int maxSdk, Map<String, String> values) {
+            this.minSdk = minSdk;
+            this.maxSdk = maxSdk;
+            this.values = Collections.unmodifiableMap(values);
+        }
+
+    }
+
+    public final int version;
+    public final List<PerSdkConfig> perSdkConfig;
+
+    public SignedConfig(int version, List<PerSdkConfig> perSdkConfig) {
+        this.version = version;
+        this.perSdkConfig = Collections.unmodifiableList(perSdkConfig);
+    }
+
+    /**
+     * Find matching sdk config for a given SDK level.
+     *
+     * @param sdkVersion SDK version of device.
+     * @return Matching config, of {@code null} if there is none.
+     */
+    public PerSdkConfig getMatchingConfig(int sdkVersion) {
+        for (PerSdkConfig config : perSdkConfig) {
+            if (config.minSdk <= sdkVersion && sdkVersion <= config.maxSdk) {
+                return config;
+            }
+        }
+        // nothing matching
+        return null;
+    }
+
+    /**
+     * Parse configuration from an APK.
+     *
+     * @param config config as read from the APK metadata.
+     * @return Parsed configuration.
+     * @throws InvalidConfigException If there's a problem parsing the config.
+     */
+    public static SignedConfig parse(String config, Set<String> allowedKeys)
+            throws InvalidConfigException {
+        try {
+            JSONObject json = new JSONObject(config);
+            int version = json.getInt(KEY_VERSION);
+
+            JSONArray perSdkConfig = json.getJSONArray(KEY_CONFIG);
+            List<PerSdkConfig> parsedConfigs = new ArrayList<>();
+            for (int i = 0; i < perSdkConfig.length(); ++i) {
+                parsedConfigs.add(parsePerSdkConfig(perSdkConfig.getJSONObject(i), allowedKeys));
+            }
+
+            return new SignedConfig(version, parsedConfigs);
+        } catch (JSONException e) {
+            throw new InvalidConfigException("Could not parse JSON", e);
+        }
+
+    }
+
+    @VisibleForTesting
+    static PerSdkConfig parsePerSdkConfig(JSONObject json, Set<String> allowedKeys)
+            throws JSONException, InvalidConfigException {
+        int minSdk = json.getInt(CONFIG_KEY_MIN_SDK);
+        int maxSdk = json.getInt(CONFIG_KEY_MAX_SDK);
+        JSONArray valueArray = json.getJSONArray(CONFIG_KEY_VALUES);
+        Map<String, String> values = new HashMap<>();
+        for (int i = 0; i < valueArray.length(); ++i) {
+            JSONObject keyValuePair = valueArray.getJSONObject(i);
+            String key = keyValuePair.getString(CONFIG_KEY_KEY);
+            String value = keyValuePair.has(CONFIG_KEY_VALUE)
+                    ? keyValuePair.getString(CONFIG_KEY_VALUE)
+                    : null;
+            if (!allowedKeys.contains(key)) {
+                throw new InvalidConfigException("Config key " + key + " is not allowed");
+            }
+            values.put(key, value);
+        }
+        return new PerSdkConfig(minSdk, maxSdk, values);
+    }
+
+}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
index 7ce071f..2312f5f 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
@@ -17,12 +17,86 @@
 package com.android.server.signedconfig;
 
 import android.content.Context;
+import android.os.Build;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
 
 class SignedConfigApplicator {
 
-    static void applyConfig(Context context, String config, String signature) {
-        //TODO verify signature
-        //TODO parse & apply config
+    private static final String TAG = "SignedConfig";
+
+    private static final Set<String> ALLOWED_KEYS = Collections.unmodifiableSet(new ArraySet<>(
+            Arrays.asList(
+                    Settings.Global.HIDDEN_API_POLICY,
+                    Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS
+            )));
+
+    private final Context mContext;
+    private final String mSourcePackage;
+
+    SignedConfigApplicator(Context context, String sourcePackage) {
+        mContext = context;
+        mSourcePackage = sourcePackage;
     }
 
+    private boolean checkSignature(String data, String signature) {
+        Slog.w(TAG, "SIGNATURE CHECK NOT IMPLEMENTED YET!");
+        return false;
+    }
+
+    private int getCurrentConfigVersion() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.SIGNED_CONFIG_VERSION, 0);
+    }
+
+    private void updateCurrentConfig(int version, Map<String, String> values) {
+        for (Map.Entry<String, String> e: values.entrySet()) {
+            Settings.Global.putString(
+                    mContext.getContentResolver(),
+                    e.getKey(),
+                    e.getValue());
+        }
+        Settings.Global.putInt(
+                mContext.getContentResolver(), Settings.Global.SIGNED_CONFIG_VERSION, version);
+    }
+
+
+    void applyConfig(String configStr, String signature) {
+        if (!checkSignature(configStr, signature)) {
+            Slog.e(TAG, "Signature check on signed configuration in package " + mSourcePackage
+                    + " failed; ignoring");
+            return;
+        }
+        SignedConfig config;
+        try {
+            config = SignedConfig.parse(configStr, ALLOWED_KEYS);
+        } catch (InvalidConfigException e) {
+            Slog.e(TAG, "Failed to parse config from package " + mSourcePackage, e);
+            return;
+        }
+        int currentVersion = getCurrentConfigVersion();
+        if (currentVersion >= config.version) {
+            Slog.i(TAG, "Config from package " + mSourcePackage + " is older than existing: "
+                    + config.version + "<=" + currentVersion);
+            return;
+        }
+        // We have new config!
+        Slog.i(TAG, "Got new signed config from package " + mSourcePackage + ": version "
+                + config.version + " replacing existing version " + currentVersion);
+        SignedConfig.PerSdkConfig matchedConfig =
+                config.getMatchingConfig(Build.VERSION.SDK_INT);
+        if (matchedConfig == null) {
+            Slog.i(TAG, "Config is not applicable to current SDK version; ignoring");
+            return;
+        }
+
+        Slog.i(TAG, "Updating signed config to version " + config.version);
+        updateCurrentConfig(config.version, matchedConfig.values);
+    }
 }
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
index 1485686..be1d41d 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -85,7 +85,8 @@
                 Slog.d(TAG, "Got signed config: " + config);
                 Slog.d(TAG, "Got config signature: " + signature);
             }
-            SignedConfigApplicator.applyConfig(mContext, config, signature);
+            new SignedConfigApplicator(mContext, packageName).applyConfig(
+                    config, signature);
         } else {
             if (DBG) Slog.d(TAG, "Package has no config/signature.");
         }
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index c8a68b4..6b0419e 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -136,6 +135,7 @@
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
+        validateInput(mContext, request.getCallingPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -158,6 +158,7 @@
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
+        validateInput(mContext, request.getCallingPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -180,6 +181,7 @@
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
+        validateInput(mContext, request.getCallingPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -199,7 +201,7 @@
     public void onSelectionEvent(
             TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException {
         Preconditions.checkNotNull(event);
-        validateInput(event.getPackageName(), mContext);
+        validateInput(mContext, event.getPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -220,6 +222,7 @@
             ITextLanguageCallback callback) throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
+        validateInput(mContext, request.getCallingPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -242,6 +245,7 @@
             IConversationActionsCallback callback) throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
+        validateInput(mContext, request.getCallingPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -263,7 +267,7 @@
             throws RemoteException {
         Preconditions.checkNotNull(sessionId);
         Preconditions.checkNotNull(classificationContext);
-        validateInput(classificationContext.getPackageName(), mContext);
+        validateInput(mContext, classificationContext.getPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -398,15 +402,17 @@
                 e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
     }
 
-    private static void validateInput(String packageName, Context context)
+    private static void validateInput(Context context, @Nullable String packageName)
             throws RemoteException {
+        if (packageName == null) return;
+
         try {
             final int uid = context.getPackageManager()
                     .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
             Preconditions.checkArgument(Binder.getCallingUid() == uid);
-        } catch (IllegalArgumentException | NullPointerException |
-                PackageManager.NameNotFoundException e) {
-            throw new RemoteException(e.getMessage());
+        } catch (Exception e) {
+            throw new RemoteException(
+                    String.format("Invalid package: name=%s, error=%s", packageName, e));
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index da997ba..10542d5 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -48,12 +48,16 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.RootActivityContainer.FindTaskResult;
 import static com.android.server.wm.RootActivityContainer.TAG_STATES;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.WindowConfiguration;
 import android.content.res.Configuration;
 import android.graphics.Point;
+import android.os.IBinder;
 import android.os.UserHandle;
 import android.util.IntArray;
 import android.util.Slog;
@@ -86,6 +90,8 @@
 
     private ActivityTaskManagerService mService;
     private RootActivityContainer mRootActivityContainer;
+    // TODO: Remove once unification is complete.
+    DisplayContent mDisplayContent;
     /** Actual Display this object tracks. */
     int mDisplayId;
     Display mDisplay;
@@ -138,8 +144,6 @@
     // Used in updating the display size
     private Point mTmpDisplaySize = new Point();
 
-    private DisplayWindowController mWindowContainerController;
-
     private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
 
     ActivityDisplay(RootActivityContainer root, Display display) {
@@ -147,19 +151,15 @@
         mService = root.mService;
         mDisplayId = display.getDisplayId();
         mDisplay = display;
-        mWindowContainerController = createWindowContainerController();
+        mDisplayContent = createDisplayContent();
         updateBounds();
     }
 
-    protected DisplayWindowController createWindowContainerController() {
-        return new DisplayWindowController(mDisplay, this);
+    protected DisplayContent createDisplayContent() {
+        return mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
     }
 
-    DisplayWindowController getWindowContainerController() {
-        return mWindowContainerController;
-    }
-
-    void updateBounds() {
+    private void updateBounds() {
         mDisplay.getRealSize(mTmpDisplaySize);
         setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
     }
@@ -178,7 +178,10 @@
         }
 
         updateBounds();
-        mWindowContainerController.onDisplayChanged();
+        if (mDisplayContent != null) {
+            mDisplayContent.updateDisplayInfo();
+            mService.mWindowManager.requestTraversal();
+        }
     }
 
     @Override
@@ -270,9 +273,9 @@
         // ActivityStack#getWindowContainerController() can be null. In this special case,
         // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
         // we don't have to call WindowContainerController#positionChildAt() here.
-        if (stack.getWindowContainerController() != null) {
-            mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
-                    insertPosition, includingParents);
+        if (stack.getWindowContainerController() != null && mDisplayContent != null) {
+            mDisplayContent.positionStackAt(insertPosition,
+                    stack.getWindowContainerController().mContainer, includingParents);
         }
         if (!wasContained) {
             stack.setParent(this);
@@ -958,17 +961,23 @@
                 getRequestedOverrideConfiguration().windowConfiguration.getRotation();
         if (currRotation != ROTATION_UNDEFINED
                 && currRotation != overrideConfiguration.windowConfiguration.getRotation()
-                && getWindowContainerController() != null) {
-            getWindowContainerController().applyRotation(currRotation,
+                && mDisplayContent != null) {
+            mDisplayContent.applyRotationLocked(currRotation,
                     overrideConfiguration.windowConfiguration.getRotation());
         }
         super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
+        if (mDisplayContent != null) {
+            mService.mWindowManager.setNewDisplayOverrideConfiguration(
+                    overrideConfiguration, mDisplayContent);
+        }
     }
 
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         // update resources before cascade so that docked/pinned stacks use the correct info
-        getWindowContainerController().preOnConfigurationChanged();
+        if (mDisplayContent != null) {
+            mDisplayContent.preOnConfigurationChanged();
+        }
         super.onConfigurationChanged(newParentConfig);
     }
 
@@ -1099,8 +1108,8 @@
 
     private void releaseSelfIfNeeded() {
         if (mStacks.isEmpty() && mRemoved) {
-            mWindowContainerController.removeContainer();
-            mWindowContainerController = null;
+            mDisplayContent.removeIfPossible();
+            mDisplayContent = null;
             mRootActivityContainer.removeChild(this);
             mRootActivityContainer.mStackSupervisor
                     .getKeyguardController().onDisplayRemoved(mDisplayId);
@@ -1122,7 +1131,7 @@
      * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
      */
     boolean supportsSystemDecorations() {
-        return mWindowContainerController.supportsSystemDecorations();
+        return mDisplayContent.supportsSystemDecorations();
     }
 
     @VisibleForTesting
@@ -1136,7 +1145,30 @@
     }
 
     void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
-        mWindowContainerController.setFocusedApp(r.appToken, moveFocusNow);
+        if (mDisplayContent == null) {
+            return;
+        }
+        final AppWindowToken newFocus;
+        final IBinder token = r.appToken;
+        if (token == null) {
+            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, displayId="
+                    + mDisplayId);
+            newFocus = null;
+        } else {
+            newFocus = mService.mWindowManager.mRoot.getAppWindowToken(token);
+            if (newFocus == null) {
+                Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
+                        + ", displayId=" + mDisplayId);
+            }
+            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
+                    + " moveFocusNow=" + moveFocusNow + " displayId=" + mDisplayId);
+        }
+
+        final boolean changed = mDisplayContent.setFocusedApp(newFocus);
+        if (moveFocusNow && changed) {
+            mService.mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+                    true /*updateInputWindows*/);
+        }
     }
 
     /**
@@ -1284,17 +1316,21 @@
     }
 
     /**
-     * See {@link DisplayWindowController#deferUpdateImeTarget()}
+     * See {@link DisplayContent#deferUpdateImeTarget()}
      */
     public void deferUpdateImeTarget() {
-        mWindowContainerController.deferUpdateImeTarget();
+        if (mDisplayContent != null) {
+            mDisplayContent.deferUpdateImeTarget();
+        }
     }
 
     /**
-     * See {@link DisplayWindowController#deferUpdateImeTarget()}
+     * See {@link DisplayContent#deferUpdateImeTarget()}
      */
     public void continueUpdateImeTarget() {
-        mWindowContainerController.continueUpdateImeTarget();
+        if (mDisplayContent != null) {
+            mDisplayContent.continueUpdateImeTarget();
+        }
     }
 
     public void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 26a4cef..4e9c5ab 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2942,7 +2942,7 @@
             final ActivityLifecycleItem lifecycleItem;
             if (andResume) {
                 lifecycleItem = ResumeActivityItem.obtain(
-                        getDisplay().getWindowContainerController().isNextTransitionForward());
+                        getDisplay().mDisplayContent.isNextTransitionForward());
             } else {
                 lifecycleItem = PauseActivityItem.obtain();
             }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index c41a173..7683172 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -574,7 +574,7 @@
                 // bounds were on the pre-rotated display.
                 if (prevRotation != newRotation) {
                     mTmpRect2.set(mTmpRect);
-                    getDisplay().getWindowContainerController().mContainer
+                    getDisplay().mDisplayContent
                             .rotateBounds(newParentConfig.windowConfiguration.getBounds(),
                                     prevRotation, newRotation, mTmpRect2);
                     hasNewOverrideBounds = true;
@@ -609,8 +609,8 @@
             } else if (
                     getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
                 Rect dockedBounds = display.getSplitScreenPrimaryStack().getBounds();
-                final boolean isMinimizedDock = getDisplay().getWindowContainerController()
-                        .mContainer.getDockedDividerController().isMinimizedDock();
+                final boolean isMinimizedDock =
+                        getDisplay().mDisplayContent.getDockedDividerController().isMinimizedDock();
                 if (isMinimizedDock) {
                     TaskRecord topTask = display.getSplitScreenPrimaryStack().topTask();
                     if (topTask != null) {
@@ -2714,16 +2714,16 @@
         // that the previous one will be hidden soon.  This way it can know
         // to ignore it when computing the desired screen orientation.
         boolean anim = true;
-        final DisplayWindowController dwc = getDisplay().getWindowContainerController();
+        final DisplayContent dc = getDisplay().mDisplayContent;
         if (prev != null) {
             if (prev.finishing) {
                 if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                         "Prepare close transition: prev=" + prev);
                 if (mStackSupervisor.mNoAnimActivities.contains(prev)) {
                     anim = false;
-                    dwc.prepareAppTransition(TRANSIT_NONE, false);
+                    dc.prepareAppTransition(TRANSIT_NONE, false);
                 } else {
-                    dwc.prepareAppTransition(
+                    dc.prepareAppTransition(
                             prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_CLOSE
                                     : TRANSIT_TASK_CLOSE, false);
                 }
@@ -2733,9 +2733,9 @@
                         "Prepare open transition: prev=" + prev);
                 if (mStackSupervisor.mNoAnimActivities.contains(next)) {
                     anim = false;
-                    dwc.prepareAppTransition(TRANSIT_NONE, false);
+                    dc.prepareAppTransition(TRANSIT_NONE, false);
                 } else {
-                    dwc.prepareAppTransition(
+                    dc.prepareAppTransition(
                             prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_OPEN
                                     : next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND
                                             : TRANSIT_TASK_OPEN, false);
@@ -2745,9 +2745,9 @@
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
             if (mStackSupervisor.mNoAnimActivities.contains(next)) {
                 anim = false;
-                dwc.prepareAppTransition(TRANSIT_NONE, false);
+                dc.prepareAppTransition(TRANSIT_NONE, false);
             } else {
-                dwc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false);
+                dc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false);
             }
         }
 
@@ -2869,8 +2869,7 @@
                 next.clearOptionsLocked();
                 transaction.setLifecycleStateRequest(
                         ResumeActivityItem.obtain(next.app.getReportedProcState(),
-                                getDisplay().getWindowContainerController()
-                                        .isNextTransitionForward()));
+                                getDisplay().mDisplayContent.isNextTransitionForward()));
                 mService.getLifecycleManager().scheduleTransaction(transaction);
 
                 if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
@@ -3072,11 +3071,11 @@
         task.setFrontOfTask();
 
         if (!isHomeOrRecentsStack() || numActivities() > 0) {
-            final DisplayWindowController dwc = getDisplay().getWindowContainerController();
+            final DisplayContent dc = getDisplay().mDisplayContent;
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                     "Prepare open transition: starting " + r);
             if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
-                dwc.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
+                dc.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
                 mStackSupervisor.mNoAnimActivities.add(r);
             } else {
                 int transit = TRANSIT_ACTIVITY_OPEN;
@@ -3095,7 +3094,7 @@
                         transit = TRANSIT_TASK_OPEN;
                     }
                 }
-                dwc.prepareAppTransition(transit, keepCurTransition);
+                dc.prepareAppTransition(transit, keepCurTransition);
                 mStackSupervisor.mNoAnimActivities.remove(r);
             }
             boolean doShow = true;
@@ -3724,7 +3723,7 @@
         int taskNdx = mTaskHistory.indexOf(finishedTask);
         final TaskRecord task = finishedTask;
         int activityNdx = task.mActivities.indexOf(r);
-        getDisplay().getWindowContainerController().prepareAppTransition(
+        getDisplay().mDisplayContent.prepareAppTransition(
                 TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
         finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
         finishedTask = task;
@@ -3890,7 +3889,7 @@
                     mService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
                             task.taskId);
                 }
-                getDisplay().getWindowContainerController().prepareAppTransition(transit, false);
+                getDisplay().mDisplayContent.prepareAppTransition(transit, false);
 
                 // Tell window manager to prepare for this one to be removed.
                 r.setVisibility(false);
@@ -3945,10 +3944,10 @@
     }
 
     private void prepareActivityHideTransitionAnimation(ActivityRecord r, int transit) {
-        final DisplayWindowController dwc = getDisplay().getWindowContainerController();
-        dwc.prepareAppTransition(transit, false);
+        final DisplayContent dc = getDisplay().mDisplayContent;
+        dc.prepareAppTransition(transit, false);
         r.setVisibility(false);
-        dwc.executeAppTransition();
+        dc.executeAppTransition();
         if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
             mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(r);
         }
@@ -4692,7 +4691,7 @@
                 ActivityOptions.abort(options);
             }
         }
-        getDisplay().getWindowContainerController().prepareAppTransition(transit, false);
+        getDisplay().mDisplayContent.prepareAppTransition(transit, false);
     }
 
     private void updateTaskMovement(TaskRecord task, boolean toFront) {
@@ -4761,8 +4760,7 @@
 
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
             if (noAnimation) {
-                getDisplay().getWindowContainerController().prepareAppTransition(
-                        TRANSIT_NONE, false);
+                getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_NONE, false);
                 if (r != null) {
                     mStackSupervisor.mNoAnimActivities.add(r);
                 }
@@ -4844,8 +4842,7 @@
         mTaskHistory.add(0, tr);
         updateTaskMovement(tr, false);
 
-        getDisplay().getWindowContainerController().prepareAppTransition(
-                TRANSIT_TASK_TO_BACK, false);
+        getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
         moveToBack("moveTaskToBackLocked", tr);
 
         if (inPinnedWindowingMode()) {
@@ -5170,7 +5167,7 @@
                             + r.intent.getComponent().flattenToShortString());
                     // Force the destroy to skip right to removal.
                     r.app = null;
-                    getDisplay().getWindowContainerController().prepareAppTransition(
+                    getDisplay().mDisplayContent.prepareAppTransition(
                             TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
                     finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
                             "handleAppCrashedLocked");
@@ -5508,7 +5505,7 @@
     }
 
     void executeAppTransition(ActivityOptions options) {
-        getDisplay().getWindowContainerController().executeAppTransition();
+        getDisplay().mDisplayContent.executeAppTransition();
         ActivityOptions.abort(options);
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index d92a9f2..e761ad8 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -818,7 +818,7 @@
                 final ClientTransaction clientTransaction = ClientTransaction.obtain(
                         proc.getThread(), r.appToken);
 
-                final DisplayWindowController dwc = r.getDisplay().getWindowContainerController();
+                final DisplayContent dc = r.getDisplay().mDisplayContent;
                 clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                         System.identityHashCode(r), r.info,
                         // TODO: Have this take the merged configuration instead of separate global
@@ -827,12 +827,12 @@
                         mergedConfiguration.getOverrideConfiguration(), r.compat,
                         r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                         r.icicle, r.persistentState, results, newIntents,
-                        dwc.isNextTransitionForward(), profilerInfo));
+                        dc.isNextTransitionForward(), profilerInfo));
 
                 // Set desired final state.
                 final ActivityLifecycleItem lifecycleItem;
                 if (andResume) {
-                    lifecycleItem = ResumeActivityItem.obtain(dwc.isNextTransitionForward());
+                    lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
                 } else {
                     lifecycleItem = PauseActivityItem.obtain();
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index bc2136e..57bfc29 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1565,7 +1565,7 @@
                 mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 // Go ahead and tell window manager to execute app transition for this activity
                 // since the app transition will not be triggered through the resume channel.
-                mTargetStack.getDisplay().getWindowContainerController().executeAppTransition();
+                mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
             } else {
                 // If the target stack was not previously focusable (previous top running activity
                 // on that stack was not visible) then any prior calls to move the stack to the
@@ -2506,8 +2506,9 @@
             // full resolution.
             mLaunchParams.mPreferredDisplayId =
                     mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY;
+            final boolean onTop = aOptions == null || !aOptions.getAvoidMoveToFront();
             final ActivityStack stack =
-                    mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP, mLaunchParams);
+                    mRootActivityContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams);
             mLaunchParams.mPreferredDisplayId = mPreferredDisplayId;
             return stack;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 61eb9d4..182d1a0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1738,7 +1738,7 @@
 
             if (self.isState(
                     ActivityStack.ActivityState.RESUMED, ActivityStack.ActivityState.PAUSING)) {
-                self.getDisplay().getWindowContainerController().overridePendingAppTransition(
+                self.getDisplay().mDisplayContent.mAppTransition.overridePendingAppTransition(
                         packageName, enterAnim, exitAnim, null);
             }
 
@@ -3073,12 +3073,11 @@
         // Get top display of front most application.
         final ActivityStack focusedStack = getTopDisplayFocusedStack();
         if (focusedStack != null) {
-            final DisplayWindowController dwc =
-                    focusedStack.getDisplay().getWindowContainerController();
-            dwc.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
-            dwc.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
+            final DisplayContent dc = focusedStack.getDisplay().mDisplayContent;
+            dc.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
+            dc.mAppTransition.overrideInPlaceAppTransition(activityOptions.getPackageName(),
                     activityOptions.getCustomInPlaceResId());
-            dwc.executeAppTransition();
+            dc.executeAppTransition();
         }
     }
 
@@ -5770,17 +5769,18 @@
                 if (activityDisplay == null) {
                     return;
                 }
-                final DisplayWindowController dwc = activityDisplay.getWindowContainerController();
-                final boolean wasTransitionSet = dwc.getPendingAppTransition() != TRANSIT_NONE;
+                final DisplayContent dc = activityDisplay.mDisplayContent;
+                final boolean wasTransitionSet =
+                        dc.mAppTransition.getAppTransition() != TRANSIT_NONE;
                 if (!wasTransitionSet) {
-                    dwc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
+                    dc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
                 }
                 mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
 
                 // If there was a transition set already we don't want to interfere with it as we
                 // might be starting it too early.
                 if (!wasTransitionSet) {
-                    dwc.executeAppTransition();
+                    dc.executeAppTransition();
                 }
             }
             if (callback != null) {
@@ -6270,18 +6270,20 @@
                     finishInstrumentationCallback.run();
                 }
 
-                mWindowManager.deferSurfaceLayout();
-                try {
-                    if (!restarting && hasVisibleActivities
-                            && !mRootActivityContainer.resumeFocusedStacksTopActivities()) {
-                        // If there was nothing to resume, and we are not already restarting this
-                        // process, but there is a visible activity that is hosted by the process...
-                        // then make sure all visible activities are running, taking care of
-                        // restarting this process.
-                        mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+                if (!restarting && hasVisibleActivities) {
+                    mWindowManager.deferSurfaceLayout();
+                    try {
+                        if (!mRootActivityContainer.resumeFocusedStacksTopActivities()) {
+                            // If there was nothing to resume, and we are not already restarting
+                            // this process, but there is a visible activity that is hosted by the
+                            // process...then make sure all visible activities are running, taking
+                            // care of restarting this process.
+                            mRootActivityContainer.ensureActivitiesVisible(null, 0,
+                                    !PRESERVE_WINDOWS);
+                        }
+                    } finally {
+                        mWindowManager.continueSurfaceLayout();
                     }
-                } finally {
-                    mWindowManager.continueSurfaceLayout();
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index aba2eb3..2f4c5ca 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -59,8 +59,11 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -207,6 +210,9 @@
     /** Unique identifier of this stack. */
     private final int mDisplayId;
 
+    // TODO: Remove once unification is complete.
+    ActivityDisplay mAcitvityDisplay;
+
     /** The containers below are the only child containers the display can have. */
     // Contains all window containers that are related to apps (Activities)
     private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mWmService);
@@ -227,7 +233,6 @@
 
     private WindowState mTmpWindow;
     private WindowState mTmpWindow2;
-    private WindowAnimator mTmpWindowAnimator;
     private boolean mTmpRecoveringMemory;
     private boolean mUpdateImeTarget;
     private boolean mTmpInitial;
@@ -827,12 +832,12 @@
      * initialize direct children.
      * @param display May not be null.
      * @param service You know.
-     * @param controller The controller for the display container.
+     * @param activityDisplay The ActivityDisplay for the display container.
      */
     DisplayContent(Display display, WindowManagerService service,
-            DisplayWindowController controller) {
+            ActivityDisplay activityDisplay) {
         super(service);
-        setController(controller);
+        mAcitvityDisplay = activityDisplay;
         if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
             throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
                     + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
@@ -1020,11 +1025,6 @@
     }
 
     @Override
-    DisplayWindowController getController() {
-        return (DisplayWindowController) super.getController();
-    }
-
-    @Override
     public Display getDisplay() {
         return mDisplay;
     }
@@ -1138,6 +1138,17 @@
         return true;
     }
 
+    /**
+     * The display content may have configuration set from {@link #DisplayWindowSettings}. This
+     * callback let the owner of container know there is existing configuration to prevent the
+     * values from being replaced by the initializing {@link #ActivityDisplay}.
+     */
+    void initializeDisplayOverrideConfiguration() {
+        if (mAcitvityDisplay != null) {
+            mAcitvityDisplay.onInitializeOverrideConfiguration(getRequestedOverrideConfiguration());
+        }
+    }
+
     /** Notify the configuration change of this display. */
     void sendNewConfiguration() {
         mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
@@ -4686,6 +4697,11 @@
     }
 
     void prepareAppTransition(@WindowManager.TransitionType int transit,
+            boolean alwaysKeepCurrent) {
+        prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
+    }
+
+    void prepareAppTransition(@WindowManager.TransitionType int transit,
             boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags,
             boolean forceOverride) {
         final boolean prepared = mAppTransition.prepareAppTransitionLocked(
@@ -4737,6 +4753,14 @@
         pendingLayoutChanges |= changes;
     }
 
+    /** Check if pending app transition is for activity / task launch. */
+    boolean isNextTransitionForward() {
+        final int transit = mAppTransition.getAppTransition();
+        return transit == TRANSIT_ACTIVITY_OPEN
+                || transit == TRANSIT_TASK_OPEN
+                || transit == TRANSIT_TASK_TO_FRONT;
+    }
+
     /**
      * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
      */
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index d4bd91b..8b8cadc 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -763,6 +763,12 @@
                         || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) {
                     attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT;
                 }
+                // Accessibility users may need longer timeout duration. This api compares
+                // original timeout with user's preference and return longer one. It returns
+                // original timeout if there's no preference.
+                attrs.hideTimeoutMilliseconds = mAccessibilityManager.getRecommendedTimeoutMillis(
+                        (int) attrs.hideTimeoutMilliseconds,
+                        AccessibilityManager.FLAG_CONTENT_TEXT);
                 attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
                 break;
         }
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
deleted file mode 100644
index c7eadf9..0000000
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
-
-import android.content.res.Configuration;
-import android.graphics.GraphicBuffer;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.IRemoteCallback;
-import android.util.Slog;
-import android.view.AppTransitionAnimationSpec;
-import android.view.Display;
-import android.view.WindowManager;
-import android.view.WindowManager.TransitionType;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Controller for the display container. This is created by activity manager to link activity
- * displays to the display content they use in window manager.
- */
-public class DisplayWindowController
-        extends WindowContainerController<DisplayContent, WindowContainerListener> {
-
-    private final int mDisplayId;
-
-    public DisplayWindowController(Display display, WindowContainerListener listener) {
-        super(listener, WindowManagerService.getInstance());
-        mDisplayId = display.getDisplayId();
-
-        synchronized (mGlobalLock) {
-            final long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                mRoot.createDisplayContent(display, this /* controller */);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
-
-            if (mContainer == null) {
-                throw new IllegalArgumentException("Trying to add display=" + display
-                        + " dc=" + mRoot.getDisplayContent(mDisplayId));
-            }
-        }
-    }
-
-    @VisibleForTesting
-    public DisplayWindowController(Display display, WindowManagerService service) {
-        super(null, service);
-        mDisplayId = display.getDisplayId();
-    }
-
-    @Override
-    public void removeContainer() {
-        synchronized (mGlobalLock) {
-            if(mContainer == null) {
-                if (DEBUG_DISPLAY) Slog.i(TAG_WM, "removeDisplay: could not find displayId="
-                        + mDisplayId);
-                return;
-            }
-            mContainer.removeIfPossible();
-            super.removeContainer();
-        }
-    }
-
-    @Override
-    public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration,
-                        mContainer);
-            }
-        }
-    }
-
-    /**
-     * Updates the docked/pinned controller resources to the current system context.
-     */
-    public void preOnConfigurationChanged() {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.preOnConfigurationChanged();
-            }
-        }
-    }
-
-  /**
-   * @see DisplayContent#applyRotationLocked(int, int)
-   */
-    public void applyRotation(int oldRotation, int newRotation) {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.applyRotationLocked(oldRotation, newRotation);
-            }
-        }
-    }
-
-    public int getDisplayId() {
-        return mDisplayId;
-    }
-
-    /**
-     * Called when the corresponding display receives
-     * {@link android.hardware.display.DisplayManager.DisplayListener#onDisplayChanged(int)}.
-     */
-    public void onDisplayChanged() {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                if (DEBUG_DISPLAY) Slog.i(TAG_WM, "onDisplayChanged: could not find display="
-                        + mDisplayId);
-                return;
-            }
-            mContainer.updateDisplayInfo();
-            mService.mWindowPlacerLocked.requestTraversal();
-        }
-    }
-
-    /**
-     * Positions the task stack at the given position in the task stack container.
-     */
-    public void positionChildAt(StackWindowController child, int position,
-            boolean includingParents) {
-        synchronized (mGlobalLock) {
-            if (DEBUG_STACK) Slog.i(TAG_WM, "positionTaskStackAt: positioning stack=" + child
-                    + " at " + position);
-            if (mContainer == null) {
-                if (DEBUG_STACK) Slog.i(TAG_WM,
-                        "positionTaskStackAt: could not find display=" + mContainer);
-                return;
-            }
-            if (child.mContainer == null) {
-                if (DEBUG_STACK) Slog.i(TAG_WM,
-                        "positionTaskStackAt: could not find stack=" + this);
-                return;
-            }
-            mContainer.positionStackAt(position, child.mContainer, includingParents);
-        }
-    }
-
-    /**
-     * Starts deferring the ability to update the IME target. This is needed when a call will
-     * attempt to update the IME target before all information about the Windows have been updated.
-     */
-    public void deferUpdateImeTarget() {
-        synchronized (mGlobalLock) {
-            final DisplayContent dc = mRoot.getDisplayContent(mDisplayId);
-            if (dc != null) {
-                dc.deferUpdateImeTarget();
-            }
-        }
-    }
-
-    /**
-     * Resumes updating the IME target after deferring. See {@link #deferUpdateImeTarget()}
-     */
-    public void continueUpdateImeTarget() {
-        synchronized (mGlobalLock) {
-            final DisplayContent dc = mRoot.getDisplayContent(mDisplayId);
-            if (dc != null) {
-                dc.continueUpdateImeTarget();
-            }
-        }
-    }
-
-    /**
-     * Sets a focused app on this display.
-     *
-     * @param token Specifies which app should be focused.
-     * @param moveFocusNow Specifies if we should update the focused window immediately.
-     */
-    public void setFocusedApp(IBinder token, boolean moveFocusNow) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "setFocusedApp: could not find displayId="
-                        + mDisplayId);
-                return;
-            }
-            final AppWindowToken newFocus;
-            if (token == null) {
-                if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, displayId="
-                        + mDisplayId);
-                newFocus = null;
-            } else {
-                newFocus = mRoot.getAppWindowToken(token);
-                if (newFocus == null) {
-                    Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
-                            + ", displayId=" + mDisplayId);
-                }
-                if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
-                        + " moveFocusNow=" + moveFocusNow + " displayId=" + mDisplayId);
-            }
-
-            final boolean changed = mContainer.setFocusedApp(newFocus);
-            if (moveFocusNow && changed) {
-                mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
-                        true /*updateInputWindows*/);
-            }
-        }
-    }
-
-    public void prepareAppTransition(@WindowManager.TransitionType int transit,
-            boolean alwaysKeepCurrent) {
-        prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
-    }
-
-    /**
-     * @param transit What kind of transition is happening. Use one of the constants
-     *                AppTransition.TRANSIT_*.
-     * @param alwaysKeepCurrent If true and a transition is already set, new transition will NOT
-     *                          be set.
-     * @param flags Additional flags for the app transition, Use a combination of the constants
-     *              AppTransition.TRANSIT_FLAG_*.
-     * @param forceOverride Always override the transit, not matter what was set previously.
-     */
-    public void prepareAppTransition(@WindowManager.TransitionType int transit,
-            boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags,
-            boolean forceOverride) {
-        synchronized (mGlobalLock) {
-            mRoot.getDisplayContent(mDisplayId).prepareAppTransition(transit, alwaysKeepCurrent,
-                    flags, forceOverride);
-        }
-    }
-
-    public void executeAppTransition() {
-        synchronized (mGlobalLock) {
-            mRoot.getDisplayContent(mDisplayId).executeAppTransition();
-        }
-    }
-
-    public void overridePendingAppTransition(String packageName,
-            int enterAnim, int exitAnim, IRemoteCallback startedCallback) {
-        synchronized (mGlobalLock) {
-            mRoot.getDisplayContent(mDisplayId).mAppTransition.overridePendingAppTransition(
-                    packageName, enterAnim, exitAnim, startedCallback);
-        }
-    }
-
-    public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
-            int startHeight) {
-        synchronized (mGlobalLock) {
-            mRoot.getDisplayContent(mDisplayId).mAppTransition.overridePendingAppTransitionScaleUp(
-                    startX, startY, startWidth, startHeight);
-        }
-    }
-
-    public void overridePendingAppTransitionClipReveal(int startX, int startY,
-            int startWidth, int startHeight) {
-        synchronized (mGlobalLock) {
-            mRoot.getDisplayContent(mDisplayId)
-                    .mAppTransition.overridePendingAppTransitionClipReveal(startX, startY,
-                    startWidth, startHeight);
-        }
-    }
-
-    public void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX,
-            int startY, IRemoteCallback startedCallback, boolean scaleUp) {
-        synchronized (mGlobalLock) {
-            mRoot.getDisplayContent(mDisplayId)
-                    .mAppTransition.overridePendingAppTransitionThumb(srcThumb, startX, startY,
-                    startedCallback, scaleUp);
-        }
-    }
-
-    public void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX,
-            int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
-            boolean scaleUp) {
-        synchronized (mGlobalLock) {
-            mRoot.getDisplayContent(mDisplayId)
-                    .mAppTransition.overridePendingAppTransitionAspectScaledThumb(srcThumb, startX,
-                    startY, targetWidth, targetHeight, startedCallback, scaleUp);
-        }
-    }
-
-    public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
-            IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
-            boolean scaleUp) {
-        synchronized (mGlobalLock) {
-            mRoot.getDisplayContent(mDisplayId)
-                    .mAppTransition.overridePendingAppTransitionMultiThumb(specs,
-                    onAnimationStartedCallback, onAnimationFinishedCallback, scaleUp);
-        }
-    }
-
-    public void overridePendingAppTransitionStartCrossProfileApps() {
-        synchronized (mGlobalLock) {
-            mRoot.getDisplayContent(mDisplayId)
-                    .mAppTransition.overridePendingAppTransitionStartCrossProfileApps();
-        }
-    }
-
-    public void overridePendingAppTransitionInPlace(String packageName, int anim) {
-        synchronized (mGlobalLock) {
-            mRoot.getDisplayContent(mDisplayId)
-                    .mAppTransition.overrideInPlaceAppTransition(packageName, anim);
-        }
-    }
-
-    /**
-     * Get Pending App transition of display.
-     *
-     * @return The pending app transition of the display.
-     */
-    public @TransitionType int getPendingAppTransition() {
-        synchronized (mGlobalLock) {
-            return mRoot.getDisplayContent(mDisplayId).mAppTransition.getAppTransition();
-        }
-    }
-
-    /**
-     * Check if pending app transition is for activity / task launch.
-     */
-    public boolean isNextTransitionForward() {
-        final int transit = getPendingAppTransition();
-        return transit == TRANSIT_ACTIVITY_OPEN
-                || transit == TRANSIT_TASK_OPEN
-                || transit == TRANSIT_TASK_TO_FRONT;
-    }
-
-    /**
-     * Checks if system decorations should be shown on this display.
-     *
-     * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
-     */
-    public boolean supportsSystemDecorations() {
-        synchronized (mGlobalLock) {
-            return mContainer.supportsSystemDecorations();
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "{DisplayWindowController displayId=" + mDisplayId + "}";
-    }
-}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 639ed02..f9c9d33 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -1,5 +1,6 @@
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
@@ -9,7 +10,6 @@
 import android.os.Debug;
 import android.os.IBinder;
 import android.util.Slog;
-import android.view.InputApplicationHandle;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
@@ -204,6 +204,37 @@
                 + WindowManagerService.TYPE_LAYER_OFFSET;
     }
 
+    /** Callback to get pointer display id. */
+    @Override
+    public int getPointerDisplayId() {
+        synchronized (mService.mGlobalLock) {
+            // If desktop mode is not enabled, show on the default display.
+            if (!mService.mForceDesktopModeOnExternalDisplays) {
+                return DEFAULT_DISPLAY;
+            }
+
+            // Look for the topmost freeform display.
+            int firstExternalDisplayId = DEFAULT_DISPLAY;
+            for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
+                final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
+                // Heuristic solution here. Currently when "Freeform windows" developer option is
+                // enabled we automatically put secondary displays in freeform mode and emulating
+                // "desktop mode". It also makes sense to show the pointer on the same display.
+                if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+                    return displayContent.getDisplayId();
+                }
+
+                if (firstExternalDisplayId == DEFAULT_DISPLAY
+                        && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
+                    firstExternalDisplayId = displayContent.getDisplayId();
+                }
+            }
+
+            // Look for the topmost non-default display
+            return firstExternalDisplayId;
+        }
+    }
+
     /** Waits until the built-in input devices have been configured. */
     public boolean waitForInputDevicesReady(long timeoutMillis) {
         synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 4ef3513..5f56fe5 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -174,7 +174,7 @@
         mWindowManager.deferSurfaceLayout();
         try {
             setKeyguardGoingAway(true);
-            mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
+            mRootActivityContainer.getDefaultDisplay().mDisplayContent
                     .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
                             false /* alwaysKeepCurrent */, convertTransitFlags(flags),
                             false /* forceOverride */);
@@ -302,7 +302,7 @@
         if (isKeyguardLocked()) {
             mWindowManager.deferSurfaceLayout();
             try {
-                mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
+                mRootActivityContainer.getDefaultDisplay().mDisplayContent
                         .prepareAppTransition(resolveOccludeTransit(),
                                 false /* alwaysKeepCurrent */, 0 /* flags */,
                                 true /* forceOverride */);
@@ -332,11 +332,11 @@
 
         // If we are about to unocclude the Keyguard, but we can dismiss it without security,
         // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
-        final DisplayWindowController dwc =
-                mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
+        final DisplayContent dc =
+                mRootActivityContainer.getDefaultDisplay().mDisplayContent;
         if (mKeyguardShowing && canDismissKeyguard()
-                && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
-            dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
+                && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
+            dc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
                     0 /* flags */, true /* forceOverride */);
             mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
             mWindowManager.executeAppTransition();
@@ -355,10 +355,10 @@
     }
 
     private int resolveOccludeTransit() {
-        final DisplayWindowController dwc =
-                mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
+        final DisplayContent dc =
+                mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent;
         if (mBeforeUnoccludeTransit != TRANSIT_UNSET
-                && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
+                && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
                 // TODO(b/113840485): Handle app transition for individual display.
                 && isDisplayOccluded(DEFAULT_DISPLAY)) {
 
@@ -369,7 +369,7 @@
         } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) {
 
             // Save transit in case we dismiss/occlude Keyguard shortly after.
-            mBeforeUnoccludeTransit = dwc.getPendingAppTransition();
+            mBeforeUnoccludeTransit = dc.mAppTransition.getAppTransition();
             return TRANSIT_KEYGUARD_UNOCCLUDE;
         } else {
             return TRANSIT_KEYGUARD_OCCLUDE;
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index d2f2863..3b66f7d 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -582,7 +582,7 @@
             mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
             final ActivityStack stack = task.getStack();
             if (stack != null) {
-                stack.getDisplay().getWindowContainerController().executeAppTransition();
+                stack.getDisplay().mDisplayContent.executeAppTransition();
             }
         } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
             mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 9422182..42cd8e8 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -93,12 +93,12 @@
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
 
         // TODO(multi-display) currently only support recents animation in default display.
-        final DisplayWindowController dwc =
-                mService.mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
+        final DisplayContent dc =
+                mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent;
         if (!mWindowManager.canStartRecentsAnimation()) {
             notifyAnimationCancelBeforeStart(recentsAnimationRunner);
             if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
-                        + dwc.getPendingAppTransition());
+                        + dc.mAppTransition.getAppTransition());
             return;
         }
 
@@ -107,7 +107,7 @@
                 && recentsComponent.equals(intent.getComponent())
                         ? ACTIVITY_TYPE_RECENTS
                         : ACTIVITY_TYPE_HOME;
-        final ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
+        ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
                 mTargetActivityType);
         ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent());
         final boolean hasExistingActivity = targetActivity != null;
@@ -153,7 +153,7 @@
                             "startRecentsActivity");
                 }
             } else {
-                // No recents activity
+                // No recents activity, create the new recents activity bottom most
                 ActivityOptions options = ActivityOptions.makeBasic();
                 options.setLaunchActivityType(mTargetActivityType);
                 options.setAvoidMoveToFront();
@@ -166,11 +166,20 @@
                         .setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
                         .setMayWait(userId)
                         .execute();
+
+                // Move the recents activity into place for the animation
+                targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
+                        mTargetActivityType).getTopActivity();
+                targetStack = targetActivity.getActivityStack();
+                mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
+                if (DEBUG) {
+                    Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
+                            + mDefaultDisplay.getStackAbove(targetStack));
+                }
+
                 mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
                 mWindowManager.executeAppTransition();
 
-                targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
-                        mTargetActivityType).getTopActivity();
 
                 // TODO: Maybe wait for app to draw in this particular case?
 
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index f7877c0..c5b42f9 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -914,7 +914,7 @@
     void executeAppTransitionForAllDisplay() {
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            display.getWindowContainerController().executeAppTransition();
+            display.mDisplayContent.executeAppTransition();
         }
     }
 
@@ -1254,9 +1254,8 @@
     }
 
     // TODO: remove after object merge with RootWindowContainer
-    void onChildPositionChanged(DisplayWindowController childController, int position) {
+    void onChildPositionChanged(ActivityDisplay display, int position) {
         // Assume AM lock is held from positionChildAt of controller in each hierarchy.
-        final ActivityDisplay display = getActivityDisplay(childController.getDisplayId());
         if (display != null) {
             positionChildAt(display, position);
         }
@@ -1281,8 +1280,7 @@
     @VisibleForTesting
     void addChild(ActivityDisplay activityDisplay, int position) {
         positionChildAt(activityDisplay, position);
-        mRootWindowContainer.positionChildAt(position,
-                activityDisplay.getWindowContainerController().mContainer);
+        mRootWindowContainer.positionChildAt(position, activityDisplay.mDisplayContent);
     }
 
     void removeChild(ActivityDisplay activityDisplay) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3a5c578..3bbef92 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -224,7 +224,7 @@
         return null;
     }
 
-    DisplayContent createDisplayContent(final Display display, DisplayWindowController controller) {
+    DisplayContent createDisplayContent(final Display display, ActivityDisplay activityDisplay) {
         final int displayId = display.getDisplayId();
 
         // In select scenarios, it is possible that a DisplayContent will be created on demand
@@ -233,17 +233,17 @@
         final DisplayContent existing = getDisplayContent(displayId);
 
         if (existing != null) {
-            initializeDisplayOverrideConfiguration(controller, existing);
-            existing.setController(controller);
+            existing.mAcitvityDisplay = activityDisplay;
+            existing.initializeDisplayOverrideConfiguration();
             return existing;
         }
 
-        final DisplayContent dc = new DisplayContent(display, mWmService, controller);
+        final DisplayContent dc = new DisplayContent(display, mWmService, activityDisplay);
 
         if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
 
         mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
-        initializeDisplayOverrideConfiguration(controller, dc);
+        dc.initializeDisplayOverrideConfiguration();
 
         if (mWmService.mDisplayManagerInternal != null) {
             mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
@@ -256,19 +256,6 @@
         return dc;
     }
 
-    /**
-     * The display content may have configuration set from {@link #DisplayWindowSettings}. This
-     * callback let the owner of container know there is existing configuration to prevent the
-     * values from being replaced by the initializing {@link #ActivityDisplay}.
-     */
-    private void initializeDisplayOverrideConfiguration(DisplayWindowController controller,
-            DisplayContent displayContent) {
-        if (controller != null && controller.mListener != null) {
-            controller.mListener.onInitializeOverrideConfiguration(
-                    displayContent.getRequestedOverrideConfiguration());
-        }
-    }
-
     boolean isLayoutNeeded() {
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
@@ -1040,7 +1027,7 @@
     void positionChildAt(int position, DisplayContent child, boolean includingParents) {
         super.positionChildAt(position, child, includingParents);
         if (mRootActivityContainer != null) {
-            mRootActivityContainer.onChildPositionChanged(child.getController(), position);
+            mRootActivityContainer.onChildPositionChanged(child.mAcitvityDisplay, position);
         }
     }
 
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index b85489a..6d72191 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -126,7 +126,6 @@
 
     static_libs: [
         "android.hardware.broadcastradio@common-utils-1x-lib",
-        "libscrypt_static",
     ],
 
     product_variables: {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 43d2dcf..bf83ca9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,6 +107,7 @@
     jmethodID getLongPressTimeout;
     jmethodID getPointerLayer;
     jmethodID getPointerIcon;
+    jmethodID getPointerDisplayId;
     jmethodID getKeyboardLayoutOverlay;
     jmethodID getDeviceAlias;
     jmethodID getTouchCalibrationForInputDevice;
@@ -174,15 +175,6 @@
     loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
 }
 
-static void updatePointerControllerFromViewport(
-        sp<PointerController> controller, const DisplayViewport* const viewport) {
-    if (controller != nullptr && viewport != nullptr) {
-        const int32_t width = viewport->logicalRight - viewport->logicalLeft;
-        const int32_t height = viewport->logicalBottom - viewport->logicalTop;
-        controller->setDisplayViewport(width, height, viewport->orientation);
-    }
-}
-
 enum {
     WM_ACTION_PASS_TO_USER = 1,
 };
@@ -242,6 +234,7 @@
             jfloatArray matrixArr);
     virtual TouchAffineTransformation getTouchAffineTransformation(
             const std::string& inputDeviceDescriptor, int32_t surfaceRotation);
+    virtual void updatePointerDisplay();
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
@@ -314,10 +307,11 @@
 
     std::atomic<bool> mInteractive;
 
-    void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
+    void updateInactivityTimeoutLocked();
     void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
     void ensureSpriteControllerLocked();
-
+    const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
+    int32_t getPointerDisplayId();
     static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
 
     static inline JNIEnv* jniEnv() {
@@ -391,9 +385,10 @@
     return false;
 }
 
-static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
-    for (const DisplayViewport& v : viewports) {
-        if (v.type == ViewportType::VIEWPORT_INTERNAL) {
+const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
+        REQUIRES(mLock) {
+    for (const DisplayViewport& v : mLocked.viewports) {
+        if (v.displayId == displayId) {
             return &v;
         }
     }
@@ -420,20 +415,10 @@
         }
     }
 
-    const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
-    {
+    { // acquire lock
         AutoMutex _l(mLock);
-        const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
-        // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
-        // if they are different.
-        const bool internalViewportChanged = (newInternalViewport != nullptr) &&
-                (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
-        if (internalViewportChanged) {
-            sp<PointerController> controller = mLocked.pointerController.promote();
-            updatePointerControllerFromViewport(controller, newInternalViewport);
-        }
         mLocked.viewports = viewports;
-    }
+    } // release lock
 
     mInputManager->getReader()->requestRefreshConfiguration(
             InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -556,15 +541,43 @@
 
         controller = new PointerController(this, mLooper, mLocked.spriteController);
         mLocked.pointerController = controller;
-
-        const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
-        updatePointerControllerFromViewport(controller, internalViewport);
-
-        updateInactivityTimeoutLocked(controller);
+        updateInactivityTimeoutLocked();
     }
+
     return controller;
 }
 
+int32_t NativeInputManager::getPointerDisplayId() {
+    JNIEnv* env = jniEnv();
+    jint pointerDisplayId = env->CallIntMethod(mServiceObj,
+            gServiceClassInfo.getPointerDisplayId);
+    if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
+        pointerDisplayId = ADISPLAY_ID_DEFAULT;
+    }
+
+    return pointerDisplayId;
+}
+
+void NativeInputManager::updatePointerDisplay() {
+    ATRACE_CALL();
+
+    jint pointerDisplayId = getPointerDisplayId();
+
+    AutoMutex _l(mLock);
+    sp<PointerController> controller = mLocked.pointerController.promote();
+    if (controller != nullptr) {
+        const DisplayViewport* viewport = findDisplayViewportLocked(pointerDisplayId);
+        if (viewport == nullptr) {
+            ALOGW("Can't find pointer display viewport, fallback to default display.");
+            viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
+        }
+
+        if (viewport != nullptr) {
+            controller->setDisplayViewport(*viewport);
+        }
+    }
+}
+
 void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
     if (mLocked.spriteController == nullptr) {
         JNIEnv* env = jniEnv();
@@ -821,16 +834,16 @@
 
     if (mLocked.systemUiVisibility != visibility) {
         mLocked.systemUiVisibility = visibility;
-
-        sp<PointerController> controller = mLocked.pointerController.promote();
-        if (controller != nullptr) {
-            updateInactivityTimeoutLocked(controller);
-        }
+        updateInactivityTimeoutLocked();
     }
 }
 
-void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
-        REQUIRES(mLock) {
+void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
+    sp<PointerController> controller = mLocked.pointerController.promote();
+    if (controller == nullptr) {
+        return;
+    }
+
     bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
     controller->setInactivityTimeout(lightsOut
             ? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -1824,6 +1837,9 @@
     GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
             "getPointerIcon", "()Landroid/view/PointerIcon;");
 
+    GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
+            "getPointerDisplayId", "()I");
+
     GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
             "getKeyboardLayoutOverlay",
             "(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 4d0556c..0e349b7 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -756,14 +756,18 @@
     Return<void> gnssMeasurementCb(const IGnssMeasurementCallback_V1_1::GnssData& data) override;
     Return<void> GnssMeasurementCb(const IGnssMeasurementCallback_V1_0::GnssData& data) override;
  private:
-    void translateGnssMeasurement_V1_0(
-            const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurement,
-            JavaObject& object);
-    jobjectArray translateGnssMeasurements(
-            JNIEnv* env,
-            const IGnssMeasurementCallback_V1_1::GnssMeasurement* measurements_v1_1,
-            const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurements_v1_0,
-            size_t count);
+    template<class T>
+    void translateSingleGnssMeasurement(const T* measurement, JavaObject& object);
+
+    template<class T>
+    jobjectArray translateAllGnssMeasurements(JNIEnv* env, const T* measurements, size_t count);
+
+    template<class T>
+    void translateAndSetGnssData(const T& data);
+
+    template<class T>
+    size_t getMeasurementCount(const T& data);
+
     jobject translateGnssClock(
             JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssClock* clock);
     void setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray);
@@ -777,41 +781,48 @@
 
 Return<void> GnssMeasurementCallback::gnssMeasurementCb(
         const IGnssMeasurementCallback_V1_1::GnssData& data) {
-    JNIEnv* env = getJniEnv();
-
-    jobject clock;
-    jobjectArray measurementArray;
-
-    clock = translateGnssClock(env, &data.clock);
-
-    measurementArray = translateGnssMeasurements(
-        env, data.measurements.data(), nullptr, data.measurements.size());
-    setMeasurementData(env, clock, measurementArray);
-
-    env->DeleteLocalRef(clock);
-    env->DeleteLocalRef(measurementArray);
+    translateAndSetGnssData(data);
     return Void();
 }
 
 Return<void> GnssMeasurementCallback::GnssMeasurementCb(
         const IGnssMeasurementCallback_V1_0::GnssData& data) {
+    translateAndSetGnssData(data);
+    return Void();
+}
+
+template<class T>
+void GnssMeasurementCallback::translateAndSetGnssData(const T& data) {
     JNIEnv* env = getJniEnv();
 
     jobject clock;
     jobjectArray measurementArray;
 
     clock = translateGnssClock(env, &data.clock);
-    measurementArray = translateGnssMeasurements(
-        env, nullptr, data.measurements.data(), data.measurementCount);
+    size_t count = getMeasurementCount(data);
+    measurementArray = translateAllGnssMeasurements(env, data.measurements.data(), count);
     setMeasurementData(env, clock, measurementArray);
 
     env->DeleteLocalRef(clock);
     env->DeleteLocalRef(measurementArray);
-    return Void();
 }
 
-// preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
-void GnssMeasurementCallback::translateGnssMeasurement_V1_0(
+template<>
+size_t GnssMeasurementCallback::getMeasurementCount<IGnssMeasurementCallback_V1_0::GnssData>
+        (const IGnssMeasurementCallback_V1_0::GnssData& data) {
+    return data.measurementCount;
+}
+
+template<>
+size_t GnssMeasurementCallback::getMeasurementCount<IGnssMeasurementCallback_V1_1::GnssData>
+        (const IGnssMeasurementCallback_V1_1::GnssData& data) {
+    return data.measurements.size();
+}
+
+// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
+template<>
+void GnssMeasurementCallback::translateSingleGnssMeasurement
+        <IGnssMeasurementCallback_V1_0::GnssMeasurement>(
         const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurement,
         JavaObject& object) {
     uint32_t flags = static_cast<uint32_t>(measurement->flags);
@@ -852,6 +863,20 @@
     }
 }
 
+// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
+template<>
+void GnssMeasurementCallback::translateSingleGnssMeasurement
+        <IGnssMeasurementCallback_V1_1::GnssMeasurement>(
+        const IGnssMeasurementCallback_V1_1::GnssMeasurement* measurement_V1_1,
+        JavaObject& object) {
+    translateSingleGnssMeasurement(&(measurement_V1_1->v1_0), object);
+
+    // Set the V1_1 flag, and mark that new field has valid information for Java Layer
+    SET(AccumulatedDeltaRangeState,
+            (static_cast<int32_t>(measurement_V1_1->accumulatedDeltaRangeState) |
+            ADR_STATE_HALF_CYCLE_REPORTED));
+}
+
 jobject GnssMeasurementCallback::translateGnssClock(
        JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssClock* clock) {
     JavaObject object(env, "android/location/GnssClock");
@@ -891,10 +916,10 @@
     return object.get();
 }
 
-jobjectArray GnssMeasurementCallback::translateGnssMeasurements(JNIEnv* env,
-         const IGnssMeasurementCallback_V1_1::GnssMeasurement* measurements_v1_1,
-         const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurements_v1_0,
-         size_t count) {
+template<class T>
+jobjectArray GnssMeasurementCallback::translateAllGnssMeasurements(JNIEnv* env,
+        const T* measurements,
+        size_t count) {
     if (count == 0) {
         return nullptr;
     }
@@ -907,17 +932,7 @@
 
     for (uint16_t i = 0; i < count; ++i) {
         JavaObject object(env, "android/location/GnssMeasurement");
-        if (measurements_v1_1 != nullptr) {
-            translateGnssMeasurement_V1_0(&(measurements_v1_1[i].v1_0), object);
-
-            // Set the V1_1 flag, and mark that new field has valid information for Java Layer
-            SET(AccumulatedDeltaRangeState,
-                    (static_cast<int32_t>(measurements_v1_1[i].accumulatedDeltaRangeState) |
-                    ADR_STATE_HALF_CYCLE_REPORTED));
-        } else {
-            translateGnssMeasurement_V1_0(&(measurements_v1_0[i]), object);
-        }
-
+        translateSingleGnssMeasurement(&(measurements[i]), object);
         env->SetObjectArrayElement(gnssMeasurementArray, i, object.get());
     }
 
diff --git a/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp b/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp
index bc13fde..9dd6032 100644
--- a/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp
+++ b/services/core/jni/com_android_server_locksettings_SyntheticPasswordManager.cpp
@@ -27,10 +27,6 @@
 #include <gatekeeper/password_handle.h>
 
 
-extern "C" {
-#include "crypto_scrypt.h"
-}
-
 namespace android {
 
 static jlong android_server_SyntheticPasswordManager_nativeSidFromPasswordHandle(JNIEnv* env, jobject, jbyteArray handleArray) {
@@ -48,38 +44,9 @@
     }
 }
 
-static jbyteArray android_server_SyntheticPasswordManager_nativeScrypt(JNIEnv* env, jobject, jbyteArray password, jbyteArray salt, jint N, jint r, jint p, jint outLen) {
-    if (!password || !salt) {
-        return NULL;
-    }
-
-    int passwordLen = env->GetArrayLength(password);
-    int saltLen = env->GetArrayLength(salt);
-    jbyteArray ret = env->NewByteArray(outLen);
-
-    jbyte* passwordPtr = (jbyte*)env->GetByteArrayElements(password, NULL);
-    jbyte* saltPtr = (jbyte*)env->GetByteArrayElements(salt, NULL);
-    jbyte* retPtr = (jbyte*)env->GetByteArrayElements(ret, NULL);
-
-    int rc = crypto_scrypt((const uint8_t *)passwordPtr, passwordLen,
-                       (const uint8_t *)saltPtr, saltLen, N, r, p, (uint8_t *)retPtr,
-                       outLen);
-    env->ReleaseByteArrayElements(password, passwordPtr, JNI_ABORT);
-    env->ReleaseByteArrayElements(salt, saltPtr, JNI_ABORT);
-    env->ReleaseByteArrayElements(ret, retPtr, 0);
-
-    if (!rc) {
-        return ret;
-    } else {
-        SLOGE("scrypt failed");
-        return NULL;
-    }
-}
-
 static const JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"nativeSidFromPasswordHandle", "([B)J", (void*)android_server_SyntheticPasswordManager_nativeSidFromPasswordHandle},
-    {"nativeScrypt", "([B[BIIII)[B", (void*)android_server_SyntheticPasswordManager_nativeScrypt},
 };
 
 int register_android_server_SyntheticPasswordManager(JNIEnv* env) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 65d3245..c44f306 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -81,7 +81,8 @@
     }
 
     @Override
-    public void setGlobalPrivateDns(ComponentName who, int mode, String privateDnsHost) {
+    public int setGlobalPrivateDns(ComponentName who, int mode, String privateDnsHost) {
+        return DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
     }
 
     @Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f68f4d7..bfbaac9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -59,6 +59,8 @@
 import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_SUCCESS;
 import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
@@ -109,6 +111,7 @@
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyCache;
+import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.NetworkEvent;
@@ -195,6 +198,7 @@
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.service.persistentdata.PersistentDataBlockManager;
+import android.stats.devicepolicy.DevicePolicyEnums;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 import android.text.TextUtils;
@@ -205,7 +209,6 @@
 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;
@@ -4051,6 +4054,12 @@
             }
             maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PASSWORD_QUALITY)
+                .setAdmin(who)
+                .setInt(quality)
+                .setBoolean(parent)
+                .write();
     }
 
     /**
@@ -4164,6 +4173,11 @@
             }
             maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LENGTH)
+                .setAdmin(who)
+                .setInt(length)
+                .write();
     }
 
     @Override
@@ -4391,6 +4405,11 @@
             }
             maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_UPPER_CASE)
+                .setAdmin(who)
+                .setInt(length)
+                .write();
     }
 
     @Override
@@ -4414,6 +4433,11 @@
             }
             maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LOWER_CASE)
+                .setAdmin(who)
+                .setInt(length)
+                .write();
     }
 
     @Override
@@ -4440,6 +4464,11 @@
             }
             maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LETTERS)
+                .setAdmin(who)
+                .setInt(length)
+                .write();
     }
 
     @Override
@@ -4466,6 +4495,11 @@
             }
             maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_NUMERIC)
+                .setAdmin(who)
+                .setInt(length)
+                .write();
     }
 
     @Override
@@ -4492,6 +4526,11 @@
             }
             maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_SYMBOLS)
+                .setAdmin(who)
+                .setInt(length)
+                .write();
     }
 
     @Override
@@ -4518,6 +4557,11 @@
             }
             maybeLogPasswordComplexitySet(who, userId, parent, metrics);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_NON_LETTER)
+                .setAdmin(who)
+                .setInt(length)
+                .write();
     }
 
     @Override
@@ -4890,14 +4934,16 @@
     private boolean resetPasswordInternal(String password, long tokenHandle, byte[] token,
             int flags, int callingUid, int userHandle) {
         int quality;
+        final int realQuality;
         synchronized (getLockObject()) {
             quality = getPasswordQuality(null, userHandle, /* parent */ false);
             if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
                 quality = PASSWORD_QUALITY_UNSPECIFIED;
             }
             final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
+            realQuality = metrics.quality;
             if (quality != PASSWORD_QUALITY_UNSPECIFIED) {
-                final int realQuality = metrics.quality;
+
                 if (realQuality < quality
                         && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
                     Slog.w(LOG_TAG, "resetPassword: password quality 0x"
@@ -4984,7 +5030,7 @@
         try {
             if (token == null) {
                 if (!TextUtils.isEmpty(password)) {
-                    mLockPatternUtils.saveLockPassword(password, null, quality, userHandle);
+                    mLockPatternUtils.saveLockPassword(password, null, realQuality, userHandle);
                 } else {
                     mLockPatternUtils.clearLock(null, userHandle);
                 }
@@ -4993,7 +5039,7 @@
                 result = mLockPatternUtils.setLockCredentialWithToken(password,
                         TextUtils.isEmpty(password) ? LockPatternUtils.CREDENTIAL_TYPE_NONE
                                 : LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
-                                quality, tokenHandle, token, userHandle);
+                        realQuality, tokenHandle, token, userHandle);
             }
             boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
             if (requireEntry) {
@@ -5285,6 +5331,10 @@
                 mInjector.binderRestoreCallingIdentity(ident);
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.LOCK_NOW)
+                .setInt(flags)
+                .write();
     }
 
     @Override
@@ -5363,6 +5413,12 @@
         final long id = mInjector.binderClearCallingIdentity();
         try {
             alias = mCertificateMonitor.installCaCert(userHandle, certBuffer);
+            final boolean isDelegate = (admin == null);
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.INSTALL_CA_CERT)
+                    .setAdmin(callerPackage)
+                    .setBoolean(isDelegate)
+                    .write();
             if (alias == null) {
                 Log.w(LOG_TAG, "Problem installing cert");
                 return false;
@@ -5422,6 +5478,12 @@
                     keyChain.setGrant(callingUid, alias, true);
                 }
                 keyChain.setUserSelectable(alias, isUserSelectable);
+                final boolean isDelegate = (who == null);
+                DevicePolicyEventLogger
+                        .createEvent(DevicePolicyEnums.INSTALL_KEY_PAIR)
+                        .setAdmin(callerPackage)
+                        .setBoolean(isDelegate)
+                        .write();
                 return true;
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, "Installing certificate", e);
@@ -6116,6 +6178,11 @@
         synchronized (getLockObject()) {
             admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.WIPE_DATA_WITH_REASON)
+                .setAdmin(admin.info.getComponent())
+                .setInt(flags)
+                .write();
         String internalReason = "DevicePolicyManager.wipeDataWithReason() from "
                 + admin.info.getComponent().flattenToShortString();
         wipeDataNoLock(
@@ -7220,6 +7287,12 @@
             SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISABLED_FEATURES_SET,
                     who.getPackageName(), userHandle, affectedUserId, which);
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_KEYGUARD_DISABLED_FEATURES)
+                .setAdmin(who)
+                .setInt(which)
+                .setBoolean(parent)
+                .write();
     }
 
     /**
@@ -9466,7 +9539,14 @@
             }
             saveUserRestrictionsLocked(userHandle);
         }
-        StatsLog.write(StatsLog.USER_RESTRICTION_CHANGED, key, enabledFromThisOwner);
+        final int eventId = enabledFromThisOwner
+                ? DevicePolicyEnums.ADD_USER_RESTRICTION
+                : DevicePolicyEnums.REMOVE_USER_RESTRICTION;
+        DevicePolicyEventLogger
+                .createEvent(eventId)
+                .setAdmin(who)
+                .setStrings(key)
+                .write();
         if (SecurityLog.isLoggingEnabled()) {
             final int eventTag = enabledFromThisOwner
                     ? SecurityLog.TAG_USER_RESTRICTION_ADDED
@@ -10238,6 +10318,11 @@
                     try {
                         setUserRestriction(who, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
                                 (Integer.parseInt(value) == 0) ? true : false);
+                        DevicePolicyEventLogger
+                                .createEvent(DevicePolicyEnums.SET_SECURE_SETTING)
+                                .setAdmin(who)
+                                .setStrings(setting, value)
+                                .write();
                     } catch (NumberFormatException exc) {
                         Slog.e(LOG_TAG, "Invalid value: " + value + " for setting " + setting);
                     }
@@ -10265,6 +10350,11 @@
                 mInjector.binderRestoreCallingIdentity(id);
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_SECURE_SETTING)
+                .setAdmin(who)
+                .setStrings(setting, value)
+                .write();
     }
 
     @Override
@@ -10623,6 +10713,10 @@
             synchronized (getLockObject()) {
                 updateMaximumTimeToLockLocked(userId);
             }
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.SEPARATE_PROFILE_CHALLENGE_CHANGED)
+                    .setBoolean(isSeparateProfileChallengeEnabled(userId))
+                    .write();
         }
 
         @Override
@@ -10963,6 +11057,13 @@
                 saveSettingsLocked(userId);
             }
         }
+        final boolean isDelegate = (admin == null);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PERMISSION_POLICY)
+                .setAdmin(callerPackage)
+                .setInt(policy)
+                .setBoolean(isDelegate)
+                .write();
     }
 
     @Override
@@ -11014,7 +11115,6 @@
                                 PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0, user);
                     } break;
                 }
-                return true;
             } catch (SecurityException se) {
                 return false;
             } catch (NameNotFoundException e) {
@@ -11023,6 +11123,15 @@
                 mInjector.binderRestoreCallingIdentity(ident);
             }
         }
+        final boolean isDelegate = (admin == null);
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_PERMISSION_GRANT_STATE)
+                .setAdmin(callerPackage)
+                .setStrings(permission)
+                .setInt(grantState)
+                .setBoolean(isDelegate)
+                .write();
+        return true;
     }
 
     @Override
@@ -11848,6 +11957,11 @@
                 mSecurityLogMonitor.stop();
             }
         }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_SECURITY_LOGGING_ENABLED)
+                .setAdmin(admin)
+                .setBoolean(enabled)
+                .write();
     }
 
     @Override
@@ -11885,13 +11999,17 @@
         Preconditions.checkNotNull(admin);
         ensureDeviceOwnerAndAllUsersAffiliated(admin);
 
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.RETRIEVE_PRE_REBOOT_SECURITY_LOGS)
+                .setAdmin(admin)
+                .write();
+
         if (!mContext.getResources().getBoolean(R.bool.config_supportPreRebootSecurityLogs)
                 || !mInjector.securityLogGetLoggingEnabledProperty()) {
             return null;
         }
 
         recordSecurityLogRetrievalTime();
-
         ArrayList<SecurityEvent> output = new ArrayList<SecurityEvent>();
         try {
             SecurityLog.readPreviousEvents(output);
@@ -11918,6 +12036,10 @@
         recordSecurityLogRetrievalTime();
 
         List<SecurityEvent> logs = mSecurityLogMonitor.retrieveLogs();
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.RETRIEVE_SECURITY_LOGS)
+                .setAdmin(admin)
+                .write();
         return logs != null ? new ParceledListSlice<SecurityEvent>(logs) : null;
     }
 
@@ -13278,32 +13400,40 @@
     }
 
     @Override
-    public void setGlobalPrivateDns(@NonNull ComponentName who, int mode, String privateDnsHost) {
+    public int setGlobalPrivateDns(@NonNull ComponentName who, int mode, String privateDnsHost) {
         if (!mHasFeature) {
-            return;
+            return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
         }
 
         Preconditions.checkNotNull(who, "ComponentName is null");
         enforceDeviceOwner(who);
 
+        final int returnCode;
+
         switch (mode) {
             case PRIVATE_DNS_MODE_OPPORTUNISTIC:
                 if (!TextUtils.isEmpty(privateDnsHost)) {
-                    throw new IllegalArgumentException("A DNS host should not be provided when " +
-                            "setting opportunistic mode.");
+                    throw new IllegalArgumentException(
+                            "Host provided for opportunistic mode, but is not needed.");
                 }
                 putPrivateDnsSettings(ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC, null);
-                break;
+                return PRIVATE_DNS_SET_SUCCESS;
             case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
-                if (!NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
+                if (TextUtils.isEmpty(privateDnsHost)
+                        || !NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
                     throw new IllegalArgumentException(
-                            String.format("Provided hostname is not valid: %s", privateDnsHost));
+                            String.format("Provided hostname %s is not valid", privateDnsHost));
                 }
-                putPrivateDnsSettings(ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+
+                // Connectivity check will have been performed in the DevicePolicyManager before
+                // the call here.
+                putPrivateDnsSettings(
+                        ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
                         privateDnsHost);
-                break;
+                return PRIVATE_DNS_SET_SUCCESS;
             default:
-                throw new IllegalArgumentException(String.format("Unsupported mode: %d", mode));
+                throw new IllegalArgumentException(
+                        String.format("Provided mode, %d, is not a valid mode.", mode));
         }
     }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2baef6af..88f645d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,7 +36,6 @@
 import android.content.res.Resources.Theme;
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
 import android.database.sqlite.SQLiteGlobal;
-import android.hardware.display.ColorDisplayManager;
 import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Build;
@@ -1443,11 +1442,9 @@
             mSystemServiceManager.startService(TwilightService.class);
             traceEnd();
 
-            if (ColorDisplayManager.isNightDisplayAvailable(context)) {
-                traceBeginAndSlog("StartColorDisplay");
-                mSystemServiceManager.startService(ColorDisplayService.class);
-                traceEnd();
-            }
+            traceBeginAndSlog("StartColorDisplay");
+            mSystemServiceManager.startService(ColorDisplayService.class);
+            traceEnd();
 
             traceBeginAndSlog("StartJobScheduler");
             mSystemServiceManager.startService(JobSchedulerService.class);
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index ba4caf44..b01adc9 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -57,7 +57,6 @@
     private static final String TEST_TRANSPORT = "transport";
 
     @Mock private UserBackupManagerService mUserBackupManagerService;
-    @Mock private TransportManager mTransportManager;
     private BackupManagerService mBackupManagerService;
     private Context mContext;
 
@@ -72,10 +71,7 @@
                 new BackupManagerService(
                         application,
                         new Trampoline(application),
-                        BackupManagerServiceTestUtils.startBackupThread(null),
-                        new File(application.getCacheDir(), "base_state"),
-                        new File(application.getCacheDir(), "data"),
-                        mTransportManager);
+                        BackupManagerServiceTestUtils.startBackupThread(null));
         mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService);
     }
 
diff --git a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 9d43819..efbcb96 100644
--- a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -148,7 +148,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
                 .thenReturn("destinationString");
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         String destination = backupManagerService.getDestinationString(mTransportName);
 
@@ -164,7 +164,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
                 .thenThrow(TransportNotRegisteredException.class);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         String destination = backupManagerService.getDestinationString(mTransportName);
 
@@ -180,7 +180,7 @@
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
         when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
                 .thenThrow(TransportNotRegisteredException.class);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 SecurityException.class,
@@ -195,7 +195,7 @@
     public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         setUpCurrentTransport(mTransportManager, mTransport);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
 
@@ -211,7 +211,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
         ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
 
@@ -229,7 +229,7 @@
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
         setUpCurrentTransport(mTransportManager, mTransport);
         ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 SecurityException.class,
@@ -246,7 +246,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
         ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         String[] filtered =
                 backupManagerService.filterAppsEligibleForBackup(
@@ -264,7 +264,7 @@
     @Test
     public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         String[] filtered =
                 backupManagerService.filterAppsEligibleForBackup(
@@ -281,7 +281,7 @@
     public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
         setUpCurrentTransport(mTransportManager, mTransport);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 SecurityException.class,
@@ -319,7 +319,7 @@
     public void testSelectBackupTransport() throws Exception {
         setUpForSelectTransport();
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         String oldTransport =
                 backupManagerService.selectBackupTransport(mNewTransport.transportName);
@@ -338,7 +338,7 @@
     public void testSelectBackupTransport_withoutPermission() throws Exception {
         setUpForSelectTransport();
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 SecurityException.class,
@@ -356,7 +356,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
                 .thenReturn(BackupManager.SUCCESS);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
 
         backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
@@ -380,7 +380,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
                 .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
 
         backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
@@ -402,7 +402,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
                 .thenReturn(BackupManager.SUCCESS);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
 
         backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
@@ -421,7 +421,7 @@
     public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
         setUpForSelectTransport();
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         ComponentName newTransportComponent = mNewTransport.getTransportComponent();
 
         expectThrows(
@@ -445,7 +445,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         when(mTransportManager.getCurrentTransportComponent())
                 .thenReturn(mTransport.getTransportComponent());
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
 
@@ -460,7 +460,7 @@
     public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
 
@@ -476,7 +476,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         when(mTransportManager.getCurrentTransportComponent())
                 .thenThrow(TransportNotRegisteredException.class);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
 
@@ -490,7 +490,7 @@
     @Test
     public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
     }
@@ -525,7 +525,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         Intent configurationIntent = new Intent();
         Intent dataManagementIntent = new Intent();
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         backupManagerService.updateTransportAttributes(
                 mTransportUid,
@@ -556,7 +556,7 @@
             throws Exception {
         setUpForUpdateTransportAttributes();
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 SecurityException.class,
@@ -581,7 +581,7 @@
             throws Exception {
         setUpForUpdateTransportAttributes();
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 RuntimeException.class,
@@ -605,7 +605,7 @@
     public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
         setUpForUpdateTransportAttributes();
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 RuntimeException.class,
@@ -630,7 +630,7 @@
             throws Exception {
         setUpForUpdateTransportAttributes();
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 RuntimeException.class,
@@ -657,7 +657,7 @@
                     throws Exception {
         setUpForUpdateTransportAttributes();
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 RuntimeException.class,
@@ -696,7 +696,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         Intent configurationIntent = new Intent();
         Intent dataManagementIntent = new Intent();
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         backupManagerService.updateTransportAttributes(
                 mTransportUid,
@@ -727,7 +727,7 @@
             throws Exception {
         setUpForUpdateTransportAttributes();
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 SecurityException.class,
@@ -766,7 +766,7 @@
     @Test
     public void testRequestBackup_whenPermissionDenied() throws Exception {
         mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 SecurityException.class,
@@ -780,7 +780,7 @@
     @Test
     public void testRequestBackup_whenPackagesNull() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 IllegalArgumentException.class,
@@ -796,7 +796,7 @@
     @Test
     public void testRequestBackup_whenPackagesEmpty() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
 
         expectThrows(
                 IllegalArgumentException.class,
@@ -811,7 +811,7 @@
     @Test
     public void testRequestBackup_whenBackupDisabled() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(false);
 
         int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
@@ -828,7 +828,7 @@
     @Test
     public void testRequestBackup_whenNotProvisioned() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setProvisioned(false);
 
         int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
@@ -846,7 +846,7 @@
     public void testRequestBackup_whenTransportNotRegistered() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         setUpCurrentTransport(mTransportManager, mTransport.unregistered());
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
         backupManagerService.setProvisioned(true);
 
@@ -866,7 +866,7 @@
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         mShadowPackageManager.addPackage(PACKAGE_1);
         setUpCurrentTransport(mTransportManager, mTransport);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
         backupManagerService.setProvisioned(true);
         // Haven't set PACKAGE_1 as eligible
@@ -934,7 +934,7 @@
     @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
     public void testBackupNow_clearsCallingIdentityForJobScheduler() {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         setUpPowerManager(backupManagerService);
         ShadowBinder.setCallingUid(1);
 
@@ -952,7 +952,7 @@
     @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
     public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         setUpPowerManager(backupManagerService);
         ShadowBinder.setCallingUid(1);
 
@@ -963,54 +963,170 @@
     }
 
     private UserBackupManagerService createBackupManagerServiceForRequestBackup() {
-        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
         backupManagerService.setProvisioned(true);
         return backupManagerService;
     }
 
     /**
-     * Test verifying that {@link UserBackupManagerService#UserBackupManagerService(Context,
+     * Test verifying that {@link UserBackupManagerService#createAndInitializeService(Context,
      * Trampoline, HandlerThread, File, File, TransportManager)} posts a transport registration task
-     * to the backup handler thread.
+     * to the backup thread.
      */
     @Test
-    public void testConstructor_postRegisterTransports() {
+    public void testCreateAndInitializeService_postRegisterTransports() {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
 
-        createBackupManagerService();
-
-        mShadowBackupLooper.runToEndOfTasks();
-        verify(mTransportManager).registerTransports();
-    }
-
-    /**
-     * Test verifying that the {@link UserBackupManagerService#UserBackupManagerService(Context,
-     * Trampoline, HandlerThread, File, File, TransportManager)} does not directly register
-     * transports in its own thread.
-     */
-    @Test
-    public void testConstructor_doesNotRegisterTransportsSynchronously() {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-
-        createBackupManagerService();
-
-        // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
-        verify(mTransportManager, never()).registerTransports();
-    }
-
-    private UserBackupManagerService createBackupManagerService() {
-        return new UserBackupManagerService(
+        UserBackupManagerService.createAndInitializeService(
                 mContext,
                 new Trampoline(mContext),
                 mBackupThread,
                 mBaseStateDir,
                 mDataDir,
                 mTransportManager);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        verify(mTransportManager).registerTransports();
     }
 
-    private UserBackupManagerService createInitializedBackupManagerService() {
-        return BackupManagerServiceTestUtils.createInitializedUserBackupManagerService(
+    /**
+     * Test verifying that {@link UserBackupManagerService#createAndInitializeService(Context,
+     * Trampoline, HandlerThread, File, File, TransportManager)} does not directly register
+     * transports on the main thread.
+     */
+    @Test
+    public void testCreateAndInitializeService_doesNotRegisterTransportsSynchronously() {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+
+        UserBackupManagerService.createAndInitializeService(
+                mContext,
+                new Trampoline(mContext),
+                mBackupThread,
+                mBaseStateDir,
+                mDataDir,
+                mTransportManager);
+
+        // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
+        verify(mTransportManager, never()).registerTransports();
+    }
+
+    /**
+     * Test checking non-null argument on {@link
+     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+     * File, TransportManager)}.
+     */
+    @Test
+    public void testCreateAndInitializeService_withNullContext_throws() {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        UserBackupManagerService.createAndInitializeService(
+                                /* context */ null,
+                                new Trampoline(mContext),
+                                mBackupThread,
+                                mBaseStateDir,
+                                mDataDir,
+                                mTransportManager));
+    }
+
+    /**
+     * Test checking non-null argument on {@link
+     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+     * File, TransportManager)}.
+     */
+    @Test
+    public void testCreateAndInitializeService_withNullTrampoline_throws() {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        UserBackupManagerService.createAndInitializeService(
+                                mContext,
+                                /* trampoline */ null,
+                                mBackupThread,
+                                mBaseStateDir,
+                                mDataDir,
+                                mTransportManager));
+    }
+
+    /**
+     * Test checking non-null argument on {@link
+     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+     * File, TransportManager)}.
+     */
+    @Test
+    public void testCreateAndInitializeService_withNullBackupThread_throws() {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        UserBackupManagerService.createAndInitializeService(
+                                mContext,
+                                new Trampoline(mContext),
+                                /* backupThread */ null,
+                                mBaseStateDir,
+                                mDataDir,
+                                mTransportManager));
+    }
+
+    /**
+     * Test checking non-null argument on {@link
+     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+     * File, TransportManager)}.
+     */
+    @Test
+    public void testCreateAndInitializeService_withNullStateDir_throws() {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        UserBackupManagerService.createAndInitializeService(
+                                mContext,
+                                new Trampoline(mContext),
+                                mBackupThread,
+                                /* baseStateDir */ null,
+                                mDataDir,
+                                mTransportManager));
+    }
+
+    /**
+     * Test checking non-null argument on {@link
+     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+     * File, TransportManager)}.
+     */
+    @Test
+    public void testCreateAndInitializeService_withNullDataDir_throws() {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        UserBackupManagerService.createAndInitializeService(
+                                mContext,
+                                new Trampoline(mContext),
+                                mBackupThread,
+                                mBaseStateDir,
+                                /* dataDir */ null,
+                                mTransportManager));
+    }
+
+    /**
+     * Test checking non-null argument on {@link
+     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
+     * File, TransportManager)}.
+     */
+    @Test
+    public void testCreateAndInitializeService_withNullTransportManager_throws() {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        UserBackupManagerService.createAndInitializeService(
+                                mContext,
+                                new Trampoline(mContext),
+                                mBackupThread,
+                                mBaseStateDir,
+                                mDataDir,
+                                /* transportManager */ null));
+    }
+
+    private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
+        return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
                 mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
     }
 
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 1aa4999..099127c 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -25,12 +25,9 @@
 import static android.app.backup.ForwardingBackupAgent.forward;
 
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils
-        .createInitializedUserBackupManagerService;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils
-        .setUpBackupManagerServiceBasics;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils
-        .setUpBinderCallerAndApplicationAsSystem;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.setUpBackupManagerServiceBasics;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.setUpBinderCallerAndApplicationAsSystem;
 import static com.android.server.backup.testing.PackageData.PM_PACKAGE;
 import static com.android.server.backup.testing.PackageData.fullBackupPackage;
 import static com.android.server.backup.testing.PackageData.keyValuePackage;
@@ -226,9 +223,8 @@
         // Needed to be able to use a real BMS instead of a mock
         setUpBinderCallerAndApplicationAsSystem(mApplication);
         mBackupManagerService =
-                spy(
-                        createInitializedUserBackupManagerService(
-                                mContext, mBaseStateDir, mDataDir, mTransportManager));
+                spy(createUserBackupManagerServiceAndRunTasks(
+                        mContext, mBaseStateDir, mDataDir, mTransportManager));
         setUpBackupManagerServiceBasics(
                 mBackupManagerService,
                 mApplication,
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index bacc44e..06f6d21 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -52,23 +52,36 @@
 /** Test utils for {@link UserBackupManagerService} and friends. */
 public class BackupManagerServiceTestUtils {
     /**
-     * If the class-under-test is going to execute methods as the system, it's a good idea to also
-     * call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
+     * Creates an instance of {@link UserBackupManagerService} with a new backup thread and runs
+     * tasks that were posted to it during instantiation.
+     *
+     * <p>If the class-under-test is going to execute methods as the system, it's a good idea to
+     * also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
+     *
+     * @see #createUserBackupManagerServiceAndRunTasks(Context, HandlerThread, File, File,
+     *     TransportManager)
      */
-    public static UserBackupManagerService createInitializedUserBackupManagerService(
+    public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
             Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
-        return createInitializedUserBackupManagerService(
+        return createUserBackupManagerServiceAndRunTasks(
                 context, startBackupThread(null), baseStateDir, dataDir, transportManager);
     }
 
-    public static UserBackupManagerService createInitializedUserBackupManagerService(
+    /**
+     * Creates an instance of {@link UserBackupManagerService} with the supplied backup thread
+     * {@code backupThread} and runs tasks that were posted to it during instantiation.
+     *
+     * <p>If the class-under-test is going to execute methods as the system, it's a good idea to
+     * also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
+     */
+    public static UserBackupManagerService createUserBackupManagerServiceAndRunTasks(
             Context context,
             HandlerThread backupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
         UserBackupManagerService backupManagerService =
-                new UserBackupManagerService(
+                UserBackupManagerService.createAndInitializeService(
                         context,
                         new Trampoline(context),
                         backupThread,
diff --git a/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java b/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java
index beb5941..30c7336 100644
--- a/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java
+++ b/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java
@@ -7,7 +7,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.os.Looper;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 
@@ -44,7 +43,7 @@
         when(mMockNative.pauseGeofence(anyInt())).thenReturn(true);
         when(mMockNative.removeGeofence(anyInt())).thenReturn(true);
         when(mMockNative.resumeGeofence(anyInt(), anyInt())).thenReturn(true);
-        mTestProvider = new GnssGeofenceProvider(Looper.myLooper(), mMockNative);
+        mTestProvider = new GnssGeofenceProvider(mMockNative);
         mTestProvider.addCircularHardwareGeofence(GEOFENCE_ID, LATITUDE,
                 LONGITUDE, RADIUS, LAST_TRANSITION, MONITOR_TRANSITIONS,
                 NOTIFICATION_RESPONSIVENESS,
diff --git a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java b/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
index 59e9a15..aa2a96e 100644
--- a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
+++ b/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
@@ -16,6 +16,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
 
 /**
  * Unit tests for {@link GnssNavigationMessageProvider}.
@@ -33,8 +34,8 @@
         when(mMockNative.startNavigationMessageCollection()).thenReturn(true);
         when(mMockNative.stopNavigationMessageCollection()).thenReturn(true);
 
-        mTestProvider = new GnssNavigationMessageProvider(new Handler(Looper.myLooper()),
-                mMockNative) {
+        mTestProvider = new GnssNavigationMessageProvider(RuntimeEnvironment.application,
+                new Handler(Looper.myLooper()), mMockNative) {
             @Override
             public boolean isGpsEnabled() {
                 return true;
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index e804342..d49e78a 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -69,10 +69,10 @@
         "liblog",
         "liblzma",
         "libnativehelper",
-        "libnetdaidl",
         "libui",
         "libunwind",
         "libutils",
+        "netd_aidl_interface-cpp",
     ],
 
     dxflags: ["--multi-dex"],
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index 7c00299..fd010f1 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -23,6 +23,7 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -43,10 +44,14 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.test.FakeSettingsProvider;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -97,6 +102,7 @@
     private FileDescriptor mFileDescriptorStub = new FileDescriptor();
 
     private TrampolineTestable mTrampoline;
+    private MockContentResolver mContentResolver;
 
     @Before
     public void setUp() {
@@ -110,6 +116,10 @@
         when(mSuppressFileMock.getParentFile()).thenReturn(mSuppressFileParentMock);
 
         mTrampoline = new TrampolineTestable(mContextMock);
+
+        mContentResolver = new MockContentResolver();
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(mContextMock.getContentResolver()).thenReturn(mContentResolver);
     }
 
     @Test
@@ -118,6 +128,24 @@
     }
 
     @Test
+    public void startServiceForUser_whenMultiUserSettingDisabled_isIgnored() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+
+        mTrampoline.startServiceForUser(10);
+
+        verify(mBackupManagerServiceMock, never()).startServiceForUser(10);
+    }
+
+    @Test
+    public void startServiceForUser_whenMultiUserSettingEnabled_callsBackupManagerService() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+
+        mTrampoline.startServiceForUser(10);
+
+        verify(mBackupManagerServiceMock).startServiceForUser(10);
+    }
+
+    @Test
     public void initializeService_forUserSystem_successfullyInitialized() {
         mTrampoline.initializeService(UserHandle.USER_SYSTEM);
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 5dc6d83..c3a0dda 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4163,7 +4163,7 @@
         // test reset password with token
         when(getServices().lockPatternUtils.setLockCredentialWithToken(eq(password),
                 eq(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD),
-                eq(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED), eq(handle), eq(token),
+                eq(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC), eq(handle), eq(token),
                 eq(UserHandle.USER_SYSTEM)))
                 .thenReturn(true);
         assertTrue(dpm.resetPasswordWithToken(admin1, password, token, 0));
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index c4c2ad9..839b25f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -212,7 +212,29 @@
                                 mIApplicationThread,
                                 PACKAGE_ONE,
                                 ACTIVITY_COMPONENT,
-                                UserHandle.of(PRIMARY_USER)));
+                                UserHandle.of(PRIMARY_USER).getIdentifier(),
+                                true));
+
+        verify(mActivityTaskManagerInternal, never())
+                .startActivityAsUser(
+                        nullable(IApplicationThread.class),
+                        anyString(),
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        anyInt());
+    }
+
+    @Test
+    public void startAnyActivityAsUser_currentUser() {
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                mIApplicationThread,
+                                PACKAGE_ONE,
+                                ACTIVITY_COMPONENT,
+                                UserHandle.of(PRIMARY_USER).getIdentifier(),
+                                false));
 
         verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
@@ -234,7 +256,31 @@
                                 mIApplicationThread,
                                 PACKAGE_ONE,
                                 ACTIVITY_COMPONENT,
-                                UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+                                true));
+
+        verify(mActivityTaskManagerInternal, never())
+                .startActivityAsUser(
+                        nullable(IApplicationThread.class),
+                        anyString(),
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        anyInt());
+    }
+
+    @Test
+    public void startAnyActivityAsUser_profile_notInstalled() {
+        mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, false);
+
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                mIApplicationThread,
+                                PACKAGE_ONE,
+                                ACTIVITY_COMPONENT,
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+                                false));
 
         verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
@@ -254,7 +300,29 @@
                                 mIApplicationThread,
                                 PACKAGE_TWO,
                                 ACTIVITY_COMPONENT,
-                                UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+                                true));
+
+        verify(mActivityTaskManagerInternal, never())
+                .startActivityAsUser(
+                        nullable(IApplicationThread.class),
+                        anyString(),
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        anyInt());
+    }
+
+    @Test
+    public void startAnyActivityAsUser_profile_fakeCaller() {
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                mIApplicationThread,
+                                PACKAGE_TWO,
+                                ACTIVITY_COMPONENT,
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+                                false));
 
         verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
@@ -276,7 +344,31 @@
                                 mIApplicationThread,
                                 PACKAGE_ONE,
                                 ACTIVITY_COMPONENT,
-                                UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+                                true));
+
+        verify(mActivityTaskManagerInternal, never())
+                .startActivityAsUser(
+                        nullable(IApplicationThread.class),
+                        anyString(),
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        anyInt());
+    }
+
+    @Test
+    public void startAnyActivityAsUser_profile_notExported() {
+        mActivityInfo.exported = false;
+
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                mIApplicationThread,
+                                PACKAGE_ONE,
+                                ACTIVITY_COMPONENT,
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+                                false));
 
         verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
@@ -296,7 +388,29 @@
                                 mIApplicationThread,
                                 PACKAGE_ONE,
                                 new ComponentName(PACKAGE_TWO, "test"),
-                                UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+                                true));
+
+        verify(mActivityTaskManagerInternal, never())
+                .startActivityAsUser(
+                        nullable(IApplicationThread.class),
+                        anyString(),
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        anyInt());
+    }
+
+    @Test
+    public void startAnyActivityAsUser_profile_anotherPackage() {
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                mIApplicationThread,
+                                PACKAGE_ONE,
+                                new ComponentName(PACKAGE_TWO, "test"),
+                                UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
+                                false));
 
         verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
@@ -316,7 +430,29 @@
                                 mIApplicationThread,
                                 PACKAGE_ONE,
                                 ACTIVITY_COMPONENT,
-                                UserHandle.of(SECONDARY_USER)));
+                                UserHandle.of(SECONDARY_USER).getIdentifier(),
+                                true));
+
+        verify(mActivityTaskManagerInternal, never())
+                .startActivityAsUser(
+                        nullable(IApplicationThread.class),
+                        anyString(),
+                        any(Intent.class),
+                        nullable(Bundle.class),
+                        anyInt());
+    }
+
+    @Test
+    public void startAnyActivityAsUser_secondaryUser() {
+        assertThrows(
+                SecurityException.class,
+                () ->
+                        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                                mIApplicationThread,
+                                PACKAGE_ONE,
+                                ACTIVITY_COMPONENT,
+                                UserHandle.of(SECONDARY_USER).getIdentifier(),
+                                false));
 
         verify(mActivityTaskManagerInternal, never())
                 .startActivityAsUser(
@@ -335,7 +471,8 @@
                 mIApplicationThread,
                 PACKAGE_ONE,
                 ACTIVITY_COMPONENT,
-                UserHandle.of(PRIMARY_USER));
+                UserHandle.of(PRIMARY_USER).getIdentifier(),
+                true);
 
         verify(mActivityTaskManagerInternal)
                 .startActivityAsUser(
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7bcb571..823b7a5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -16,13 +16,13 @@
 
 package com.android.server.pm;
 
+import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
-import android.app.ActivityManager;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -162,6 +162,29 @@
     }
 
     @MediumTest
+    public void testRemoveUserByHandle() {
+        UserInfo userInfo = createUser("Guest 1", UserInfo.FLAG_GUEST);
+        final UserHandle user = userInfo.getUserHandle();
+        synchronized (mUserRemoveLock) {
+            mUserManager.removeUser(user);
+            long time = System.currentTimeMillis();
+            while (mUserManager.getUserInfo(user.getIdentifier()) != null) {
+                try {
+                    mUserRemoveLock.wait(REMOVE_CHECK_INTERVAL_MILLIS);
+                } catch (InterruptedException ie) {
+                    Thread.currentThread().interrupt();
+                    return;
+                }
+                if (System.currentTimeMillis() - time > REMOVE_TIMEOUT_MILLIS) {
+                    fail("Timeout waiting for removeUser. userId = " + user.getIdentifier());
+                }
+            }
+        }
+
+        assertFalse(findUser(userInfo.id));
+    }
+
+    @MediumTest
     public void testAddGuest() throws Exception {
         UserInfo userInfo1 = createUser("Guest 1", UserInfo.FLAG_GUEST);
         UserInfo userInfo2 = createUser("Guest 2", UserInfo.FLAG_GUEST);
@@ -504,9 +527,21 @@
         UserInfo user = createUser("User", 0);
         assertNotNull(user);
         // Switch to the user just created.
-        switchUser(user.id);
+        switchUser(user.id, null, true);
         // Switch back to the starting user.
-        switchUser(startUser);
+        switchUser(startUser, null, true);
+    }
+
+    @LargeTest
+    public void testSwitchUserByHandle() {
+        ActivityManager am = getContext().getSystemService(ActivityManager.class);
+        final int startUser = am.getCurrentUser();
+        UserInfo user = createUser("User", 0);
+        assertNotNull(user);
+        // Switch to the user just created.
+        switchUser(-1, user.getUserHandle(), false);
+        // Switch back to the starting user.
+        switchUser(-1, UserHandle.of(startUser), false);
     }
 
     @MediumTest
@@ -544,10 +579,20 @@
         }
     }
 
-    private void switchUser(int userId) {
+    /**
+     * @param userId value will be used to call switchUser(int) only if ignoreHandle is false.
+     * @param user value will be used to call switchUser(UserHandle) only if ignoreHandle is true.
+     * @param ignoreHandle if true, switchUser(int) will be called with the provided userId,
+     *                     else, switchUser(UserHandle) will be called with the provided user.
+     */
+    private void switchUser(int userId, UserHandle user, boolean ignoreHandle) {
         synchronized (mUserSwitchLock) {
             ActivityManager am = getContext().getSystemService(ActivityManager.class);
-            am.switchUser(userId);
+            if (ignoreHandle) {
+                am.switchUser(userId);
+            } else {
+                am.switchUser(user);
+            }
             long time = System.currentTimeMillis();
             try {
                 mUserSwitchLock.wait(SWITCH_USER_TIMEOUT_MILLIS);
@@ -556,7 +601,8 @@
                 return;
             }
             if (System.currentTimeMillis() - time > SWITCH_USER_TIMEOUT_MILLIS) {
-                fail("Timeout waiting for the user switch to u" + userId);
+                fail("Timeout waiting for the user switch to u"
+                        + (ignoreHandle ? userId : user.getIdentifier()));
             }
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index dad7b93..fd07cb0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -18,10 +18,14 @@
 
 import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
 import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.DynamicCodeFile;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -41,6 +45,10 @@
 
 import com.android.server.pm.Installer;
 
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.PathClassLoader;
+import dalvik.system.VMRuntime;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -50,10 +58,6 @@
 import org.mockito.junit.MockitoRule;
 import org.mockito.quality.Strictness;
 
-import dalvik.system.DelegateLastClassLoader;
-import dalvik.system.PathClassLoader;
-import dalvik.system.VMRuntime;
-
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -129,6 +133,9 @@
 
         // Package is not used by others, so we should get nothing back.
         assertNoUseInfo(mFooUser0);
+
+        // A package loading its own code is not stored as DCL.
+        assertNoDclInfo(mFooUser0);
     }
 
     @Test
@@ -140,6 +147,8 @@
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
         assertIsUsedByOtherApps(mBarUser0, pui, true);
         assertTrue(pui.getDexUseInfoMap().isEmpty());
+
+        assertHasDclInfo(mBarUser0, mFooUser0, mBarUser0.getBaseAndSplitDexPaths());
     }
 
     @Test
@@ -152,6 +161,8 @@
         assertIsUsedByOtherApps(mFooUser0, pui, false);
         assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
+
+        assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
     }
 
     @Test
@@ -164,6 +175,8 @@
         assertIsUsedByOtherApps(mBarUser0, pui, false);
         assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
+
+        assertHasDclInfo(mBarUser0, mFooUser0, barSecondaries);
     }
 
     @Test
@@ -200,9 +213,10 @@
     }
 
     @Test
-    public void testPackageUseInfoNotFound() {
+    public void testNoNotify() {
         // Assert we don't get back data we did not previously record.
         assertNoUseInfo(mFooUser0);
+        assertNoDclInfo(mFooUser0);
     }
 
     @Test
@@ -210,6 +224,7 @@
         // Notifying with an invalid ISA should be ignored.
         notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
         assertNoUseInfo(mInvalidIsa);
+        assertNoDclInfo(mInvalidIsa);
     }
 
     @Test
@@ -218,6 +233,7 @@
         // register in DexManager#load should be ignored.
         notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
         assertNoUseInfo(mDoesNotExist);
+        assertNoDclInfo(mDoesNotExist);
     }
 
     @Test
@@ -226,6 +242,8 @@
         // Request should be ignored.
         notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
         assertNoUseInfo(mBarUser1);
+
+        assertNoDclInfo(mBarUser1);
     }
 
     @Test
@@ -235,6 +253,10 @@
         // still check that nothing goes unexpected in DexManager.
         notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
         assertNoUseInfo(mBarUser1);
+        assertNoUseInfo(mFooUser0);
+
+        assertNoDclInfo(mBarUser1);
+        assertNoDclInfo(mFooUser0);
     }
 
     @Test
@@ -247,6 +269,7 @@
         // is trying to load something from it we should not find it.
         notifyDexLoad(mFooUser0, newSecondaries, mUser0);
         assertNoUseInfo(newPackage);
+        assertNoDclInfo(newPackage);
 
         // Notify about newPackage install and let mFoo load its dexes.
         mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
@@ -257,6 +280,7 @@
         assertIsUsedByOtherApps(newPackage, pui, false);
         assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
+        assertHasDclInfo(newPackage, mFooUser0, newSecondaries);
     }
 
     @Test
@@ -273,6 +297,7 @@
         assertIsUsedByOtherApps(newPackage, pui, false);
         assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
+        assertHasDclInfo(newPackage, newPackage, newSecondaries);
     }
 
     @Test
@@ -305,6 +330,7 @@
         // We shouldn't find yet the new split as we didn't notify the package update.
         notifyDexLoad(mFooUser0, newSplits, mUser0);
         assertNoUseInfo(mBarUser0);
+        assertNoDclInfo(mBarUser0);
 
         // Notify that bar is updated. splitSourceDirs will contain the updated path.
         mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
@@ -314,8 +340,8 @@
         // Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
         notifyDexLoad(mFooUser0, newSplits, mUser0);
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
         assertIsUsedByOtherApps(newSplits, pui, true);
+        assertHasDclInfo(mBarUser0, mFooUser0, newSplits);
     }
 
     @Test
@@ -326,11 +352,15 @@
 
         mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), mUser0);
 
-        // Bar should not be around since it was removed for all users.
+        // Data for user 1 should still be present
         PackageUseInfo pui = getPackageUseInfo(mBarUser1);
-        assertNotNull(pui);
         assertSecondaryUse(mBarUser1, pui, mBarUser1.getSecondaryDexPaths(),
                 /*isUsedByOtherApps*/false, mUser1);
+        assertHasDclInfo(mBarUser1, mBarUser1, mBarUser1.getSecondaryDexPaths());
+
+        // But not user 0
+        assertNoUseInfo(mBarUser0, mUser0);
+        assertNoDclInfo(mBarUser0, mUser0);
     }
 
     @Test
@@ -349,6 +379,8 @@
         PackageUseInfo pui = getPackageUseInfo(mFooUser0);
         assertIsUsedByOtherApps(mFooUser0, pui, true);
         assertTrue(pui.getDexUseInfoMap().isEmpty());
+
+        assertNoDclInfo(mFooUser0);
     }
 
     @Test
@@ -362,6 +394,7 @@
         // Foo should not be around since all its secondary dex info were deleted
         // and it is not used by other apps.
         assertNoUseInfo(mFooUser0);
+        assertNoDclInfo(mFooUser0);
     }
 
     @Test
@@ -374,6 +407,7 @@
 
         // Bar should not be around since it was removed for all users.
         assertNoUseInfo(mBarUser0);
+        assertNoDclInfo(mBarUser0);
     }
 
     @Test
@@ -383,6 +417,7 @@
         notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
         // The dex file should not be recognized as a package.
         assertFalse(mDexManager.hasInfoOnPackage(frameworkDex));
+        assertNull(mDexManager.getPackageDynamicCodeInfo(frameworkDex));
     }
 
     @Test
@@ -395,6 +430,8 @@
         assertIsUsedByOtherApps(mFooUser0, pui, false);
         assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
+
+        assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
     }
 
     @Test
@@ -402,7 +439,12 @@
         List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
         notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
 
+        // We don't record the dex usage
         assertNoUseInfo(mBarUser0UnsupportedClassLoader);
+
+        // But we do record this as an intance of dynamic code loading
+        assertHasDclInfo(
+                mBarUser0UnsupportedClassLoader, mBarUser0UnsupportedClassLoader, secondaries);
     }
 
     @Test
@@ -414,6 +456,8 @@
         notifyDexLoad(mBarUser0, classLoaders, classPaths, mUser0);
 
         assertNoUseInfo(mBarUser0);
+
+        assertHasDclInfo(mBarUser0, mBarUser0, mBarUser0.getSecondaryDexPaths());
     }
 
     @Test
@@ -421,6 +465,7 @@
         notifyDexLoad(mBarUser0, null, mUser0);
 
         assertNoUseInfo(mBarUser0);
+        assertNoDclInfo(mBarUser0);
     }
 
     @Test
@@ -455,12 +500,14 @@
         notifyDexLoad(mBarUser0, secondaries, mUser0);
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
         assertSecondaryUse(mBarUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
+        assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
 
         // Record bar secondaries again with an unsupported class loader. This should not change the
         // context.
         notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
         pui = getPackageUseInfo(mBarUser0);
         assertSecondaryUse(mBarUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
+        assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
     }
 
     @Test
@@ -533,13 +580,53 @@
 
     private PackageUseInfo getPackageUseInfo(TestData testData) {
         assertTrue(mDexManager.hasInfoOnPackage(testData.getPackageName()));
-        return mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
+        PackageUseInfo pui = mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
+        assertNotNull(pui);
+        return pui;
     }
 
     private void assertNoUseInfo(TestData testData) {
         assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
     }
 
+    private void assertNoUseInfo(TestData testData, int userId) {
+        if (!mDexManager.hasInfoOnPackage(testData.getPackageName())) {
+            return;
+        }
+        PackageUseInfo pui = getPackageUseInfo(testData);
+        for (DexUseInfo dexUseInfo : pui.getDexUseInfoMap().values()) {
+            assertNotEquals(userId, dexUseInfo.getOwnerUserId());
+        }
+    }
+
+    private void assertNoDclInfo(TestData testData) {
+        assertNull(mDexManager.getPackageDynamicCodeInfo(testData.getPackageName()));
+    }
+
+    private void assertNoDclInfo(TestData testData, int userId) {
+        PackageDynamicCode info = mDexManager.getPackageDynamicCodeInfo(testData.getPackageName());
+        if (info == null) {
+            return;
+        }
+
+        for (DynamicCodeFile fileInfo : info.mFileUsageMap.values()) {
+            assertNotEquals(userId, fileInfo.mUserId);
+        }
+    }
+
+    private void assertHasDclInfo(TestData owner, TestData loader, List<String> paths) {
+        PackageDynamicCode info = mDexManager.getPackageDynamicCodeInfo(owner.getPackageName());
+        assertNotNull("No DCL data for owner " + owner.getPackageName(), info);
+        for (String path : paths) {
+            DynamicCodeFile fileInfo = info.mFileUsageMap.get(path);
+            assertNotNull("No DCL data for path " + path, fileInfo);
+            assertEquals(PackageDynamicCodeLoading.FILE_TYPE_DEX, fileInfo.mFileType);
+            assertEquals(owner.mUserId, fileInfo.mUserId);
+            assertTrue("No DCL data for loader " + loader.getPackageName(),
+                    fileInfo.mLoadingPackages.contains(loader.getPackageName()));
+        }
+    }
+
     private static PackageInfo getMockPackageInfo(String packageName, int userId) {
         PackageInfo pi = new PackageInfo();
         pi.packageName = packageName;
@@ -563,11 +650,13 @@
         private final PackageInfo mPackageInfo;
         private final String mLoaderIsa;
         private final String mClassLoader;
+        private final int mUserId;
 
         private TestData(String packageName, String loaderIsa, int userId, String classLoader) {
             mPackageInfo = getMockPackageInfo(packageName, userId);
             mLoaderIsa = loaderIsa;
             mClassLoader = classLoader;
+            mUserId = userId;
         }
 
         private TestData(String packageName, String loaderIsa, int userId) {
@@ -603,9 +692,7 @@
         List<String> getBaseAndSplitDexPaths() {
             List<String> paths = new ArrayList<>();
             paths.add(mPackageInfo.applicationInfo.sourceDir);
-            for (String split : mPackageInfo.applicationInfo.splitSourceDirs) {
-                paths.add(split);
-            }
+            Collections.addAll(paths, mPackageInfo.applicationInfo.splitSourceDirs);
             return paths;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
new file mode 100644
index 0000000..eb4cc4e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
@@ -0,0 +1,560 @@
+/*
+ * 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 com.android.server.pm.dex;
+
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.escape;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.unescape;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.pm.dex.PackageDynamicCodeLoading.DynamicCodeFile;
+import com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PackageDynamicCodeLoadingTests {
+
+    // Deliberately making a copy here since we're testing identity and
+    // string literals have a tendency to be identical.
+    private static final String TRIVIAL_STRING = new String("hello/world");
+    private static final Entry[] NO_ENTRIES = {};
+    private static final String[] NO_PACKAGES = {};
+
+    @Test
+    public void testRecord() {
+        Entry[] entries = {
+                new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1"),
+                new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2"),
+                new Entry("owning.package1", "/path/file2", 'D', 5, "loading.package1"),
+                new Entry("owning.package2", "/path/file3", 'D', 0, "loading.package2"),
+        };
+
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+        assertHasEntries(info, entries);
+    }
+
+    @Test
+    public void testRecord_returnsHasChanged() {
+        Entry owner1Path1Loader1 =
+                new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1");
+        Entry owner1Path1Loader2 =
+                new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2");
+        Entry owner1Path2Loader1 =
+                new Entry("owning.package1", "/path/file2", 'D', 10, "loading.package1");
+        Entry owner2Path1Loader1 =
+                new Entry("owning.package2", "/path/file1", 'D', 10, "loading.package2");
+
+        PackageDynamicCodeLoading info = new PackageDynamicCodeLoading();
+
+        assertTrue(record(info, owner1Path1Loader1));
+        assertFalse(record(info, owner1Path1Loader1));
+
+        assertTrue(record(info, owner1Path1Loader2));
+        assertFalse(record(info, owner1Path1Loader2));
+
+        assertTrue(record(info, owner1Path2Loader1));
+        assertFalse(record(info, owner1Path2Loader1));
+
+        assertTrue(record(info, owner2Path1Loader1));
+        assertFalse(record(info, owner2Path1Loader1));
+
+        assertHasEntries(info,
+                owner1Path1Loader1, owner1Path1Loader2, owner1Path2Loader1, owner2Path1Loader1);
+    }
+
+    @Test
+    public void testRecord_changeUserForFile_throws() {
+        Entry entry1 = new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1");
+        Entry entry2 = new Entry("owning.package1", "/path/file1", 'D', 20, "loading.package1");
+
+        PackageDynamicCodeLoading info = makePackageDcl(entry1);
+
+        assertThrows(() -> record(info, entry2));
+        assertHasEntries(info, entry1);
+    }
+
+    @Test
+    public void testRecord_badFileType_throws() {
+        Entry entry = new Entry("owning.package", "/path/file", 'Z', 10, "loading.package");
+        PackageDynamicCodeLoading info = new PackageDynamicCodeLoading();
+
+        assertThrows(() -> record(info, entry));
+    }
+
+    @Test
+    public void testClear() {
+        Entry[] entries = {
+                new Entry("owner1", "file1", 'D', 10, "loader1"),
+                new Entry("owner2", "file2", 'D', 20, "loader2"),
+        };
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+        info.clear();
+        assertHasEntries(info, NO_ENTRIES);
+    }
+
+    @Test
+    public void testRemovePackage_present() {
+        Entry other = new Entry("other", "file", 'D', 0, "loader");
+        Entry[] entries = {
+                new Entry("owner", "file1", 'D', 10, "loader1"),
+                new Entry("owner", "file2", 'D', 20, "loader2"),
+                other
+        };
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+        assertTrue(info.removePackage("owner"));
+        assertHasEntries(info, other);
+        assertHasPackages(info, "other");
+    }
+
+    @Test
+    public void testRemovePackage_notPresent() {
+        Entry[] entries = { new Entry("owner", "file", 'D', 0, "loader") };
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+        assertFalse(info.removePackage("other"));
+        assertHasEntries(info, entries);
+    }
+
+    @Test
+    public void testRemoveUserPackage_notPresent() {
+        Entry[] entries = { new Entry("owner", "file", 'D', 0, "loader") };
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+        assertFalse(info.removeUserPackage("other", 0));
+        assertHasEntries(info, entries);
+    }
+
+    @Test
+    public void testRemoveUserPackage_presentWithNoOtherUsers() {
+        Entry other = new Entry("other", "file", 'D', 0, "loader");
+        Entry[] entries = {
+                new Entry("owner", "file1", 'D', 0, "loader1"),
+                new Entry("owner", "file2", 'D', 0, "loader2"),
+                other
+        };
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+        assertTrue(info.removeUserPackage("owner", 0));
+        assertHasEntries(info, other);
+        assertHasPackages(info, "other");
+    }
+
+    @Test
+    public void testRemoveUserPackage_presentWithUsers() {
+        Entry other = new Entry("owner", "file", 'D', 1, "loader");
+        Entry[] entries = {
+                new Entry("owner", "file1", 'D', 0, "loader1"),
+                new Entry("owner", "file2", 'D', 0, "loader2"),
+                other
+        };
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+        assertTrue(info.removeUserPackage("owner", 0));
+        assertHasEntries(info, other);
+    }
+
+    @Test
+    public void testRemoveFile_present() {
+        Entry[] entries = {
+                new Entry("package1", "file1", 'D', 0, "loader1"),
+                new Entry("package1", "file2", 'D', 0, "loader1"),
+                new Entry("package2", "file1", 'D', 0, "loader2"),
+        };
+        Entry[] expectedSurvivors = {
+                new Entry("package1", "file2", 'D', 0, "loader1"),
+                new Entry("package2", "file1", 'D', 0, "loader2"),
+        };
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+        assertTrue(info.removeFile("package1", "file1", 0));
+        assertHasEntries(info, expectedSurvivors);
+    }
+
+    @Test
+    public void testRemoveFile_onlyEntry() {
+        Entry[] entries = {
+                new Entry("package1", "file1", 'D', 0, "loader1"),
+        };
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+        assertTrue(info.removeFile("package1", "file1", 0));
+        assertHasEntries(info, NO_ENTRIES);
+        assertHasPackages(info, NO_PACKAGES);
+    }
+
+    @Test
+    public void testRemoveFile_notPresent() {
+        Entry[] entries = {
+                new Entry("package1", "file2", 'D', 0, "loader1"),
+        };
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+        assertFalse(info.removeFile("package1", "file1", 0));
+        assertHasEntries(info, entries);
+    }
+
+    @Test
+    public void testRemoveFile_wrongUser() {
+        Entry[] entries = {
+                new Entry("package1", "file1", 'D', 10, "loader1"),
+        };
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+
+        assertFalse(info.removeFile("package1", "file1", 0));
+        assertHasEntries(info, entries);
+    }
+
+    @Test
+    public void testSyncData() {
+        Map<String, Set<Integer>> packageToUsersMap = ImmutableMap.of(
+                "package1", ImmutableSet.of(10, 20),
+                "package2", ImmutableSet.of(20));
+
+        Entry[] entries = {
+                new Entry("deleted.packaged", "file1", 'D', 10, "package1"),
+                new Entry("package1", "file2", 'D', 20, "package2"),
+                new Entry("package1", "file3", 'D', 10, "package2"),
+                new Entry("package1", "file3", 'D', 10, "deleted.package"),
+                new Entry("package2", "file4", 'D', 20, "deleted.package"),
+        };
+
+        Entry[] expectedSurvivors = {
+                new Entry("package1", "file2", 'D', 20, "package2"),
+        };
+
+        PackageDynamicCodeLoading info = makePackageDcl(entries);
+        info.syncData(packageToUsersMap);
+        assertHasEntries(info, expectedSurvivors);
+        assertHasPackages(info, "package1");
+    }
+
+    @Test
+    public void testRead_onlyHeader_emptyResult() throws Exception {
+        assertHasEntries(read("DCL1"), NO_ENTRIES);
+    }
+
+    @Test
+    public void testRead_noHeader_throws() {
+        assertThrows(IOException.class, () -> read(""));
+    }
+
+    @Test
+    public void testRead_wrongHeader_throws() {
+        assertThrows(IOException.class, () -> read("DCL2"));
+    }
+
+    @Test
+    public void testRead_oneEntry() throws Exception {
+        String inputText = ""
+                + "DCL1\n"
+                + "P:owning.package\n"
+                + "D:10:loading.package:/path/fi\\\\le\n";
+        assertHasEntries(read(inputText),
+                new Entry("owning.package", "/path/fi\\le", 'D', 10, "loading.package"));
+    }
+
+    @Test
+    public void testRead_emptyPackage() throws Exception {
+        String inputText = ""
+                + "DCL1\n"
+                + "P:owning.package\n";
+        PackageDynamicCodeLoading info = read(inputText);
+        assertHasEntries(info, NO_ENTRIES);
+        assertHasPackages(info, NO_PACKAGES);
+    }
+
+    @Test
+    public void testRead_complex() throws Exception {
+        String inputText = ""
+                + "DCL1\n"
+                + "P:owning.package1\n"
+                + "D:10:loading.package1,loading.package2:/path/file1\n"
+                + "D:5:loading.package1:/path/file2\n"
+                + "P:owning.package2\n"
+                + "D:0:loading.package2:/path/file3";
+        assertHasEntries(read(inputText),
+                new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1"),
+                new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2"),
+                new Entry("owning.package1", "/path/file2", 'D', 5, "loading.package1"),
+                new Entry("owning.package2", "/path/file3", 'D', 0, "loading.package2"));
+    }
+
+    @Test
+    public void testRead_missingPackageLine_throws() {
+        String inputText = ""
+                + "DCL1\n"
+                + "D:10:loading.package:/path/file\n";
+        assertThrows(IOException.class, () -> read(inputText));
+    }
+
+    @Test
+    public void testRead_malformedFile_throws() {
+        String inputText = ""
+                + "DCL1\n"
+                + "P:owning.package\n"
+                + "Hello world!\n";
+        assertThrows(IOException.class, () -> read(inputText));
+    }
+
+    @Test
+    public void testRead_badFileType_throws() {
+        String inputText = ""
+                + "DCL1\n"
+                + "P:owning.package\n"
+                + "X:10:loading.package:/path/file\n";
+        assertThrows(IOException.class, () -> read(inputText));
+    }
+
+    @Test
+    public void testRead_badUserId_throws() {
+        String inputText = ""
+                + "DCL1\n"
+                + "P:owning.package\n"
+                + "D:999999999999999999:loading.package:/path/file\n";
+        assertThrows(IOException.class, () -> read(inputText));
+    }
+
+    @Test
+    public void testRead_missingPackages_throws() {
+        String inputText = ""
+                + "DCL1\n"
+                + "P:owning.package\n"
+                + "D:1:,:/path/file\n";
+        assertThrows(IOException.class, () -> read(inputText));
+    }
+
+    @Test
+    public void testWrite_empty() throws Exception {
+        assertEquals("DCL1\n", write(NO_ENTRIES));
+    }
+
+    @Test
+    public void testWrite_oneEntry() throws Exception {
+        String expected = ""
+                + "DCL1\n"
+                + "P:owning.package\n"
+                + "D:10:loading.package:/path/fi\\\\le\n";
+        String actual = write(
+                new Entry("owning.package", "/path/fi\\le", 'D', 10, "loading.package"));
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testWrite_complex_roundTrips() throws Exception {
+        // There isn't a canonical order for the output in the presence of multiple items.
+        // So we just check that if we read back what we write we end up where we started.
+        Entry[] entries = {
+            new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1"),
+            new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package2"),
+            new Entry("owning.package1", "/path/file2", 'D', 5, "loading.package1"),
+            new Entry("owning.package2", "/path/fi\\le3", 'D', 0, "loading.package2")
+        };
+        assertHasEntries(read(write(entries)), entries);
+    }
+
+    @Test
+    public void testWrite_failure_throws() {
+        PackageDynamicCodeLoading info = makePackageDcl(
+                new Entry("owning.package", "/path/fi\\le", 'D', 10, "loading.package"));
+        assertThrows(IOException.class, () -> info.write(new ThrowingOutputStream()));
+    }
+
+    @Test
+    public void testEscape_trivialCase_returnsSameString() {
+        assertSame(TRIVIAL_STRING, escape(TRIVIAL_STRING));
+    }
+
+    @Test
+    public void testEscape() {
+        String input = "backslash\\newline\nreturn\r";
+        String expected = "backslash\\\\newline\\nreturn\\r";
+        assertEquals(expected, escape(input));
+    }
+
+    @Test
+    public void testUnescape_trivialCase_returnsSameString() throws Exception {
+        assertSame(TRIVIAL_STRING, unescape(TRIVIAL_STRING));
+    }
+
+    @Test
+    public void testUnescape() throws Exception {
+        String input = "backslash\\\\newline\\nreturn\\r";
+        String expected = "backslash\\newline\nreturn\r";
+        assertEquals(expected, unescape(input));
+    }
+
+    @Test
+    public void testUnescape_badEscape_throws() {
+        assertThrows(IOException.class, () -> unescape("this is \\bad"));
+    }
+
+    @Test
+    public void testUnescape_trailingBackslash_throws() {
+        assertThrows(IOException.class, () -> unescape("don't do this\\"));
+    }
+
+    @Test
+    public void testEscapeUnescape_roundTrips() throws Exception {
+        assertRoundTripsWithEscape("foo");
+        assertRoundTripsWithEscape("\\\\\n\n\r");
+        assertRoundTripsWithEscape("\\a\\b\\");
+        assertRoundTripsWithEscape("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\");
+    }
+
+    private void assertRoundTripsWithEscape(String original) throws Exception {
+        assertEquals(original, unescape(escape(original)));
+    }
+
+    private boolean record(PackageDynamicCodeLoading info, Entry entry) {
+        return info.record(entry.mOwningPackage, entry.mPath, entry.mFileType, entry.mUserId,
+                entry.mLoadingPackage);
+    }
+
+    private PackageDynamicCodeLoading read(String inputText) throws Exception {
+        ByteArrayInputStream inputStream =
+                new ByteArrayInputStream(inputText.getBytes(UTF_8));
+
+        PackageDynamicCodeLoading info = new PackageDynamicCodeLoading();
+        info.read(inputStream);
+
+        return info;
+    }
+
+    private String write(Entry... entries) throws Exception {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        makePackageDcl(entries).write(output);
+        return new String(output.toByteArray(), UTF_8);
+    }
+
+    private Set<Entry> entriesFrom(PackageDynamicCodeLoading info) {
+        ImmutableSet.Builder<Entry> entries = ImmutableSet.builder();
+        for (String owningPackage : info.getAllPackagesWithDynamicCodeLoading()) {
+            PackageDynamicCode packageInfo = info.getPackageDynamicCodeInfo(owningPackage);
+            Map<String, DynamicCodeFile> usageMap = packageInfo.mFileUsageMap;
+            for (Map.Entry<String, DynamicCodeFile> fileEntry : usageMap.entrySet()) {
+                String path = fileEntry.getKey();
+                DynamicCodeFile fileInfo = fileEntry.getValue();
+                for (String loadingPackage : fileInfo.mLoadingPackages) {
+                    entries.add(new Entry(owningPackage, path, fileInfo.mFileType, fileInfo.mUserId,
+                            loadingPackage));
+                }
+            }
+        }
+
+        return entries.build();
+    }
+
+    private PackageDynamicCodeLoading makePackageDcl(Entry... entries) {
+        PackageDynamicCodeLoading result = new PackageDynamicCodeLoading();
+        for (Entry entry : entries) {
+            result.record(entry.mOwningPackage, entry.mPath, entry.mFileType, entry.mUserId,
+                    entry.mLoadingPackage);
+        }
+        return result;
+
+    }
+
+    private void assertHasEntries(PackageDynamicCodeLoading info, Entry... expected) {
+        assertEquals(ImmutableSet.copyOf(expected), entriesFrom(info));
+    }
+
+    private void assertHasPackages(PackageDynamicCodeLoading info, String... expected) {
+        assertEquals(ImmutableSet.copyOf(expected), info.getAllPackagesWithDynamicCodeLoading());
+    }
+
+    /**
+     * Immutable representation of one entry in the dynamic code loading data (one package
+     * owning one file loaded by one package). Has well-behaved equality, hash and toString
+     * for ease of use in assertions.
+     */
+    private static class Entry {
+        private final String mOwningPackage;
+        private final String mPath;
+        private final char mFileType;
+        private final int mUserId;
+        private final String mLoadingPackage;
+
+        private Entry(String owningPackage, String path, char fileType, int userId,
+                String loadingPackage) {
+            mOwningPackage = owningPackage;
+            mPath = path;
+            mFileType = fileType;
+            mUserId = userId;
+            mLoadingPackage = loadingPackage;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Entry that = (Entry) o;
+            return mFileType == that.mFileType
+                    && mUserId == that.mUserId
+                    && Objects.equals(mOwningPackage, that.mOwningPackage)
+                    && Objects.equals(mPath, that.mPath)
+                    && Objects.equals(mLoadingPackage, that.mLoadingPackage);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mOwningPackage, mPath, mFileType, mUserId, mLoadingPackage);
+        }
+
+        @Override
+        public String toString() {
+            return "Entry("
+                    + "\"" + mOwningPackage + '"'
+                    + ", \"" + mPath + '"'
+                    + ", '" + mFileType + '\''
+                    + ", " + mUserId
+                    + ", \"" + mLoadingPackage + '\"'
+                    + ')';
+        }
+    }
+
+    private static class ThrowingOutputStream extends OutputStream {
+        @Override
+        public void write(int b) throws IOException {
+            throw new IOException("Intentional failure");
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index c1963da..94d293e 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -281,6 +281,10 @@
         mFakeHal.mCallback.onValues(newSkin);
         verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
                 .times(1)).shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
+        Temperature newBattery = new Temperature(60, Temperature.TYPE_BATTERY, "batt", status);
+        mFakeHal.mCallback.onValues(newBattery);
+        verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java b/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java
new file mode 100644
index 0000000..a9d4519
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/signedconfig/SignedConfigTest.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.signedconfig;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import static java.util.Collections.emptySet;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.common.collect.Sets;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+
+
+/**
+ * Tests for {@link SignedConfig}
+ */
+@RunWith(AndroidJUnit4.class)
+public class SignedConfigTest {
+
+    private static Set<String> setOf(String... values) {
+        return Sets.newHashSet(values);
+    }
+
+    @Test
+    public void testParsePerSdkConfigSdkMinMax() throws JSONException, InvalidConfigException {
+        JSONObject json = new JSONObject("{\"minSdk\":2, \"maxSdk\": 3, \"values\": []}");
+        SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, emptySet());
+        assertThat(config.minSdk).isEqualTo(2);
+        assertThat(config.maxSdk).isEqualTo(3);
+    }
+
+    @Test
+    public void testParsePerSdkConfigNoMinSdk() throws JSONException {
+        JSONObject json = new JSONObject("{\"maxSdk\": 3, \"values\": []}");
+        try {
+            SignedConfig.parsePerSdkConfig(json, emptySet());
+            fail("Expected InvalidConfigException or JSONException");
+        } catch (JSONException | InvalidConfigException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testParsePerSdkConfigNoMaxSdk() throws JSONException {
+        JSONObject json = new JSONObject("{\"minSdk\": 1, \"values\": []}");
+        try {
+            SignedConfig.parsePerSdkConfig(json, emptySet());
+            fail("Expected InvalidConfigException or JSONException");
+        } catch (JSONException | InvalidConfigException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testParsePerSdkConfigNoValues() throws JSONException {
+        JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3}");
+        try {
+            SignedConfig.parsePerSdkConfig(json, emptySet());
+            fail("Expected InvalidConfigException or JSONException");
+        } catch (JSONException | InvalidConfigException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testParsePerSdkConfigSdkNullMinSdk() throws JSONException, InvalidConfigException {
+        JSONObject json = new JSONObject("{\"minSdk\":null, \"maxSdk\": 3, \"values\": []}");
+        try {
+            SignedConfig.parsePerSdkConfig(json, emptySet());
+            fail("Expected InvalidConfigException or JSONException");
+        } catch (JSONException | InvalidConfigException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testParsePerSdkConfigSdkNullMaxSdk() throws JSONException, InvalidConfigException {
+        JSONObject json = new JSONObject("{\"minSdk\":1, \"maxSdk\": null, \"values\": []}");
+        try {
+            SignedConfig.parsePerSdkConfig(json, emptySet());
+            fail("Expected InvalidConfigException or JSONException");
+        } catch (JSONException | InvalidConfigException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testParsePerSdkConfigNullValues() throws JSONException {
+        JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3, \"values\": null}");
+        try {
+            SignedConfig.parsePerSdkConfig(json, emptySet());
+            fail("Expected InvalidConfigException or JSONException");
+        } catch (JSONException | InvalidConfigException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testParsePerSdkConfigZeroValues()
+            throws JSONException, InvalidConfigException {
+        JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 3, \"values\": []}");
+        SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"));
+        assertThat(config.values).hasSize(0);
+    }
+
+    @Test
+    public void testParsePerSdkConfigSingleKey()
+            throws JSONException, InvalidConfigException {
+        JSONObject json = new JSONObject(
+                "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
+        SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"));
+        assertThat(config.values).containsExactly("a", "1");
+    }
+
+    @Test
+    public void testParsePerSdkConfigMultiKeys()
+            throws JSONException, InvalidConfigException {
+        JSONObject json = new JSONObject(
+                "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}, "
+                        + "{\"key\":\"c\", \"value\": \"2\"}]}");
+        SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(
+                json, setOf("a", "b", "c"));
+        assertThat(config.values).containsExactly("a", "1", "c", "2");
+    }
+
+    @Test
+    public void testParsePerSdkConfigSingleKeyNotAllowed() throws JSONException {
+        JSONObject json = new JSONObject(
+                "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\", \"value\": \"1\"}]}");
+        try {
+            SignedConfig.parsePerSdkConfig(json, setOf("b"));
+            fail("Expected InvalidConfigException or JSONException");
+        } catch (JSONException | InvalidConfigException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testParsePerSdkConfigSingleKeyNoValue()
+            throws JSONException, InvalidConfigException {
+        JSONObject json = new JSONObject(
+                "{\"minSdk\": 1, \"maxSdk\": 1, \"values\": [{\"key\":\"a\"}]}");
+        SignedConfig.PerSdkConfig config = SignedConfig.parsePerSdkConfig(json, setOf("a", "b"));
+        assertThat(config.values).containsExactly("a", null);
+    }
+
+    @Test
+    public void testParsePerSdkConfigValuesInvalid() throws JSONException  {
+        JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1,  \"values\": \"foo\"}");
+        try {
+            SignedConfig.parsePerSdkConfig(json, emptySet());
+            fail("Expected InvalidConfigException or JSONException");
+        } catch (JSONException | InvalidConfigException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testParsePerSdkConfigConfigEntryInvalid() throws JSONException {
+        JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1,  \"values\": [1, 2]}");
+        try {
+            SignedConfig.parsePerSdkConfig(json, emptySet());
+            fail("Expected InvalidConfigException or JSONException");
+        } catch (JSONException | InvalidConfigException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testParsePerSdkConfigConfigEntryNull() throws JSONException {
+        JSONObject json = new JSONObject("{\"minSdk\": 1, \"maxSdk\": 1,  \"values\": [null]}");
+        try {
+            SignedConfig.parsePerSdkConfig(json, emptySet());
+            fail("Expected InvalidConfigException or JSONException");
+        } catch (JSONException | InvalidConfigException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testParseVersion() throws InvalidConfigException {
+        SignedConfig config = SignedConfig.parse(
+                "{\"version\": 1, \"config\": []}", emptySet());
+        assertThat(config.version).isEqualTo(1);
+    }
+
+    @Test
+    public void testParseVersionInvalid() {
+        try {
+            SignedConfig.parse("{\"version\": \"notanint\", \"config\": []}", emptySet());
+            fail("Expected InvalidConfigException");
+        } catch (InvalidConfigException e) {
+            //expected
+        }
+    }
+
+    @Test
+    public void testParseNoVersion() {
+        try {
+            SignedConfig.parse("{\"config\": []}", emptySet());
+            fail("Expected InvalidConfigException");
+        } catch (InvalidConfigException e) {
+            //expected
+        }
+    }
+
+    @Test
+    public void testParseNoConfig() {
+        try {
+            SignedConfig.parse("{\"version\": 1}", emptySet());
+            fail("Expected InvalidConfigException");
+        } catch (InvalidConfigException e) {
+            //expected
+        }
+    }
+
+    @Test
+    public void testParseConfigNull() {
+        try {
+            SignedConfig.parse("{\"version\": 1, \"config\": null}", emptySet());
+            fail("Expected InvalidConfigException");
+        } catch (InvalidConfigException e) {
+            //expected
+        }
+    }
+
+    @Test
+    public void testParseVersionNull() {
+        try {
+            SignedConfig.parse("{\"version\": null, \"config\": []}", emptySet());
+            fail("Expected InvalidConfigException");
+        } catch (InvalidConfigException e) {
+            //expected
+        }
+    }
+
+    @Test
+    public void testParseConfigInvalidEntry() {
+        try {
+            SignedConfig.parse("{\"version\": 1, \"config\": [{}]}", emptySet());
+            fail("Expected InvalidConfigException");
+        } catch (InvalidConfigException e) {
+            //expected
+        }
+    }
+
+    @Test
+    public void testParseSdkConfigSingle() throws InvalidConfigException {
+        SignedConfig config = SignedConfig.parse(
+                "{\"version\": 1, \"config\":[{\"minSdk\": 1, \"maxSdk\": 1, \"values\": []}]}",
+                emptySet());
+        assertThat(config.perSdkConfig).hasSize(1);
+    }
+
+    @Test
+    public void testParseSdkConfigMultiple() throws InvalidConfigException {
+        SignedConfig config = SignedConfig.parse(
+                "{\"version\": 1, \"config\":[{\"minSdk\": 1, \"maxSdk\": 1, \"values\": []}, "
+                        + "{\"minSdk\": 2, \"maxSdk\": 2, \"values\": []}]}", emptySet());
+        assertThat(config.perSdkConfig).hasSize(2);
+    }
+
+    @Test
+    public void testGetMatchingConfigFirst() {
+        SignedConfig.PerSdkConfig sdk1 = new SignedConfig.PerSdkConfig(
+                1, 1, Collections.emptyMap());
+        SignedConfig.PerSdkConfig sdk2 = new SignedConfig.PerSdkConfig(
+                2, 2, Collections.emptyMap());
+        SignedConfig config = new SignedConfig(0, Arrays.asList(sdk1, sdk2));
+        assertThat(config.getMatchingConfig(1)).isEqualTo(sdk1);
+    }
+
+    @Test
+    public void testGetMatchingConfigSecond() {
+        SignedConfig.PerSdkConfig sdk1 = new SignedConfig.PerSdkConfig(
+                1, 1, Collections.emptyMap());
+        SignedConfig.PerSdkConfig sdk2 = new SignedConfig.PerSdkConfig(
+                2, 2, Collections.emptyMap());
+        SignedConfig config = new SignedConfig(0, Arrays.asList(sdk1, sdk2));
+        assertThat(config.getMatchingConfig(2)).isEqualTo(sdk2);
+    }
+
+    @Test
+    public void testGetMatchingConfigInRange() {
+        SignedConfig.PerSdkConfig sdk13 = new SignedConfig.PerSdkConfig(
+                1, 3, Collections.emptyMap());
+        SignedConfig.PerSdkConfig sdk46 = new SignedConfig.PerSdkConfig(
+                4, 6, Collections.emptyMap());
+        SignedConfig config = new SignedConfig(0, Arrays.asList(sdk13, sdk46));
+        assertThat(config.getMatchingConfig(2)).isEqualTo(sdk13);
+    }
+
+    @Test
+    public void testGetMatchingConfigNoMatch() {
+        SignedConfig.PerSdkConfig sdk1 = new SignedConfig.PerSdkConfig(
+                1, 1, Collections.emptyMap());
+        SignedConfig.PerSdkConfig sdk2 = new SignedConfig.PerSdkConfig(
+                2, 2, Collections.emptyMap());
+        SignedConfig config = new SignedConfig(0, Arrays.asList(sdk1, sdk2));
+        assertThat(config.getMatchingConfig(3)).isNull();
+    }
+
+}
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index ca8cc0d..7a5eaa8 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -50,9 +50,9 @@
         "liblog",
         "liblzma",
         "libnativehelper",
-        "libnetdaidl",
         "libui",
         "libunwindstack",
         "libutils",
+        "netd_aidl_interface-cpp",
     ],
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index 82e0fbe..a71aca5 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -106,8 +106,8 @@
         mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
         TestableLooper.get(this).processAllMessages();
 
-        verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
-                argThat(b -> {
+        verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN),
+                eq(null), argThat(b -> {
                     assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
                     return true;
                 }));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 3a56419..f553c35 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -408,6 +408,7 @@
             initialize(intentFirewall, intentController, looper);
             initRootActivityContainerMocks(wm);
             setWindowManager(wm);
+            createDefaultDisplay();
         }
 
         void initRootActivityContainerMocks(WindowManagerService wm) {
@@ -424,7 +425,9 @@
             // Called when moving activity to pinned stack.
             doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
                     anyBoolean());
+        }
 
+        void createDefaultDisplay() {
             // Create a default display and put a home stack on it so that we'll always have
             // something focusable.
             mDefaultDisplay = TestActivityDisplay.create(mStackSupervisor, DEFAULT_DISPLAY);
@@ -598,10 +601,8 @@
         }
 
         @Override
-        protected DisplayWindowController createWindowContainerController() {
-            DisplayWindowController out = mock(DisplayWindowController.class);
-            out.mContainer = WindowTestUtils.createTestDisplayContent();
-            return out;
+        protected DisplayContent createDisplayContent() {
+            return WindowTestUtils.createTestDisplayContent();
         }
 
         void removeAllTasks() {
@@ -616,6 +617,7 @@
 
     private static WindowManagerService prepareMockWindowManager() {
         final WindowManagerService service = mock(WindowManagerService.class);
+        service.mRoot = mock(RootWindowContainer.class);
 
         doAnswer((InvocationOnMock invocationOnMock) -> {
             final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 577859c..fa42289 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -80,7 +80,7 @@
     @Test
     public void testForceOverride() {
         mWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */);
-        mDc.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+        mDc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
                 false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
         assertEquals(TRANSIT_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransition());
     }
@@ -102,8 +102,8 @@
     @Test
     public void testAppTransitionStateForMultiDisplay() {
         // Create 2 displays & presume both display the state is ON for ready to display & animate.
-        final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON);
-        final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON);
+        final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
+        final DisplayContent dc2 = createNewDisplay(Display.STATE_ON);
 
         // Create 2 app window tokens to represent 2 activity window.
         final WindowTestUtils.TestAppWindowToken token1 = createTestAppWindowToken(dc1,
@@ -117,10 +117,10 @@
 
         // Simulate activity resume / finish flows to prepare app transition & set visibility,
         // make sure transition is set as expected for each display.
-        dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+        dc1.prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
                 false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
         assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition());
-        dc2.getController().prepareAppTransition(TRANSIT_ACTIVITY_CLOSE,
+        dc2.prepareAppTransition(TRANSIT_ACTIVITY_CLOSE,
                 false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
         assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition());
         // One activity window is visible for resuming & the other activity window is invisible
@@ -138,8 +138,8 @@
     @Test
     public void testCleanAppTransitionWhenTaskStackReparent() {
         // Create 2 displays & presume both display the state is ON for ready to display & animate.
-        final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON);
-        final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON);
+        final DisplayContent dc1 = createNewDisplay(Display.STATE_ON);
+        final DisplayContent dc2 = createNewDisplay(Display.STATE_ON);
 
         final TaskStack stack1 = createTaskStackOnDisplay(dc1);
         final Task task1 = createTaskInStack(stack1, 0 /* userId */);
@@ -151,7 +151,7 @@
         dc1.mClosingApps.add(token1);
         assertTrue(dc1.mClosingApps.size() > 0);
 
-        dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+        dc1.prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
                 false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
         assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition());
         assertTrue(dc1.mAppTransition.isTransitionSet());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 3c7b4b1..3e025f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -875,8 +875,8 @@
         }
 
         @Override
-        void initRootActivityContainerMocks(WindowManagerService wm) {
-            super.initRootActivityContainerMocks(wm);
+        void createDefaultDisplay() {
+            super.createDefaultDisplay();
             mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
             mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
             mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 612f9ad..8854ede 100644
--- a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -49,7 +49,7 @@
         mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token);
 
         // Make sure our handler processed the message.
-        SystemClock.sleep(100);
+        mWm.mH.runWithScissors(() -> { }, 0);
         assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
     }
 
@@ -65,7 +65,7 @@
         mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token2);
 
         // Make sure our handler processed the message.
-        SystemClock.sleep(100);
+        mWm.mH.runWithScissors(() -> { }, 0);
         assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
index 50fd188..522ab9f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -149,10 +149,9 @@
                 mService.onInitReady();
 
                 final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
-                final DisplayWindowController dcw = new DisplayWindowController(display, mService);
                 // Display creation is driven by the ActivityManagerService via
                 // ActivityStackSupervisor. We emulate those steps here.
-                mService.mRoot.createDisplayContent(display, dcw);
+                mService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
             }
 
             private void removeServices() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index aa0ecf8..65e1835 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -58,8 +58,8 @@
     public static class TestDisplayContent extends DisplayContent {
 
         private TestDisplayContent(Display display, WindowManagerService service,
-                DisplayWindowController controller) {
-            super(display, service, controller);
+                ActivityDisplay activityDisplay) {
+            super(display, service, activityDisplay);
         }
 
         /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 4394f9a..5c3368b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -360,21 +360,17 @@
         final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                 displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
         synchronized (mWm.mGlobalLock) {
-            return new DisplayContent(display, mWm, mock(DisplayWindowController.class));
+            return new DisplayContent(display, mWm, mock(ActivityDisplay.class));
         }
     }
 
     /**
      * Creates a {@link DisplayContent} with given display state and adds it to the system.
      *
-     * Unlike {@link #createNewDisplay()} that uses a mock {@link DisplayWindowController} to
-     * initialize {@link DisplayContent}, this method used real controller object when the test
-     * need to verify its related flows.
-     *
      * @param displayState For initializing the state of the display. See
      *                     {@link Display#getState()}.
      */
-    DisplayContent createNewDisplayWithController(int displayState) {
+    DisplayContent createNewDisplay(int displayState) {
         // Leverage main display info & initialize it with display state for given displayId.
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.copyFrom(mDisplayInfo);
@@ -382,11 +378,11 @@
         final int displayId = sNextDisplayId++;
         final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                 displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-        final DisplayWindowController dcw = new DisplayWindowController(display, mWm);
         synchronized (mWm.mGlobalLock) {
             // Display creation is driven by DisplayWindowController via ActivityStackSupervisor.
             // We skip those steps here.
-            return mWm.mRoot.createDisplayContent(display, dcw);
+            final ActivityDisplay mockAd = mock(ActivityDisplay.class);
+            return mWm.mRoot.createDisplayContent(display, mockAd);
         }
     }
 
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d617de0..36d0188 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -122,10 +122,21 @@
      * The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
      * extras. Used to pass the phone accounts to display on the front end to the user in order to
      * select phone accounts to (for example) place a call.
+     * @deprecated Use the list from {@link #EXTRA_SUGGESTED_PHONE_ACCOUNTS} instead.
      */
+    @Deprecated
     public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
 
     /**
+     * Key for extra used to pass along a list of {@link PhoneAccountSuggestion}s to the in-call
+     * UI when a call enters the {@link #STATE_SELECT_PHONE_ACCOUNT} state. The list included here
+     * will have the same length and be in the same order as the list passed with
+     * {@link #AVAILABLE_PHONE_ACCOUNTS}.
+     */
+    public static final String EXTRA_SUGGESTED_PHONE_ACCOUNTS =
+            "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS";
+
+    /**
      * Extra key used to indicate the time (in milliseconds since midnight, January 1, 1970 UTC)
      * when the last outgoing emergency call was made.  This is used to identify potential emergency
      * callbacks.
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.java b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
new file mode 100644
index 0000000..4e6a178
--- /dev/null
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
@@ -0,0 +1,135 @@
+/*
+ * 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.telecom;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public final class PhoneAccountSuggestion implements Parcelable {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {REASON_NONE, REASON_INTRA_CARRIER, REASON_FREQUENT,
+            REASON_USER_SET, REASON_OTHER}, prefix = { "REASON_" })
+    public @interface SuggestionReason {}
+
+    /**
+     * Indicates that this account is not suggested for use, but is still available.
+     */
+    public static final int REASON_NONE = 0;
+
+    /**
+     * Indicates that the {@link PhoneAccountHandle} is suggested because the number we're calling
+     * is on the same carrier, and therefore may have lower rates.
+     */
+    public static final int REASON_INTRA_CARRIER = 1;
+
+    /**
+     * Indicates that the {@link PhoneAccountHandle} is suggested because the user uses it
+     * frequently for the number that we are calling.
+     */
+    public static final int REASON_FREQUENT = 2;
+
+    /**
+     * Indicates that the {@link PhoneAccountHandle} is suggested because the user explicitly
+     * specified that it be used for the number we are calling.
+     */
+    public static final int REASON_USER_SET = 3;
+
+    /**
+     * Indicates that the {@link PhoneAccountHandle} is suggested for a reason not otherwise
+     * enumerated here.
+     */
+    public static final int REASON_OTHER = 4;
+
+    private PhoneAccountHandle mHandle;
+    private int mReason;
+    private boolean mShouldAutoSelect;
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public PhoneAccountSuggestion(PhoneAccountHandle handle, @SuggestionReason int reason,
+            boolean shouldAutoSelect) {
+        this.mHandle = handle;
+        this.mReason = reason;
+        this.mShouldAutoSelect = shouldAutoSelect;
+    }
+
+    private PhoneAccountSuggestion(Parcel in) {
+        mHandle = in.readParcelable(PhoneAccountHandle.class.getClassLoader());
+        mReason = in.readInt();
+        mShouldAutoSelect = in.readByte() != 0;
+    }
+
+    public static final Creator<PhoneAccountSuggestion> CREATOR =
+            new Creator<PhoneAccountSuggestion>() {
+                @Override
+                public PhoneAccountSuggestion createFromParcel(Parcel in) {
+                    return new PhoneAccountSuggestion(in);
+                }
+
+                @Override
+                public PhoneAccountSuggestion[] newArray(int size) {
+                    return new PhoneAccountSuggestion[size];
+                }
+            };
+
+    /**
+     * @return The {@link PhoneAccountHandle} for this suggestion.
+     */
+    public PhoneAccountHandle getPhoneAccountHandle() {
+        return mHandle;
+    }
+
+    /**
+     * @return The reason for this suggestion
+     */
+    public @SuggestionReason int getReason() {
+        return mReason;
+    }
+
+    /**
+     * Suggests whether the dialer should automatically place the call using this account without
+     * user interaction. This may be set on multiple {@link PhoneAccountSuggestion}s, and the dialer
+     * is free to choose which one to use.
+     * @return {@code true} if the hint is to auto-select, {@code false} otherwise.
+     */
+    public boolean shouldAutoSelect() {
+        return mShouldAutoSelect;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mHandle, flags);
+        dest.writeInt(mReason);
+        dest.writeByte((byte) (mShouldAutoSelect ? 1 : 0));
+    }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 0a60e34..60eb18c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -78,6 +78,15 @@
     // system image, that can be added in packages/apps/CarrierConfig.
 
     /**
+     * Specifies a value that identifies the version of the carrier configuration that is
+     * currently in use. This string is displayed on the UI.
+     * The format of the string is not specified.
+     * @hide
+     */
+    public static final String KEY_CARRIER_CONFIG_VERSION_STRING =
+            "carrier_config_version_string";
+
+    /**
      * This flag specifies whether VoLTE availability is based on provisioning. By default this is
      * false.
      */
@@ -2392,6 +2401,7 @@
 
     static {
         sDefaults = new PersistableBundle();
+        sDefaults.putString(KEY_CARRIER_CONFIG_VERSION_STRING, "");
         sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index f6e8d34..c95837e 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1738,7 +1738,10 @@
      * @param number the number to look up.
      * @return true if the number is in the list of emergency numbers
      *         listed in the RIL / SIM, otherwise return false.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)} instead.
      */
+    @Deprecated
     public static boolean isEmergencyNumber(String number) {
         return isEmergencyNumber(getDefaultVoiceSubId(), number);
     }
@@ -1751,8 +1754,13 @@
      * @param number the number to look up.
      * @return true if the number is in the list of emergency numbers
      *         listed in the RIL / SIM, otherwise return false.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage
     public static boolean isEmergencyNumber(int subId, String number) {
         // Return true only if the specified number *exactly* matches
@@ -1778,8 +1786,12 @@
      *         listed in the RIL / SIM, *or* if the number starts with the
      *         same digits as any of those emergency numbers.
      *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     public static boolean isPotentialEmergencyNumber(String number) {
         return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number);
     }
@@ -1802,9 +1814,14 @@
      * @return true if the number is in the list of emergency numbers
      *         listed in the RIL / SIM, *or* if the number starts with the
      *         same digits as any of those emergency numbers.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
     @UnsupportedAppUsage
+    @Deprecated
     public static boolean isPotentialEmergencyNumber(int subId, String number) {
         // Check against the emergency numbers listed by the RIL / SIM,
         // and *don't* require an exact match.
@@ -1867,8 +1884,12 @@
      * @return if the number is an emergency number for the specific country, then return true,
      * otherwise false
      *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage
     public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
             return isEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
@@ -1882,8 +1903,13 @@
      * @param defaultCountryIso the specific country which the number should be checked against
      * @return if the number is an emergency number for the specific country, then return true,
      * otherwise false
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     public static boolean isEmergencyNumber(int subId, String number, String defaultCountryIso) {
         return isEmergencyNumberInternal(subId, number,
                                          defaultCountryIso,
@@ -1909,8 +1935,12 @@
      *         country, *or* if the number starts with the same digits as
      *         any of those emergency numbers.
      *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
         return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
     }
@@ -1934,8 +1964,13 @@
      * @return true if the number is an emergency number for the specific
      *         country, *or* if the number starts with the same digits as
      *         any of those emergency numbers.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     public static boolean isPotentialEmergencyNumber(int subId, String number,
             String defaultCountryIso) {
         return isEmergencyNumberInternal(subId, number,
@@ -1983,92 +2018,7 @@
     private static boolean isEmergencyNumberInternal(int subId, String number,
                                                      String defaultCountryIso,
                                                      boolean useExactMatch) {
-        // If the number passed in is null, just return false:
-        if (number == null) return false;
-
-        // If the number passed in is a SIP address, return false, since the
-        // concept of "emergency numbers" is only meaningful for calls placed
-        // over the cell network.
-        // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
-        // since the whole point of extractNetworkPortionAlt() is to filter out
-        // any non-dialable characters (which would turn 'abc911def@example.com'
-        // into '911', for example.))
-        if (isUriNumber(number)) {
-            return false;
-        }
-
-        // Strip the separators from the number before comparing it
-        // to the list.
-        number = extractNetworkPortionAlt(number);
-
-        String emergencyNumbers = "";
-        int slotId = SubscriptionManager.getSlotIndex(subId);
-
-        // retrieve the list of emergency numbers
-        // check read-write ecclist property first
-        String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
-
-        emergencyNumbers = SystemProperties.get(ecclist, "");
-
-        Rlog.d(LOG_TAG, "slotId:" + slotId + " subId:" + subId + " country:"
-                + defaultCountryIso + " emergencyNumbers: " +  emergencyNumbers);
-
-        if (TextUtils.isEmpty(emergencyNumbers)) {
-            // then read-only ecclist property since old RIL only uses this
-            emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
-        }
-
-        if (!TextUtils.isEmpty(emergencyNumbers)) {
-            // searches through the comma-separated list for a match,
-            // return true if one is found.
-            for (String emergencyNum : emergencyNumbers.split(",")) {
-                // It is not possible to append additional digits to an emergency number to dial
-                // the number in Brazil - it won't connect.
-                if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
-                    if (number.equals(emergencyNum)) {
-                        return true;
-                    }
-                } else {
-                    if (number.startsWith(emergencyNum)) {
-                        return true;
-                    }
-                }
-            }
-            // no matches found against the list!
-            return false;
-        }
-
-        Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
-                + " Use embedded logic for determining ones.");
-
-        // If slot id is invalid, means that there is no sim card.
-        // According spec 3GPP TS22.101, the following numbers should be
-        // ECC numbers when SIM/USIM is not present.
-        emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
-
-        for (String emergencyNum : emergencyNumbers.split(",")) {
-            if (useExactMatch) {
-                if (number.equals(emergencyNum)) {
-                    return true;
-                }
-            } else {
-                if (number.startsWith(emergencyNum)) {
-                    return true;
-                }
-            }
-        }
-
-        // No ecclist system property, so use our own list.
-        if (defaultCountryIso != null) {
-            ShortNumberInfo info = ShortNumberInfo.getInstance();
-            if (useExactMatch) {
-                return info.isEmergencyNumber(number, defaultCountryIso);
-            } else {
-                return info.connectsToEmergencyNumber(number, defaultCountryIso);
-            }
-        }
-
-        return false;
+        return TelephonyManager.getDefault().isCurrentEmergencyNumber(number);
     }
 
     /**
@@ -2078,7 +2028,11 @@
      * @param context the specific context which the number should be checked against
      * @return true if the specified number is an emergency number for the country the user
      * is currently in.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+     *             instead.
      */
+    @Deprecated
     public static boolean isLocalEmergencyNumber(Context context, String number) {
         return isLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
     }
@@ -2091,8 +2045,13 @@
      * @param context the specific context which the number should be checked against
      * @return true if the specified number is an emergency number for the country the user
      * is currently in.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage
     public static boolean isLocalEmergencyNumber(Context context, int subId, String number) {
         return isLocalEmergencyNumberInternal(subId, number,
@@ -2120,8 +2079,13 @@
      *              CountryDetector.
      *
      * @see android.location.CountryDetector
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage
     public static boolean isPotentialLocalEmergencyNumber(Context context, String number) {
         return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
@@ -2147,9 +2111,13 @@
      * @return true if the specified number is an emergency number for a local country, based on the
      *              CountryDetector.
      *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
     @UnsupportedAppUsage
+    @Deprecated
     public static boolean isPotentialLocalEmergencyNumber(Context context, int subId,
             String number) {
         return isLocalEmergencyNumberInternal(subId, number,
@@ -2217,6 +2185,101 @@
     }
 
     /**
+     * Back-up old logics for {@link #isEmergencyNumberInternal} for legacy and deprecate purpose.
+     *
+     * @hide
+     */
+    public static boolean isEmergencyNumberInternal(String number, boolean useExactMatch,
+                                                    String defaultCountryIso) {
+        // If the number passed in is null, just return false:
+        if (number == null) return false;
+
+        // If the number passed in is a SIP address, return false, since the
+        // concept of "emergency numbers" is only meaningful for calls placed
+        // over the cell network.
+        // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
+        // since the whole point of extractNetworkPortionAlt() is to filter out
+        // any non-dialable characters (which would turn 'abc911def@example.com'
+        // into '911', for example.))
+        if (PhoneNumberUtils.isUriNumber(number)) {
+            return false;
+        }
+
+        // Strip the separators from the number before comparing it
+        // to the list.
+        number = PhoneNumberUtils.extractNetworkPortionAlt(number);
+
+        String emergencyNumbers = "";
+        int slotId = SubscriptionManager.getSlotIndex(getDefaultVoiceSubId());
+
+        // retrieve the list of emergency numbers
+        // check read-write ecclist property first
+        String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
+
+        emergencyNumbers = SystemProperties.get(ecclist, "");
+
+        Rlog.d(LOG_TAG, "slotId:" + slotId + " country:"
+                + defaultCountryIso + " emergencyNumbers: " +  emergencyNumbers);
+
+        if (TextUtils.isEmpty(emergencyNumbers)) {
+            // then read-only ecclist property since old RIL only uses this
+            emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
+        }
+
+        if (!TextUtils.isEmpty(emergencyNumbers)) {
+            // searches through the comma-separated list for a match,
+            // return true if one is found.
+            for (String emergencyNum : emergencyNumbers.split(",")) {
+                // It is not possible to append additional digits to an emergency number to dial
+                // the number in Brazil - it won't connect.
+                if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
+                    if (number.equals(emergencyNum)) {
+                        return true;
+                    }
+                } else {
+                    if (number.startsWith(emergencyNum)) {
+                        return true;
+                    }
+                }
+            }
+            // no matches found against the list!
+            return false;
+        }
+
+        Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
+                + " Use embedded logic for determining ones.");
+
+        // If slot id is invalid, means that there is no sim card.
+        // According spec 3GPP TS22.101, the following numbers should be
+        // ECC numbers when SIM/USIM is not present.
+        emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
+
+        for (String emergencyNum : emergencyNumbers.split(",")) {
+            if (useExactMatch) {
+                if (number.equals(emergencyNum)) {
+                    return true;
+                }
+            } else {
+                if (number.startsWith(emergencyNum)) {
+                    return true;
+                }
+            }
+        }
+
+        // No ecclist system property, so use our own list.
+        if (defaultCountryIso != null) {
+            ShortNumberInfo info = ShortNumberInfo.getInstance();
+            if (useExactMatch) {
+                return info.isEmergencyNumber(number, defaultCountryIso);
+            } else {
+                return info.connectsToEmergencyNumber(number, defaultCountryIso);
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * isVoiceMailNumber: checks a given number against the voicemail
      *   number provided by the RIL and SIM card. The caller must have
      *   the READ_PHONE_STATE credential.
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index e8a28ca..0df0daf 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -27,12 +27,14 @@
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.Looper;
+import android.telephony.emergency.EmergencyNumber;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IPhoneStateListener;
 
 import java.lang.ref.WeakReference;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Executor;
 
 /**
@@ -313,6 +315,8 @@
      *
      * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
      * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @see #onEmergencyNumberListChanged
      */
     public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
 
@@ -603,6 +607,21 @@
     }
 
     /**
+     * Callback invoked when the current emergency number list has changed
+     *
+     * @param emergencyNumberList Map including the key as the active subscription ID
+     *                           (Note: if there is no active subscription, the key is
+     *                           {@link SubscriptionManager#getDefaultSubscriptionId})
+     *                           and the value as the list of {@link EmergencyNumber};
+     *                           null if this information is not available.
+     * @hide
+     */
+    public void onEmergencyNumberListChanged(
+            @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
+        // default implementation empty
+    }
+
+    /**
      * Callback invoked when OEM hook raw event is received. Requires
      * the READ_PRIVILEGED_PHONE_STATE permission.
      * @param rawData is the byte array of the OEM hook raw data.
@@ -859,6 +878,16 @@
                             () -> psl.onPhysicalChannelConfigurationChanged(configs)));
         }
 
+        @Override
+        public void onEmergencyNumberListChanged(Map emergencyNumberList) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onEmergencyNumberListChanged(emergencyNumberList)));
+        }
+
         public void onPhoneCapabilityChanged(PhoneCapability capability) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index ca0c854..78f05168 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -27,6 +27,7 @@
 import android.os.Parcelable;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.NetworkRegistrationState.Domain;
+import android.telephony.NetworkRegistrationState.NRStatus;
 import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
@@ -1358,6 +1359,18 @@
     }
 
     /**
+     * Get the NR 5G status of the mobile data network.
+     * @return the NR 5G status.
+     * @hide
+     */
+    public @NRStatus int getNrStatus() {
+        final NetworkRegistrationState regState = getNetworkRegistrationState(
+                NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN);
+        if (regState == null) return NetworkRegistrationState.NR_STATUS_NONE;
+        return regState.getNrStatus();
+    }
+
+    /**
      * @param nrFrequencyRange the frequency range of 5G NR.
      * @hide
      */
@@ -1410,47 +1423,49 @@
     }
 
     /** @hide */
-    public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rt) {
-        switch(rt) {
-        case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
-            return TelephonyManager.NETWORK_TYPE_GPRS;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
-            return TelephonyManager.NETWORK_TYPE_EDGE;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
-            return TelephonyManager.NETWORK_TYPE_UMTS;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
-            return TelephonyManager.NETWORK_TYPE_HSDPA;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
-            return TelephonyManager.NETWORK_TYPE_HSUPA;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
-            return TelephonyManager.NETWORK_TYPE_HSPA;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_IS95A:
-        case ServiceState.RIL_RADIO_TECHNOLOGY_IS95B:
-            return TelephonyManager.NETWORK_TYPE_CDMA;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
-            return TelephonyManager.NETWORK_TYPE_1xRTT;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
-            return TelephonyManager.NETWORK_TYPE_EVDO_0;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
-            return TelephonyManager.NETWORK_TYPE_EVDO_A;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
-            return TelephonyManager.NETWORK_TYPE_EVDO_B;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
-            return TelephonyManager.NETWORK_TYPE_EHRPD;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
-            return TelephonyManager.NETWORK_TYPE_LTE;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
-            return TelephonyManager.NETWORK_TYPE_HSPAP;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_GSM:
-            return TelephonyManager.NETWORK_TYPE_GSM;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA:
-            return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
-            return TelephonyManager.NETWORK_TYPE_IWLAN;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
-            return TelephonyManager.NETWORK_TYPE_LTE_CA;
-        default:
-            return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rat) {
+        switch(rat) {
+            case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
+                return TelephonyManager.NETWORK_TYPE_GPRS;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
+                return TelephonyManager.NETWORK_TYPE_EDGE;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
+                return TelephonyManager.NETWORK_TYPE_UMTS;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
+                return TelephonyManager.NETWORK_TYPE_HSDPA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
+                return TelephonyManager.NETWORK_TYPE_HSUPA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
+                return TelephonyManager.NETWORK_TYPE_HSPA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_IS95A:
+            case ServiceState.RIL_RADIO_TECHNOLOGY_IS95B:
+                return TelephonyManager.NETWORK_TYPE_CDMA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
+                return TelephonyManager.NETWORK_TYPE_1xRTT;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
+                return TelephonyManager.NETWORK_TYPE_EVDO_0;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
+                return TelephonyManager.NETWORK_TYPE_EVDO_A;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
+                return TelephonyManager.NETWORK_TYPE_EVDO_B;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
+                return TelephonyManager.NETWORK_TYPE_EHRPD;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
+                return TelephonyManager.NETWORK_TYPE_LTE;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
+                return TelephonyManager.NETWORK_TYPE_HSPAP;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_GSM:
+                return TelephonyManager.NETWORK_TYPE_GSM;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+                return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
+                return TelephonyManager.NETWORK_TYPE_IWLAN;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
+                return TelephonyManager.NETWORK_TYPE_LTE_CA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_NR:
+                return TelephonyManager.NETWORK_TYPE_NR;
+            default:
+                return TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
     }
 
@@ -1531,7 +1546,6 @@
         }
     }
 
-
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public @TelephonyManager.NetworkType int getDataNetworkType() {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fa9b76d..422e66d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -810,6 +810,7 @@
      * @see TelephonyManager#NETWORK_TYPE_LTE
      * @see TelephonyManager#NETWORK_TYPE_EHRPD
      * @see TelephonyManager#NETWORK_TYPE_HSPAP
+     * @see TelephonyManager#NETWORK_TYPE_NR
      *
      * <p class="note">
      * Retrieve with
@@ -2328,6 +2329,7 @@
      * @see #NETWORK_TYPE_LTE
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
+     * @see #NETWORK_TYPE_NR
      *
      * @hide
      */
@@ -2379,6 +2381,7 @@
      * @see #NETWORK_TYPE_LTE
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
+     * @see #NETWORK_TYPE_NR
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -2565,6 +2568,8 @@
                 return "IWLAN";
             case NETWORK_TYPE_LTE_CA:
                 return "LTE_CA";
+            case NETWORK_TYPE_NR:
+                return "NR";
             default:
                 return "UNKNOWN";
         }
@@ -9449,8 +9454,13 @@
     /**
      * Get the emergency number list based on current locale, sim, default, modem and network.
      *
-     * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at
-     * the smaller index in the returned list.
+     * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher
+     * priority sources will be located at the smaller index; the priority order of sources are:
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG}
      *
      * <p>The subscriptions which the returned list would be based on, are all the active
      * subscriptions, no matter which subscription could be used to create TelephonyManager.
@@ -9459,8 +9469,9 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return Map including the key as the active subscription ID (Note: if there is no active
-     * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value
-     * as the list of {@link EmergencyNumber}; null if this information is not available.
+     * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
+     * as the list of {@link EmergencyNumber}; null if this information is not available; or throw
+     * a SecurityException if the caller does not have the permission.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @Nullable
@@ -9481,8 +9492,13 @@
      * Get the per-category emergency number list based on current locale, sim, default, modem
      * and network.
      *
-     * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at
-     * the smaller index in the returned list.
+     * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher
+     * priority sources will be located at the smaller index; the priority order of sources are:
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG}
      *
      * <p>The subscriptions which the returned list would be based on, are all the active
      * subscriptions, no matter which subscription could be used to create TelephonyManager.
@@ -9503,8 +9519,9 @@
      * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
      * </ol>
      * @return Map including the key as the active subscription ID (Note: if there is no active
-     * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value
-     * as the list of {@link EmergencyNumber}; null if this information is not available.
+     * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
+     * as the list of {@link EmergencyNumber}; null if this information is not available; or throw
+     * a SecurityException if the caller does not have the permission.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @Nullable
@@ -9551,7 +9568,44 @@
             if (telephony == null) {
                 return false;
             }
-            return telephony.isCurrentEmergencyNumber(number);
+            return telephony.isCurrentEmergencyNumber(number, true);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "isCurrentEmergencyNumber RemoteException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Checks if the supplied number is an emergency number based on current locale, sim, default,
+     * modem and network.
+     *
+     * <p> Specifically, this method will return {@code true} if the specified number is an
+     * emergency number, *or* if the number simply starts with the same digits as any current
+     * emergency number.
+     *
+     * <p>The subscriptions which the identification would be based on, are all the active
+     * subscriptions, no matter which subscription could be used to create TelephonyManager.
+     *
+     * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
+     * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param number - the number to look up
+     * @return {@code true} if the given number is an emergency number or it simply starts with
+     * the same digits of any current emergency number based on current locale, sim, modem and
+     * network; {@code false} if it is not; or throw an SecurityException if the caller does not
+     * have the required permission/privileges
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isCurrentPotentialEmergencyNumber(@NonNull String number) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return false;
+            }
+            return telephony.isCurrentEmergencyNumber(number, false);
         } catch (RemoteException ex) {
             Log.e(TAG, "isCurrentEmergencyNumber RemoteException", ex);
         }
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index 41f7bd7..fe062d5 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -22,6 +22,7 @@
 import android.hardware.radio.V1_4.EmergencyServiceCategory;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Rlog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -150,6 +151,7 @@
     @IntDef(flag = true, prefix = { "EMERGENCY_NUMBER_SOURCE_" }, value = {
             EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
             EMERGENCY_NUMBER_SOURCE_SIM,
+            EMERGENCY_NUMBER_SOURCE_DATABASE,
             EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
             EMERGENCY_NUMBER_SOURCE_DEFAULT
     })
@@ -169,6 +171,10 @@
      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
      */
     public static final int EMERGENCY_NUMBER_SOURCE_SIM = EmergencyNumberSource.SIM;
+    /**
+     * Bit-field which indicates the number is from the platform-maintained database.
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_DATABASE =  1 << 4;
     /** Bit-field which indicates the number is from the modem config. */
     public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
             EmergencyNumberSource.MODEM_CONFIG;
@@ -187,21 +193,24 @@
         EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DATABASE);
         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
     }
 
     private final String mNumber;
     private final String mCountryIso;
+    private final String mMnc;
     private final int mEmergencyServiceCategoryBitmask;
     private final int mEmergencyNumberSourceBitmask;
 
     /** @hide */
     public EmergencyNumber(@NonNull String number, @NonNull String countryIso,
-                           int emergencyServiceCategories,
+                           @NonNull String mnc, int emergencyServiceCategories,
                            int emergencyNumberSources) {
         this.mNumber = number;
         this.mCountryIso = countryIso;
+        this.mMnc = mnc;
         this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
         this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
     }
@@ -210,6 +219,7 @@
     public EmergencyNumber(Parcel source) {
         mNumber = source.readString();
         mCountryIso = source.readString();
+        mMnc = source.readString();
         mEmergencyServiceCategoryBitmask = source.readInt();
         mEmergencyNumberSourceBitmask = source.readInt();
     }
@@ -236,6 +246,15 @@
     }
 
     /**
+     * Get the Mobile Network Code of the emergency number.
+     *
+     * @return the Mobile Network Code of the emergency number.
+     */
+    public String getMnc() {
+        return mMnc;
+    }
+
+    /**
      * Returns the bitmask of emergency service categories of the emergency number.
      *
      * @return bitmask of the emergency service categories
@@ -338,6 +357,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mNumber);
         dest.writeString(mCountryIso);
+        dest.writeString(mMnc);
         dest.writeInt(mEmergencyServiceCategoryBitmask);
         dest.writeInt(mEmergencyNumberSourceBitmask);
     }
@@ -350,10 +370,10 @@
 
     @Override
     public String toString() {
-        return "EmergencyNumber = " + "[Number]" + mNumber + " / [CountryIso]" + mCountryIso
-                + " / [ServiceCategories]"
-                + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
-                + " / [Sources]" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
+        return "EmergencyNumber:" + "Number-" + mNumber + "|CountryIso-" + mCountryIso
+                + "|Mnc-" + mMnc
+                + "|ServiceCategories-" + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
+                + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
     }
 
     @Override
@@ -373,6 +393,7 @@
      * The priority of sources are defined as follows:
      *     EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING >
      *     EMERGENCY_NUMBER_SOURCE_SIM >
+     *     EMERGENCY_NUMBER_SOURCE_DATABASE >
      *     EMERGENCY_NUMBER_SOURCE_DEFAULT >
      *     EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
      *
@@ -385,7 +406,9 @@
         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) {
             score += 1 << 3;
         }
-        // TODO add a score if the number comes from Google's emergency number database
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
+            score += 1 << 2;
+        }
         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) {
             score += 1 << 1;
         }
@@ -412,14 +435,104 @@
                 < emergencyNumber.getDisplayPriorityScore()) {
             return 1;
         } else {
-            /**
-             * TODO if both numbers have the same display priority score, the number matches the
-             * Google's emergency number database has a higher display priority.
-             */
             return 0;
         }
     }
 
+    /**
+     * In-place merge same emergency numbers in the emergency number list.
+     *
+     * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
+     * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
+     * for the same EmergencyNumber.
+     *
+     * @param emergencyNumberList the emergency number list to process
+     *
+     * @hide
+     */
+    public static void mergeSameNumbersInEmergencyNumberList(
+            List<EmergencyNumber> emergencyNumberList) {
+        if (emergencyNumberList == null) {
+            return;
+        }
+        Set<EmergencyNumber> mergedEmergencyNumber = new HashSet<>();
+        for (int i = 0; i < emergencyNumberList.size(); i++) {
+            // Skip the check because it was merged.
+            if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) {
+                continue;
+            }
+            for (int j = i + 1; j < emergencyNumberList.size(); j++) {
+                if (isSameEmergencyNumber(
+                        emergencyNumberList.get(i), emergencyNumberList.get(j))) {
+                    Rlog.e(LOG_TAG, "Found unexpected duplicate numbers: "
+                            + emergencyNumberList.get(i) + " vs " + emergencyNumberList.get(j));
+                    // Set the merged emergency number in the current position
+                    emergencyNumberList.set(i, mergeNumbers(
+                            emergencyNumberList.get(i), emergencyNumberList.get(j)));
+                    // Mark the emergency number has been merged
+                    mergedEmergencyNumber.add(emergencyNumberList.get(j));
+                }
+            }
+        }
+        // Remove the marked emergency number in the orignal list
+        for (int i = 0; i < emergencyNumberList.size(); i++) {
+            if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) {
+                emergencyNumberList.remove(i--);
+            }
+        }
+    }
+
+    /**
+     * Check if two emergency numbers are the same.
+     *
+     * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
+     * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
+     * for the same EmergencyNumber.
+     *
+     * @param first first EmergencyNumber to compare
+     * @param second second EmergencyNumber to compare
+     * @return true if they are the same EmergencyNumbers; false otherwise.
+     *
+     * @hide
+     */
+    public static boolean isSameEmergencyNumber(@NonNull EmergencyNumber first,
+                                                @NonNull EmergencyNumber second) {
+        if (!first.getNumber().equals(second.getNumber())) {
+            return false;
+        }
+        if (!first.getCountryIso().equals(second.getCountryIso())) {
+            return false;
+        }
+        if (!first.getMnc().equals(second.getMnc())) {
+            return false;
+        }
+        if (first.getEmergencyServiceCategoryBitmask()
+                != second.getEmergencyServiceCategoryBitmask()) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Get a merged EmergencyNumber for two numbers if they are the same.
+     *
+     * @param first first EmergencyNumber to compare
+     * @param second second EmergencyNumber to compare
+     * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber
+     *
+     * @hide
+     */
+    public static EmergencyNumber mergeNumbers(@NonNull EmergencyNumber first,
+                                         @NonNull EmergencyNumber second) {
+        if (isSameEmergencyNumber(first, second)) {
+            return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
+                    first.getEmergencyServiceCategoryBitmask(),
+                    first.getEmergencyNumberSourceBitmask()
+                            | second.getEmergencyNumberSourceBitmask());
+        }
+        return null;
+    }
+
     public static final Parcelable.Creator<EmergencyNumber> CREATOR =
             new Parcelable.Creator<EmergencyNumber>() {
         @Override
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 79f0635..78fc0bc4 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -25,6 +25,7 @@
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
+import android.telephony.emergency.EmergencyNumber;
 
 oneway interface IPhoneStateListener {
     void onServiceStateChanged(in ServiceState serviceState);
@@ -53,5 +54,6 @@
     void onPhoneCapabilityChanged(in PhoneCapability capability);
     void onPreferredDataSubIdChanged(in int subId);
     void onRadioPowerStateChanged(in int state);
+    void onEmergencyNumberListChanged(in Map emergencyNumberList);
 }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 399dc52..88b9302 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1716,7 +1716,7 @@
     /**
      * Identify if the number is emergency number, based on all the active subscriptions.
      */
-    boolean isCurrentEmergencyNumber(String number);
+    boolean isCurrentEmergencyNumber(String number, boolean exactMatch);
 
     /**
      * Return a list of certs in hex string from loaded carrier privileges access rules.
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 76e7509..d9f5c3f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -81,5 +81,5 @@
     void notifyPhoneCapabilityChanged(in PhoneCapability capability);
     void notifyPreferredDataSubIdChanged(int preferredSubId);
     void notifyRadioPowerStateChanged(in int state);
-    void notifyEmergencyNumberList(in List<EmergencyNumber> emergencyNumberList);
+    void notifyEmergencyNumberList();
 }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index cb8269e..c934317 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -483,4 +483,5 @@
     int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
     int RIL_UNSOL_ICC_SLOT_STATUS = 1100;
     int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1101;
+    int RIL_UNSOL_EMERGENCY_NUMBER_LIST = 1102;
 }
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index b917fbd..0ac35bc 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -54,10 +54,10 @@
      */
     private class InversionIContentProvider implements IContentProvider {
         @Override
-        public ContentProviderResult[] applyBatch(String callingPackage,
+        public ContentProviderResult[] applyBatch(String callingPackage, String authority,
                 ArrayList<ContentProviderOperation> operations)
                 throws RemoteException, OperationApplicationException {
-            return MockContentProvider.this.applyBatch(operations);
+            return MockContentProvider.this.applyBatch(authority, operations);
         }
 
         @Override
@@ -112,9 +112,9 @@
         }
 
         @Override
-        public Bundle call(String callingPackage, String method, String request, Bundle args)
-                throws RemoteException {
-            return MockContentProvider.this.call(method, request, args);
+        public Bundle call(String callingPackage, String authority, String method, String request,
+                Bundle args) throws RemoteException {
+            return MockContentProvider.this.call(authority, method, request, args);
         }
 
         @Override
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index 112d7ee..fc2a464 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -80,7 +80,7 @@
     }
 
     @Override
-    public ContentProviderResult[] applyBatch(String callingPackage,
+    public ContentProviderResult[] applyBatch(String callingPackage, String authority,
             ArrayList<ContentProviderOperation> operations) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
@@ -103,8 +103,8 @@
     }
 
     @Override
-    public Bundle call(String callingPackage, String method, String request, Bundle args)
-            throws RemoteException {
+    public Bundle call(String callingPackage, String authority, String method, String request,
+            Bundle args) throws RemoteException {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml
index d62ef9e..ca25386 100644
--- a/tests/FrameworkPerf/AndroidManifest.xml
+++ b/tests/FrameworkPerf/AndroidManifest.xml
@@ -13,7 +13,8 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <service android:name="SchedulerService">
+        <service android:name="SchedulerService"
+            android:foregroundServiceType="sync">
         </service>
         <service android:name="TestService" android:process=":test">
         </service>
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java b/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java
index fc3f390..d4cbbf9 100644
--- a/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/SchedulerService.java
@@ -17,16 +17,22 @@
 package com.android.frameworkperf;
 
 import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
 
 public class SchedulerService extends Service {
+    private static final String NOTIFICATION_CHANNEL_ID = SchedulerService.class.getSimpleName();
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
-        Notification status = new Notification.Builder(this)
+        getSystemService(NotificationManager.class).createNotificationChannel(
+                new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
+                        NotificationManager.IMPORTANCE_DEFAULT));
+        Notification status = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
                 .setSmallIcon(R.drawable.stat_happy)
                 .setWhen(System.currentTimeMillis())
                 .setContentTitle("Scheduler Test running")
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 132135d..9d1edbf 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -51,7 +51,6 @@
     liblog \
     liblzma \
     libnativehelper \
-    libnetdaidl \
     libpackagelistparser \
     libpcre2 \
     libselinux \
@@ -93,7 +92,6 @@
   liblog \
   libcutils \
   libnativehelper \
-  libnetdaidl \
   netd_aidl_interface-cpp
 
 LOCAL_STATIC_LIBRARIES := \
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index b40921f..50aef1d 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -24,9 +24,9 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
 import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
@@ -46,7 +46,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
 
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -457,6 +456,62 @@
         assertEquals(nc1, nc2);
     }
 
+    @Test
+    public void testSetNetworkSpecifierOnMultiTransportNc() {
+        // Sequence 1: Transport + Transport + NetworkSpecifier
+        NetworkCapabilities nc1 = new NetworkCapabilities();
+        nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI);
+        try {
+            nc1.setNetworkSpecifier(new StringNetworkSpecifier("specs"));
+            fail("Cannot set NetworkSpecifier on a NetworkCapability with multiple transports!");
+        } catch (IllegalStateException expected) {
+            // empty
+        }
+
+        // Sequence 2: Transport + NetworkSpecifier + Transport
+        NetworkCapabilities nc2 = new NetworkCapabilities();
+        nc2.addTransportType(TRANSPORT_CELLULAR).setNetworkSpecifier(
+                new StringNetworkSpecifier("specs"));
+        try {
+            nc2.addTransportType(TRANSPORT_WIFI);
+            fail("Cannot set NetworkSpecifier on a NetworkCapability with multiple transports!");
+        } catch (IllegalStateException expected) {
+            // empty
+        }
+    }
+
+    @Test
+    public void testSetTransportInfoOnMultiTransportNc() {
+        // Sequence 1: Transport + Transport + TransportInfo
+        NetworkCapabilities nc1 = new NetworkCapabilities();
+        nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI)
+                .setTransportInfo(new TransportInfo() {});
+
+        // Sequence 2: Transport + NetworkSpecifier + Transport
+        NetworkCapabilities nc2 = new NetworkCapabilities();
+        nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TransportInfo() {})
+                .addTransportType(TRANSPORT_WIFI);
+    }
+
+    @Test
+    public void testCombineTransportInfo() {
+        NetworkCapabilities nc1 = new NetworkCapabilities();
+        nc1.setTransportInfo(new TransportInfo() {
+            // empty
+        });
+        NetworkCapabilities nc2 = new NetworkCapabilities();
+        nc2.setTransportInfo(new TransportInfo() {
+            // empty
+        });
+
+        try {
+            nc1.combineCapabilities(nc2);
+            fail("Should not be able to combine NetworkCaabilities which contain TransportInfos");
+        } catch (IllegalStateException expected) {
+            // empty
+        }
+    }
+
     private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) {
         Parcel p = Parcel.obtain();
         netCap.writeToParcel(p, /* flags */ 0);
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index d6dbf5a..1e3a49b 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -448,22 +448,58 @@
     }
 
     @Test
-    public void testWithoutUid() throws Exception {
-        final NetworkStats before = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 64L, 4L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
+    public void testRemoveUids() throws Exception {
+        final NetworkStats before = new NetworkStats(TEST_START, 3);
 
-        final NetworkStats after = before.withoutUids(new int[] { 100 });
-        assertEquals(6, before.size());
-        assertEquals(2, after.size());
-        assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
-                DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
-        assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
-                DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
+        // Test 0 item stats.
+        NetworkStats after = before.clone();
+        after.removeUids(new int[0]);
+        assertEquals(0, after.size());
+        after.removeUids(new int[] {100});
+        assertEquals(0, after.size());
+
+        // Test 1 item stats.
+        before.addValues(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
+        after = before.clone();
+        after.removeUids(new int[0]);
+        assertEquals(1, after.size());
+        assertValues(after, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+        after.removeUids(new int[] {99});
+        assertEquals(0, after.size());
+
+        // Append remaining test items.
+        before.addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
+        assertEquals(7, before.size());
+
+        // Test remove with empty uid list.
+        after = before.clone();
+        after.removeUids(new int[0]);
+        assertValues(after.getTotalIncludingTags(null), 127L, 254L, 0L, 4L, 40L);
+
+        // Test remove uids don't exist in stats.
+        after.removeUids(new int[] {98, 0, Integer.MIN_VALUE, Integer.MAX_VALUE});
+        assertValues(after.getTotalIncludingTags(null), 127L, 254L, 0L, 4L, 40L);
+
+        // Test remove all uids.
+        after.removeUids(new int[] {99, 100, 100, 101});
+        assertEquals(0, after.size());
+
+        // Test remove in the middle.
+        after = before.clone();
+        after.removeUids(new int[] {100});
+        assertEquals(3, after.size());
+        assertValues(after, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+        assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 32L, 4L, 0L, 0L, 0L);
+        assertValues(after, 2, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 64L, 2L, 0L, 0L, 0L);
     }
 
     @Test
diff --git a/tests/net/java/android/net/ip/IpClientTest.java b/tests/net/java/android/net/ip/IpClientTest.java
index 5a8d2cd..cba3c65 100644
--- a/tests/net/java/android/net/ip/IpClientTest.java
+++ b/tests/net/java/android/net/ip/IpClientTest.java
@@ -81,7 +81,7 @@
     private static final int TEST_IFINDEX = 1001;
     // See RFC 7042#section-2.1.2 for EUI-48 documentation values.
     private static final MacAddress TEST_MAC = MacAddress.fromString("00:00:5E:00:53:01");
-    private static final int TEST_TIMEOUT_MS = 200;
+    private static final int TEST_TIMEOUT_MS = 400;
 
     @Mock private Context mContext;
     @Mock private INetworkManagementService mNMService;
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
index 2291e5a..4ffcf68 100755
--- a/tools/hiddenapi/exclude.sh
+++ b/tools/hiddenapi/exclude.sh
@@ -11,6 +11,7 @@
   android.system \
   com.android.bouncycastle \
   com.android.conscrypt \
+  com.android.i18n.phonenumbers \
   com.android.okhttp \
   com.sun \
   dalvik \
diff --git a/tools/processors/view_inspector/Android.bp b/tools/processors/view_inspector/Android.bp
new file mode 100644
index 0000000..ca6b3c4
--- /dev/null
+++ b/tools/processors/view_inspector/Android.bp
@@ -0,0 +1,27 @@
+java_library_host {
+    name: "view-inspector-annotation-processor",
+
+    srcs: ["src/java/**/*.java"],
+    java_resource_dirs: ["src/resources"],
+
+    static_libs: [
+    	"javapoet",
+    ],
+
+    use_tools_jar: true,
+}
+
+java_test_host {
+    name: "view-inspector-annotation-processor-test",
+
+    srcs: ["test/java/**/*.java"],
+    java_resource_dirs: ["test/resources"],
+
+    static_libs: [
+        "guava",
+        "junit",
+        "view-inspector-annotation-processor",
+    ],
+
+    test_suites: ["general-tests"],
+}
diff --git a/tools/processors/view_inspector/TEST_MAPPING b/tools/processors/view_inspector/TEST_MAPPING
new file mode 100644
index 0000000..a91b5b4
--- /dev/null
+++ b/tools/processors/view_inspector/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "view-inspector-annotation-processor-test"
+    }
+  ]
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
new file mode 100644
index 0000000..f157949
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/AnnotationUtils.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import java.util.Map;
+import java.util.Optional;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+/**
+ * Utilities for working with {@link AnnotationMirror}.
+ */
+final class AnnotationUtils {
+    private final Elements mElementUtils;
+    private final Types mTypeUtils;
+
+    AnnotationUtils(ProcessingEnvironment processingEnv) {
+        mElementUtils = processingEnv.getElementUtils();
+        mTypeUtils = processingEnv.getTypeUtils();
+    }
+
+    /**
+     * Get a {@link AnnotationMirror} specified by name from an {@link Element}.
+     *
+     * @param qualifiedName The fully qualified name of the annotation to search for
+     * @param element The element to search for annotations on
+     * @return The mirror of the requested annotation
+     * @throws ProcessingException If there is not exactly one of the requested annotation.
+     */
+    AnnotationMirror exactlyOneMirror(String qualifiedName, Element element) {
+        final Element targetTypeElment = mElementUtils.getTypeElement(qualifiedName);
+        final TypeMirror targetType = targetTypeElment.asType();
+        AnnotationMirror result = null;
+
+        for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
+            final TypeMirror annotationType = annotation.getAnnotationType().asElement().asType();
+            if (mTypeUtils.isSameType(annotationType, targetType)) {
+                if (result == null) {
+                    result = annotation;
+                } else {
+                    final String message = String.format(
+                            "Element had multiple instances of @%s, expected exactly one",
+                            targetTypeElment.getSimpleName());
+
+                    throw new ProcessingException(message, element, annotation);
+                }
+            }
+        }
+
+        if (result == null) {
+            final String message = String.format(
+                    "Expected an @%s annotation, found none", targetTypeElment.getSimpleName());
+            throw new ProcessingException(message, element);
+        } else {
+            return result;
+        }
+    }
+
+    /**
+     * Extract a string-valued property from an {@link AnnotationMirror}.
+     *
+     * @param propertyName The name of the requested property
+     * @param annotationMirror The mirror to search for the property
+     * @return The String value of the annotation or null
+     */
+    Optional<String> stringProperty(String propertyName, AnnotationMirror annotationMirror) {
+        final AnnotationValue value = valueByName(propertyName, annotationMirror);
+        if (value != null) {
+            return Optional.of((String) value.getValue());
+        } else {
+            return Optional.empty();
+        }
+    }
+
+
+    /**
+     * Extract a {@link AnnotationValue} from a mirror by string property name.
+     *
+     * @param propertyName The name of the property requested property
+     * @param annotationMirror
+     * @return
+     */
+    AnnotationValue valueByName(String propertyName, AnnotationMirror annotationMirror) {
+        final Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap =
+                annotationMirror.getElementValues();
+
+        for (ExecutableElement method : valueMap.keySet()) {
+            if (method.getSimpleName().contentEquals(propertyName)) {
+                return valueMap.get(method);
+            }
+        }
+
+        return null;
+    }
+
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
new file mode 100644
index 0000000..f0b0ff6
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
@@ -0,0 +1,51 @@
+/*
+ * 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.processor.view.inspector;
+
+import com.squareup.javapoet.ClassName;
+
+import java.util.Optional;
+
+/**
+ * Model of an inspectable class derived from annotations.
+ *
+ * This class does not use any {javax.lang.model} objects to facilitate building models for testing
+ * {@link InspectionCompanionGenerator}.
+ */
+public final class InspectableClassModel {
+    private final ClassName mClassName;
+    private Optional<String> mNodeName = Optional.empty();
+
+    /**
+     * @param className The name of the modeled class
+     */
+    public InspectableClassModel(ClassName className) {
+        mClassName = className;
+    }
+
+    public ClassName getClassName() {
+        return mClassName;
+    }
+
+    public Optional<String> getNodeName() {
+        return mNodeName;
+    }
+
+    public void setNodeName(Optional<String> nodeName) {
+        mNodeName = nodeName;
+    }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
new file mode 100644
index 0000000..a186a82
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableNodeNameProcessor.java
@@ -0,0 +1,76 @@
+/*
+ * 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.processor.view.inspector;
+
+import java.util.Optional;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+
+/**
+ * Process {InspectableNodeName} annotations
+ *
+ * @see android.view.inspector.InspectableNodeName
+ */
+public final class InspectableNodeNameProcessor implements ModelProcessor {
+    private final String mQualifiedName;
+    private final ProcessingEnvironment mProcessingEnv;
+    private final AnnotationUtils mAnnotationUtils;
+
+    /**
+     * @param annotationQualifiedName The qualified name of the annotation to process
+     * @param processingEnv The processing environment from the parent processor
+     */
+    public InspectableNodeNameProcessor(
+            String annotationQualifiedName,
+            ProcessingEnvironment processingEnv) {
+        mQualifiedName = annotationQualifiedName;
+        mProcessingEnv = processingEnv;
+        mAnnotationUtils = new AnnotationUtils(processingEnv);
+    }
+
+    /**
+     * Set the node name on the model if one is supplied.
+     *
+     * If the model already has a different node name, the node name will not be updated, and
+     * the processor will print an error the the messager.
+     *
+     * @param element The annotated element to operate on
+     * @param model The model this element should be merged into
+     */
+    @Override
+    public void process(Element element, InspectableClassModel model) {
+        try {
+            final AnnotationMirror mirror =
+                    mAnnotationUtils.exactlyOneMirror(mQualifiedName, element);
+            final Optional<String> nodeName = mAnnotationUtils.stringProperty("value", mirror);
+
+            if (!model.getNodeName().isPresent() || model.getNodeName().equals(nodeName)) {
+                model.setNodeName(nodeName);
+            } else {
+                final String message = String.format(
+                        "Node name was already set to \"%s\", refusing to change it to \"%s\".",
+                        model.getNodeName().get(),
+                        nodeName);
+                throw new ProcessingException(message, element, mirror);
+            }
+        } catch (ProcessingException processingException) {
+            processingException.print(mProcessingEnv.getMessager());
+        }
+    }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
new file mode 100644
index 0000000..fe0153d
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
@@ -0,0 +1,181 @@
+/*
+ * 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.processor.view.inspector;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeSpec;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import javax.annotation.processing.Filer;
+import javax.lang.model.element.Modifier;
+
+/**
+ * Generates a source file defining a {@link android.view.inspector.InspectionCompanion}.
+ */
+public final class InspectionCompanionGenerator {
+    private final Filer mFiler;
+    private final Class mRequestingClass;
+
+    /**
+     * @param filer A filer to write the generated source to
+     * @param requestingClass A class object representing the class that invoked the generator
+     */
+    public InspectionCompanionGenerator(final Filer filer, final Class requestingClass) {
+        mFiler = filer;
+        mRequestingClass = requestingClass;
+    }
+
+    /**
+     * Generate and write an inspection companion.
+     *
+     * @param model The model to generated
+     * @throws IOException From the Filer
+     */
+    public void generate(InspectableClassModel model) throws IOException {
+        generateFile(model).writeTo(mFiler);
+    }
+
+    /**
+     * Generate a {@link JavaFile} from a model.
+     *
+     * This is package-public for testing.
+     *
+     * @param model The model to generate from
+     * @return A generated file of an {@link android.view.inspector.InspectionCompanion}
+     */
+    JavaFile generateFile(InspectableClassModel model) {
+        return JavaFile
+                .builder(model.getClassName().packageName(), generateTypeSpec(model))
+                .indent("    ")
+                .build();
+    }
+
+    /**
+     * Generate a {@link TypeSpec} for the {@link android.view.inspector.InspectionCompanion}
+     * for the supplied model.
+     *
+     * @param model The model to generate from
+     * @return A TypeSpec of the inspection companion
+     */
+    private TypeSpec generateTypeSpec(InspectableClassModel model) {
+        TypeSpec.Builder builder = TypeSpec
+                .classBuilder(generateClassName(model))
+                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+                .addSuperinterface(ParameterizedTypeName.get(
+                        ClassName.get("android.view.inspector", "InspectionCompanion"),
+                        model.getClassName()))
+                .addJavadoc("Inspection companion for {@link $T}.\n\n", model.getClassName())
+                .addJavadoc("Generated by {@link $T}\n", getClass())
+                .addJavadoc("on behalf of {@link $T}.\n", mRequestingClass)
+                .addMethod(generateMapProperties(model))
+                .addMethod(generateReadProperties(model));
+
+        generateGetNodeName(model).ifPresent(builder::addMethod);
+
+        return builder.build();
+    }
+
+    /**
+     * Generate a method definition for
+     * {@link android.view.inspector.InspectionCompanion#getNodeName()}, if needed.
+     *
+     * If {@link InspectableClassModel#getNodeName()} is empty, This method returns an empty
+     * optional, otherwise, it generates a simple method that returns the string value of the
+     * node name.
+     *
+     * @param model The model to generate from
+     * @return The method definition or an empty Optional
+     */
+    private Optional<MethodSpec> generateGetNodeName(InspectableClassModel model) {
+        return model.getNodeName().map(nodeName -> MethodSpec.methodBuilder("getNodeName")
+                .addAnnotation(Override.class)
+                .addModifiers(Modifier.PUBLIC)
+                .returns(String.class)
+                .addStatement("return $S", nodeName)
+                .build());
+    }
+
+    /**
+     * Generate a method definition for
+     * {@link android.view.inspector.InspectionCompanion#mapProperties(
+     * android.view.inspector.PropertyMapper)}.
+     *
+     * TODO: implement
+     *
+     * @param model The model to generate from
+     * @return The method definition
+     */
+    private MethodSpec generateMapProperties(InspectableClassModel model) {
+        final ClassName propertyMapper = ClassName.get(
+                "android.view.inspector", "PropertyMapper");
+
+        return MethodSpec.methodBuilder("mapProperties")
+                .addAnnotation(Override.class)
+                .addModifiers(Modifier.PUBLIC)
+                .addParameter(propertyMapper, "propertyMapper")
+                // TODO: add method body
+                .build();
+    }
+
+    /**
+     * Generate a method definition for
+     * {@link android.view.inspector.InspectionCompanion#readProperties(
+     * Object, android.view.inspector.PropertyReader)}.
+     *
+     * TODO: implement
+     *
+     * @param model The model to generate from
+     * @return The method definition
+     */
+    private MethodSpec generateReadProperties(InspectableClassModel model) {
+        final ClassName propertyReader = ClassName.get(
+                "android.view.inspector", "PropertyReader");
+
+        return MethodSpec.methodBuilder("readProperties")
+                .addAnnotation(Override.class)
+                .addModifiers(Modifier.PUBLIC)
+                .addParameter(model.getClassName(), "inspectable")
+                .addParameter(propertyReader, "propertyReader")
+                // TODO: add method body
+                .build();
+    }
+
+    /**
+     * Generate the final class name for the inspection companion from the model's class name.
+     *
+     * The generated class is added to the same package as the source class. If the class in the
+     * model is a nested class, the nested class names are joined with {"$"}. The suffix
+     * {"$$InspectionCompanion"} is always added the the generated name. E.g.: For modeled class
+     * {com.example.Outer.Inner}, the generated class name will be
+     * {com.example.Outer$Inner$$InspectionCompanion}.
+     *
+     * @param model The model to generate from
+     * @return A class name for the generated inspection companion class
+     */
+    private ClassName generateClassName(final InspectableClassModel model) {
+        final ClassName className = model.getClassName();
+
+        return ClassName.get(
+                className.packageName(),
+                String.join("$", className.simpleNames()) + "$$InspectionCompanion");
+    }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
new file mode 100644
index 0000000..3ffcff8
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ModelProcessor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.processor.view.inspector;
+
+import javax.lang.model.element.Element;
+
+/**
+ * An interface for annotation processors that operate on a single element and a class model.
+ */
+public interface ModelProcessor {
+    /**
+     * Process the supplied element, mutating the model as needed.
+     *
+     * @param element The annotated element to operate on
+     * @param model The model this element should be merged into
+     */
+    void process(Element element, InspectableClassModel model);
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
new file mode 100644
index 0000000..e531b67
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/PlatformInspectableProcessor.java
@@ -0,0 +1,161 @@
+/*
+ * 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.processor.view.inspector;
+
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.squareup.javapoet.ClassName;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+
+
+/**
+ * An annotation processor for the platform inspectable annotations.
+ *
+ * It mostly delegates to {@link ModelProcessor} and {@link InspectionCompanionGenerator}. This
+ * modular architecture allows the core generation code to be reused for comparable annotations
+ * outside the platform, such as in AndroidX.
+ *
+ * @see android.view.inspector.InspectableNodeName
+ * @see android.view.inspector.InspectableProperty
+ */
+@SupportedAnnotationTypes({
+        PlatformInspectableProcessor.NODE_NAME_QUALIFIED_NAME
+})
+public final class PlatformInspectableProcessor extends AbstractProcessor {
+    static final String NODE_NAME_QUALIFIED_NAME =
+            "android.view.inspector.InspectableNodeName";
+
+    @Override
+    public SourceVersion getSupportedSourceVersion() {
+        return SourceVersion.latest();
+    }
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        final Map<String, InspectableClassModel> modelMap = new HashMap<>();
+
+        for (TypeElement annotation : annotations) {
+            if (annotation.getQualifiedName().contentEquals(NODE_NAME_QUALIFIED_NAME)) {
+                runModelProcessor(
+                        roundEnv.getElementsAnnotatedWith(annotation),
+                        new InspectableNodeNameProcessor(NODE_NAME_QUALIFIED_NAME, processingEnv),
+                        modelMap);
+
+
+            } else {
+                fail("Unexpected annotation type", annotation);
+            }
+        }
+
+        final InspectionCompanionGenerator generator =
+                new InspectionCompanionGenerator(processingEnv.getFiler(), getClass());
+
+        for (InspectableClassModel model : modelMap.values()) {
+            try {
+                generator.generate(model);
+            } catch (IOException ioException) {
+                fail(String.format(
+                        "Unable to generate inspection companion for %s due to %s",
+                        model.getClassName().toString(),
+                        ioException.getMessage()));
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Run a {@link ModelProcessor} for a set of elements
+     *
+     * @param elements Elements to process, should be annotated correctly
+     * @param processor The processor to use
+     * @param modelMap A map of qualified class names to models
+     */
+    private void runModelProcessor(
+            Set<? extends Element> elements,
+            ModelProcessor processor,
+            Map<String, InspectableClassModel> modelMap) {
+        for (Element element : elements) {
+            final Optional<TypeElement> classElement = enclosingClassElement(element);
+
+            if (!classElement.isPresent()) {
+                fail("Element not contained in a class", element);
+                break;
+            }
+
+            final InspectableClassModel model = modelMap.computeIfAbsent(
+                    classElement.get().getQualifiedName().toString(),
+                    k -> new InspectableClassModel(ClassName.get(classElement.get())));
+
+            processor.process(element, model);
+        }
+    }
+
+    /**
+     * Get the nearest enclosing class if there is one.
+     *
+     * If {@param element} represents a class, it will be returned wrapped in an optional.
+     *
+     * @param element An element to search from
+     * @return A TypeElement of the nearest enclosing class or an empty optional
+     */
+    private static Optional<TypeElement> enclosingClassElement(Element element) {
+        Element cursor = element;
+
+        while (cursor != null) {
+            if (cursor.getKind() == ElementKind.CLASS) {
+                return Optional.of((TypeElement) cursor);
+            }
+
+            cursor = cursor.getEnclosingElement();
+        }
+
+        return Optional.empty();
+    }
+
+    /**
+     * Print message and fail the build.
+     *
+     * @param message Message to print
+     */
+    private void fail(String message) {
+        processingEnv.getMessager().printMessage(ERROR, message);
+    }
+
+    /**
+     * Print message and fail the build.
+     *
+     * @param message Message to print
+     * @param element The element that failed
+     */
+    private void fail(String message, Element element) {
+        processingEnv.getMessager().printMessage(ERROR, message, element);
+    }
+}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
new file mode 100644
index 0000000..6360e0a
--- /dev/null
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/ProcessingException.java
@@ -0,0 +1,78 @@
+/*
+ * 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.processor.view.inspector;
+
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+
+/**
+ * Internal exception used to signal an error processing an annotation.
+ */
+final class ProcessingException extends RuntimeException {
+    private final Element mElement;
+    private final AnnotationMirror mAnnotationMirror;
+    private final AnnotationValue mAnnotationValue;
+
+    ProcessingException(String message) {
+        this(message, null, null, null);
+    }
+
+    ProcessingException(String message, Element element) {
+        this(message, element, null, null);
+    }
+
+    ProcessingException(String message, Element element, AnnotationMirror annotationMirror) {
+        this(message, element, annotationMirror, null);
+    }
+
+    ProcessingException(
+            String message,
+            Element element,
+            AnnotationMirror annotationMirror,
+            AnnotationValue annotationValue) {
+        super(message);
+        mElement = element;
+        mAnnotationMirror = annotationMirror;
+        mAnnotationValue = annotationValue;
+    }
+
+    /**
+     * Prints the exception to a Messager.
+     *
+     * @param messager A Messager to print to
+     */
+    void print(Messager messager) {
+        if (mElement != null) {
+            if (mAnnotationMirror != null) {
+                if (mAnnotationValue != null) {
+                    messager.printMessage(
+                            ERROR, getMessage(), mElement, mAnnotationMirror, mAnnotationValue);
+                } else {
+                    messager.printMessage(ERROR, getMessage(), mElement, mAnnotationMirror);
+                }
+            } else {
+                messager.printMessage(ERROR, getMessage(), mElement);
+            }
+        } else {
+            messager.printMessage(ERROR, getMessage());
+        }
+    }
+}
diff --git a/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..fa4f71f
--- /dev/null
+++ b/tools/processors/view_inspector/src/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+android.processor.inspector.view.PlatformInspectableProcessor
diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
new file mode 100644
index 0000000..c02b0bd
--- /dev/null
+++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.view.inspector;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.TestCase.fail;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import com.squareup.javapoet.ClassName;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Optional;
+
+/**
+ * Tests for {@link InspectionCompanionGenerator}
+ */
+public class InspectionCompanionGeneratorTest {
+    private static final String RESOURCE_PATH_TEMPLATE =
+            "android/processor/view/inspector/InspectionCompanionGeneratorTest/%s.java.txt";
+    private static final ClassName TEST_CLASS_NAME =
+            ClassName.get("com.android.inspectable", "TestInspectable");
+    private InspectableClassModel mModel;
+    private InspectionCompanionGenerator mGenerator;
+
+    @Before
+    public void setup() {
+        mModel = new InspectableClassModel(TEST_CLASS_NAME);
+        mGenerator = new InspectionCompanionGenerator(null, getClass());
+    }
+
+    @Test
+    public void testNodeName() {
+        mModel.setNodeName(Optional.of("NodeName"));
+        assertGeneratedFileEquals("NodeName");
+    }
+
+    @Test
+    public void testNestedClass() {
+        mModel = new InspectableClassModel(
+                ClassName.get("com.android.inspectable", "Outer", "Inner"));
+        assertGeneratedFileEquals("NestedClass");
+    }
+
+    private void assertGeneratedFileEquals(String fileName) {
+        assertEquals(
+                loadTextResource(String.format(RESOURCE_PATH_TEMPLATE, fileName)),
+                mGenerator.generateFile(mModel).toString());
+    }
+
+    private String loadTextResource(String path) {
+        try {
+            final URL url = Resources.getResource(path);
+            assertNotNull(String.format("Resource file not found: %s", path), url);
+            return Resources.toString(url, Charsets.UTF_8);
+        } catch (IOException e) {
+            fail(e.getMessage());
+            return null;
+        }
+    }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
new file mode 100644
index 0000000..e5fb6a2
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NestedClass.java.txt
@@ -0,0 +1,22 @@
+package com.android.inspectable;
+
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+
+/**
+ * Inspection companion for {@link Outer.Inner}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class Outer$Inner$$InspectionCompanion implements InspectionCompanion<Outer.Inner> {
+    @Override
+    public void mapProperties(PropertyMapper propertyMapper) {
+    }
+
+    @Override
+    public void readProperties(Outer.Inner inspectable, PropertyReader propertyReader) {
+    }
+}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
new file mode 100644
index 0000000..a334f50
--- /dev/null
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/NodeName.java.txt
@@ -0,0 +1,28 @@
+package com.android.inspectable;
+
+import android.view.inspector.InspectionCompanion;
+import android.view.inspector.PropertyMapper;
+import android.view.inspector.PropertyReader;
+import java.lang.Override;
+import java.lang.String;
+
+/**
+ * Inspection companion for {@link TestInspectable}.
+ *
+ * Generated by {@link android.processor.view.inspector.InspectionCompanionGenerator}
+ * on behalf of {@link android.processor.view.inspector.InspectionCompanionGeneratorTest}.
+ */
+public final class TestInspectable$$InspectionCompanion implements InspectionCompanion<TestInspectable> {
+    @Override
+    public void mapProperties(PropertyMapper propertyMapper) {
+    }
+
+    @Override
+    public void readProperties(TestInspectable inspectable, PropertyReader propertyReader) {
+    }
+
+    @Override
+    public String getNodeName() {
+        return "NodeName";
+    }
+}
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 88b7e2e..5192a0e 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -264,6 +264,10 @@
                                  chainField.name.c_str(), chainField.name.c_str());
                     }
                 }
+            } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+                fprintf(out, ", %s arg%d, size_t arg%d_length",
+                        cpp_type_name(*arg), argIndex, argIndex);
+
             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
                 fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
                              "const std::map<int, int64_t>& arg%d_2, "
@@ -343,6 +347,10 @@
                     fprintf(out, "    }\n");
 
                     fprintf(out, "    event.end();\n\n");
+            } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+                fprintf(out,
+                        "    event.AppendCharArray(arg%d, arg%d_length);\n",
+                        argIndex, argIndex);
             } else {
                 if (*arg == JAVA_TYPE_STRING) {
                     fprintf(out, "    if (arg%d == NULL) {\n", argIndex);
@@ -383,12 +391,17 @@
                                 chainField.name.c_str(), chainField.name.c_str());
                    }
                }
+           } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+               fprintf(out, ", %s arg%d, size_t arg%d_length",
+                       cpp_type_name(*arg), argIndex, argIndex);
+
            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-                fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
-                             "const std::map<int, int64_t>& arg%d_2, "
-                             "const std::map<int, char const*>& arg%d_3, "
-                             "const std::map<int, float>& arg%d_4",
-                             argIndex, argIndex, argIndex, argIndex);
+               fprintf(out,
+                       ", const std::map<int, int32_t>& arg%d_1, "
+                       "const std::map<int, int64_t>& arg%d_2, "
+                       "const std::map<int, char const*>& arg%d_3, "
+                       "const std::map<int, float>& arg%d_4",
+                       argIndex, argIndex, argIndex, argIndex);
            } else {
                fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
            }
@@ -415,9 +428,11 @@
                                 chainField.name.c_str(), chainField.name.c_str());
                    }
                }
-           } else  if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-                fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4",
-                        argIndex, argIndex, argIndex, argIndex);
+           } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+               fprintf(out, ", arg%d, arg%d_length", argIndex, argIndex);
+           } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+               fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex,
+                       argIndex, argIndex, argIndex);
            } else {
                fprintf(out, ", arg%d", argIndex);
            }
@@ -580,6 +595,11 @@
                          field->name.c_str(),
                          field->name.c_str(),
                          field->name.c_str());
+        } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
+            fprintf(out, ", %s %s, size_t %s_length",
+                    cpp_type_name(field->javaType), field->name.c_str(),
+                    field->name.c_str());
+
         } else {
             fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
         }
@@ -613,6 +633,9 @@
                              "const std::map<int, char const*>& arg%d_3, "
                              "const std::map<int, float>& arg%d_4",
                              argIndex, argIndex, argIndex, argIndex);
+            } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+                fprintf(out, ", %s arg%d, size_t arg%d_length",
+                        cpp_type_name(*arg), argIndex, argIndex);
             } else {
                 fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
             }
@@ -1128,6 +1151,7 @@
                 hadStringOrChain = true;
                 fprintf(out, "    jbyte* jbyte_array%d;\n", argIndex);
                 fprintf(out, "    const char* str%d;\n", argIndex);
+                fprintf(out, "    int str%d_length = 0;\n", argIndex);
                 fprintf(out,
                         "    if (arg%d != NULL && env->GetArrayLength(arg%d) > "
                         "0) {\n",
@@ -1137,6 +1161,9 @@
                         "env->GetByteArrayElements(arg%d, NULL);\n",
                         argIndex, argIndex);
                 fprintf(out,
+                        "        str%d_length = env->GetArrayLength(arg%d);\n",
+                        argIndex, argIndex);
+                fprintf(out,
                         "        str%d = "
                         "reinterpret_cast<char*>(env->GetByteArrayElements(arg%"
                         "d, NULL));\n",
@@ -1224,6 +1251,10 @@
                                               ? "str"
                                               : "arg";
                 fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+
+                if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+                    fprintf(out, ", %s%d_length", argName, argIndex);
+                }
             }
             argIndex++;
         }