Merge "Keep initial override configuration from display settings"
diff --git a/Android.bp b/Android.bp
index 7e038ce8..25e738c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -92,6 +92,7 @@
         "core/java/android/app/IWallpaperManagerCallback.aidl",
         "core/java/android/app/admin/IDeviceAdminService.aidl",
         "core/java/android/app/admin/IDevicePolicyManager.aidl",
+        "core/java/android/app/admin/StartInstallingUpdateCallback.aidl",
         "core/java/android/app/trust/IStrongAuthTracker.aidl",
         "core/java/android/app/trust/ITrustManager.aidl",
         "core/java/android/app/trust/ITrustListener.aidl",
@@ -295,6 +296,7 @@
         "core/java/android/service/notification/IConditionListener.aidl",
         "core/java/android/service/notification/IConditionProvider.aidl",
         "core/java/android/service/settings/suggestions/ISuggestionService.aidl",
+        "core/java/android/service/sms/IFinancialSmsService.aidl",
         "core/java/android/service/vr/IPersistentVrStateCallbacks.aidl",
         "core/java/android/service/vr/IVrListener.aidl",
         "core/java/android/service/vr/IVrManager.aidl",
@@ -347,6 +349,7 @@
         "core/java/android/view/accessibility/IAccessibilityManagerClient.aidl",
         "core/java/android/view/autofill/IAutoFillManager.aidl",
         "core/java/android/view/autofill/IAutoFillManagerClient.aidl",
+        "core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
         "core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
         "core/java/android/view/intelligence/IIntelligenceManager.aidl",
         "core/java/android/view/IApplicationToken.aidl",
@@ -723,12 +726,15 @@
         "android.hardware.wifi-V1.0-java-constants",
         "android.hardware.radio-V1.0-java",
         "android.hardware.radio-V1.3-java",
+        "android.hardware.radio-V1.4-java",
         "android.hardware.usb.gadget-V1.0-java",
         "netd_aidl_interface-java",
     ],
 
-    // Loaded with System.loadLibrary by android.view.textclassifier
     required: [
+        // TODO: remove gps_debug when the build system propagates "required" properly.
+        "gps_debug.conf",
+        // Loaded with System.loadLibrary by android.view.textclassifier
         "libmedia2_jni",
     ],
 
@@ -858,7 +864,7 @@
 java_library {
     name: "ext",
     installable: true,
-    sdk_version: "core_current",
+    no_framework_libs: true,
     static_libs: [
         "libphonenumber-platform",
         "nist-sip",
@@ -1716,7 +1722,9 @@
         "core/java/android/annotation/RequiresPermission.java",
         "core/java/android/annotation/SdkConstant.java",
         "core/java/android/annotation/StringDef.java",
+        "core/java/android/annotation/TestApi.java",
         "core/java/android/annotation/UnsupportedAppUsage.java",
+        "core/java/com/android/internal/annotations/GuardedBy.java",
     ],
 }
 
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5936ee4..e731138 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,7 +5,9 @@
 
 strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
 
-hidden_api_txt_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+
+hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
 
 ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
 
diff --git a/api/current.txt b/api/current.txt
index e84bc8d..9d46863 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -159,9 +159,6 @@
     field public static final java.lang.String WRITE_CONTACTS = "android.permission.WRITE_CONTACTS";
     field public static final deprecated java.lang.String WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";
     field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES";
-    field public static final java.lang.String WRITE_MEDIA_AUDIO = "android.permission.WRITE_MEDIA_AUDIO";
-    field public static final java.lang.String WRITE_MEDIA_IMAGES = "android.permission.WRITE_MEDIA_IMAGES";
-    field public static final java.lang.String WRITE_MEDIA_VIDEO = "android.permission.WRITE_MEDIA_VIDEO";
     field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
     field public static final java.lang.String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS";
     field public static final java.lang.String WRITE_SYNC_SETTINGS = "android.permission.WRITE_SYNC_SETTINGS";
@@ -1319,6 +1316,7 @@
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
+    field public static final int supportsMultipleDisplays = 16844183; // 0x1010597
     field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
     field public static final int supportsRtl = 16843695; // 0x10103af
     field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
@@ -1499,6 +1497,7 @@
     field public static final int unselectedAlpha = 16843278; // 0x101020e
     field public static final int updatePeriodMillis = 16843344; // 0x1010250
     field public static final int use32bitAbi = 16844053; // 0x1010515
+    field public static final int useAppZygote = 16844184; // 0x1010598
     field public static final int useDefaultMargins = 16843641; // 0x1010379
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
     field public static final int useLevel = 16843167; // 0x101019f
@@ -3804,6 +3803,7 @@
     method public void overridePendingTransition(int, int);
     method public void postponeEnterTransition();
     method public void recreate();
+    method public void registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
     method public void registerForContextMenu(android.view.View);
     method public boolean releaseInstance();
     method public final deprecated void removeDialog(int);
@@ -3881,6 +3881,7 @@
     method public deprecated void stopManagingCursor(android.database.Cursor);
     method public void takeKeyEvents(boolean);
     method public void triggerSearch(java.lang.String, android.os.Bundle);
+    method public void unregisterActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
     method public void unregisterForContextMenu(android.view.View);
     field public static final int DEFAULT_KEYS_DIALER = 1; // 0x1
     field public static final int DEFAULT_KEYS_DISABLE = 0; // 0x0
@@ -5229,6 +5230,7 @@
     ctor public Notification(android.os.Parcel);
     method public android.app.Notification clone();
     method public int describeContents();
+    method public boolean getAllowSystemGeneratedContextualActions();
     method public android.app.PendingIntent getAppOverlayIntent();
     method public int getBadgeIconType();
     method public java.lang.String getChannelId();
@@ -5460,6 +5462,7 @@
     method public android.app.Notification.Style getStyle();
     method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
+    method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean);
     method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setBadgeIconType(int);
@@ -6352,6 +6355,7 @@
     method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
     method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
     method public boolean supportsAmbientMode();
+    method public boolean supportsMultipleDisplays();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
   }
@@ -6509,6 +6513,7 @@
   }
 
   public class DevicePolicyManager {
+    method public void addCrossProfileCalendarPackage(android.content.ComponentName, java.lang.String);
     method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int);
     method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
     method public int addOverrideApn(android.content.ComponentName, android.telephony.data.ApnSetting);
@@ -6538,6 +6543,7 @@
     method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
     method public boolean getCameraDisabled(android.content.ComponentName);
     method public deprecated java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
+    method public java.util.Set<java.lang.String> getCrossProfileCalendarPackages(android.content.ComponentName);
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
     method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
@@ -6598,6 +6604,7 @@
     method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
     method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
     method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, int);
+    method public void installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallUpdateCallback);
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isAffiliatedUser();
@@ -6625,6 +6632,7 @@
     method public int logoutUser(android.content.ComponentName);
     method public void reboot(android.content.ComponentName);
     method public void removeActiveAdmin(android.content.ComponentName);
+    method public boolean removeCrossProfileCalendarPackage(android.content.ComponentName, java.lang.String);
     method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
     method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
     method public boolean removeOverrideApn(android.content.ComponentName, int);
@@ -6840,6 +6848,16 @@
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
   }
 
+  public static abstract class DevicePolicyManager.InstallUpdateCallback {
+    ctor public DevicePolicyManager.InstallUpdateCallback();
+    method public void onInstallUpdateError(int, java.lang.String);
+    field public static final int UPDATE_ERROR_BATTERY_LOW = 5; // 0x5
+    field public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; // 0x4
+    field public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2; // 0x2
+    field public static final int UPDATE_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3; // 0x3
+  }
+
   public static abstract interface DevicePolicyManager.OnClearApplicationUserDataListener {
     method public abstract void onApplicationUserDataCleared(java.lang.String, boolean);
   }
@@ -10028,6 +10046,7 @@
     field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT";
     field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED";
     field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW";
+    field public static final java.lang.String ACTION_DEFINE = "android.intent.action.DEFINE";
     field public static final java.lang.String ACTION_DELETE = "android.intent.action.DELETE";
     field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW";
     field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK";
@@ -10118,6 +10137,7 @@
     field public static final java.lang.String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED";
     field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET";
     field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK";
+    field public static final java.lang.String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
     field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
     field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
     field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
@@ -11699,6 +11719,7 @@
     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 int flags;
     field public java.lang.String permission;
   }
@@ -13924,6 +13945,7 @@
   }
 
   public final class Insets {
+    method public static android.graphics.Insets add(android.graphics.Insets, android.graphics.Insets);
     method public static android.graphics.Insets of(int, int, int, int);
     method public static android.graphics.Insets of(android.graphics.Rect);
     field public static final android.graphics.Insets NONE;
@@ -14809,6 +14831,7 @@
     ctor public Typeface.CustomFallbackBuilder(android.graphics.fonts.FontFamily);
     method public android.graphics.Typeface.CustomFallbackBuilder addCustomFallback(android.graphics.fonts.FontFamily);
     method public android.graphics.Typeface build();
+    method public static int getMaxCustomFallbackCount();
     method public android.graphics.Typeface.CustomFallbackBuilder setStyle(android.graphics.fonts.FontStyle);
     method public android.graphics.Typeface.CustomFallbackBuilder setSystemFallback(java.lang.String);
   }
@@ -22628,7 +22651,7 @@
     method public abstract void onLocationChanged(android.location.Location);
     method public abstract void onProviderDisabled(java.lang.String);
     method public abstract void onProviderEnabled(java.lang.String);
-    method public abstract void onStatusChanged(java.lang.String, int, android.os.Bundle);
+    method public abstract deprecated void onStatusChanged(java.lang.String, int, android.os.Bundle);
   }
 
   public class LocationManager {
@@ -22640,7 +22663,7 @@
     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 void clearTestProviderStatus(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);
     method public java.lang.String getGnssHardwareModelName();
@@ -22677,7 +22700,7 @@
     method public boolean sendExtraCommand(java.lang.String, java.lang.String, android.os.Bundle);
     method public void setTestProviderEnabled(java.lang.String, boolean);
     method public void setTestProviderLocation(java.lang.String, android.location.Location);
-    method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
+    method public deprecated void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
     method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
     method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
     method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback);
@@ -22685,7 +22708,7 @@
     field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
     field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
     field public static final java.lang.String KEY_PROXIMITY_ENTERING = "entering";
-    field public static final java.lang.String KEY_STATUS_CHANGED = "status";
+    field public static final deprecated java.lang.String KEY_STATUS_CHANGED = "status";
     field public static final java.lang.String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
     field public static final java.lang.String NETWORK_PROVIDER = "network";
     field public static final java.lang.String PASSIVE_PROVIDER = "passive";
@@ -22704,9 +22727,9 @@
     method public boolean supportsAltitude();
     method public boolean supportsBearing();
     method public boolean supportsSpeed();
-    field public static final int AVAILABLE = 2; // 0x2
-    field public static final int OUT_OF_SERVICE = 0; // 0x0
-    field public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1
+    field public static final deprecated int AVAILABLE = 2; // 0x2
+    field public static final deprecated int OUT_OF_SERVICE = 0; // 0x0
+    field public static final deprecated int TEMPORARILY_UNAVAILABLE = 1; // 0x1
   }
 
   public abstract interface OnNmeaMessageListener {
@@ -22935,7 +22958,7 @@
     method public void adjustSuggestedStreamVolume(int, int, int);
     method public void adjustVolume(int, int);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
-    method public static int generateAudioSessionId();
+    method public int generateAudioSessionId();
     method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
     method public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
@@ -23444,6 +23467,7 @@
   }
 
   public class ExifInterface {
+    ctor public ExifInterface(java.io.File) throws java.io.IOException;
     ctor public ExifInterface(java.lang.String) throws java.io.IOException;
     ctor public ExifInterface(java.io.FileDescriptor) throws java.io.IOException;
     ctor public ExifInterface(java.io.InputStream) throws java.io.IOException;
@@ -23451,11 +23475,13 @@
     method public java.lang.String getAttribute(java.lang.String);
     method public double getAttributeDouble(java.lang.String, double);
     method public int getAttributeInt(java.lang.String, int);
+    method public long[] getAttributeRange(java.lang.String);
     method public boolean getLatLong(float[]);
     method public byte[] getThumbnail();
     method public android.graphics.Bitmap getThumbnailBitmap();
     method public byte[] getThumbnailBytes();
     method public long[] getThumbnailRange();
+    method public boolean hasAttribute(java.lang.String);
     method public boolean hasThumbnail();
     method public boolean isThumbnailCompressed();
     method public void saveAttributes() throws java.io.IOException;
@@ -24575,6 +24601,7 @@
     field public static final java.lang.String KEY_WIDTH = "width";
     field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
     field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3";
+    field public static final java.lang.String MIMETYPE_AUDIO_AC4 = "audio/ac4";
     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";
@@ -29117,15 +29144,18 @@
     method public android.net.wifi.WifiNetworkSuggestion buildNetworkSuggestion();
     method public android.net.wifi.WifiNetworkConfigBuilder setBssid(android.net.MacAddress);
     method public android.net.wifi.WifiNetworkConfigBuilder setBssidPattern(android.net.MacAddress, android.net.MacAddress);
-    method public android.net.wifi.WifiNetworkConfigBuilder setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
     method public android.net.wifi.WifiNetworkConfigBuilder setIsAppInteractionRequired();
+    method public android.net.wifi.WifiNetworkConfigBuilder setIsEnhancedOpen();
     method public android.net.wifi.WifiNetworkConfigBuilder setIsHiddenSsid();
     method public android.net.wifi.WifiNetworkConfigBuilder setIsMetered();
     method public android.net.wifi.WifiNetworkConfigBuilder setIsUserInteractionRequired();
     method public android.net.wifi.WifiNetworkConfigBuilder setPriority(int);
-    method public android.net.wifi.WifiNetworkConfigBuilder setPskPassphrase(java.lang.String);
     method public android.net.wifi.WifiNetworkConfigBuilder setSsid(java.lang.String);
     method public android.net.wifi.WifiNetworkConfigBuilder setSsidPattern(android.os.PatternMatcher);
+    method public android.net.wifi.WifiNetworkConfigBuilder setWpa2EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+    method public android.net.wifi.WifiNetworkConfigBuilder setWpa2Passphrase(java.lang.String);
+    method public android.net.wifi.WifiNetworkConfigBuilder setWpa3EnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+    method public android.net.wifi.WifiNetworkConfigBuilder setWpa3Passphrase(java.lang.String);
   }
 
   public final class WifiNetworkSuggestion implements android.os.Parcelable {
@@ -29462,7 +29492,10 @@
     method public void removeLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method public void removeServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method public void requestConnectionInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener);
+    method public void requestDiscoveryState(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DiscoveryStateListener);
     method public void requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener);
+    method public void requestNetworkInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.NetworkInfoListener);
+    method public void requestP2pState(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.P2pStateListener);
     method public void requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener);
     method public void setDnsSdResponseListeners(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener, android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener);
     method public void setServiceResponseListener(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ServiceResponseListener);
@@ -29507,6 +29540,10 @@
     method public abstract void onConnectionInfoAvailable(android.net.wifi.p2p.WifiP2pInfo);
   }
 
+  public static abstract interface WifiP2pManager.DiscoveryStateListener {
+    method public abstract void onDiscoveryStateAvailable(int);
+  }
+
   public static abstract interface WifiP2pManager.DnsSdServiceResponseListener {
     method public abstract void onDnsSdServiceAvailable(java.lang.String, java.lang.String, android.net.wifi.p2p.WifiP2pDevice);
   }
@@ -29519,6 +29556,14 @@
     method public abstract void onGroupInfoAvailable(android.net.wifi.p2p.WifiP2pGroup);
   }
 
+  public static abstract interface WifiP2pManager.NetworkInfoListener {
+    method public abstract void onNetworkInfoAvailable(android.net.NetworkInfo);
+  }
+
+  public static abstract interface WifiP2pManager.P2pStateListener {
+    method public abstract void onP2pStateAvailable(int);
+  }
+
   public static abstract interface WifiP2pManager.PeerListListener {
     method public abstract void onPeersAvailable(android.net.wifi.p2p.WifiP2pDeviceList);
   }
@@ -37370,6 +37415,17 @@
     field public static final java.lang.String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/radio";
   }
 
+  public static abstract interface MediaStore.DownloadColumns implements android.provider.MediaStore.MediaColumns {
+    field public static final java.lang.String DOWNLOAD_URI = "download_uri";
+    field public static final java.lang.String REFERER_URI = "referer_uri";
+  }
+
+  public static final class MediaStore.Downloads implements android.provider.MediaStore.DownloadColumns {
+    method public static android.net.Uri getContentUri(java.lang.String);
+    field public static final android.net.Uri EXTERNAL_CONTENT_URI;
+    field public static final android.net.Uri INTERNAL_CONTENT_URI;
+  }
+
   public static final class MediaStore.Files {
     ctor public MediaStore.Files();
     method public static android.net.Uri getContentUri(java.lang.String);
@@ -37461,7 +37517,9 @@
 
   public static class MediaStore.PendingParams {
     ctor public MediaStore.PendingParams(android.net.Uri, java.lang.String, java.lang.String);
+    method public void setDownloadUri(android.net.Uri);
     method public void setPrimaryDirectory(java.lang.String);
+    method public void setRefererUri(android.net.Uri);
     method public void setSecondaryDirectory(java.lang.String);
   }
 
@@ -42871,6 +42929,7 @@
     field public static final java.lang.String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
     field public static final java.lang.String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
     field public static final java.lang.String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
+    field public static final java.lang.String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
     field public static final java.lang.String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
     field public static final java.lang.String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool";
     field public static final java.lang.String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
@@ -43218,9 +43277,9 @@
 
   public class MbmsGroupCallSession implements java.lang.AutoCloseable {
     method public void close();
-    method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsGroupCallSessionCallback);
+    method public static android.telephony.MbmsGroupCallSession create(android.content.Context, int, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
     method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
-    method public android.telephony.mbms.GroupCall startGroupCall(java.util.concurrent.Executor, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+    method public android.telephony.mbms.GroupCall startGroupCall(long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, java.util.concurrent.Executor, android.telephony.mbms.GroupCallCallback);
   }
 
   public class MbmsStreamingSession implements java.lang.AutoCloseable {
@@ -43605,7 +43664,7 @@
     method public static int getDefaultVoiceSubscriptionId();
     method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
     method public static int getSlotIndex(int);
-    method public static int[] getSubscriptionIds(int);
+    method public int[] getSubscriptionIds(int);
     method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
     method public boolean isActiveSubscriptionId(int);
     method public boolean isNetworkRoaming(int);
@@ -44225,7 +44284,7 @@
   public class GroupCall implements java.lang.AutoCloseable {
     method public void close();
     method public long getTmgi();
-    method public void updateGroupCall(int[], int[]);
+    method public void updateGroupCall(java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
     field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
     field public static final int REASON_FREQUENCY_CONFLICT = 3; // 0x3
     field public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; // 0x6
@@ -44237,11 +44296,10 @@
     field public static final int STATE_STOPPED = 1; // 0x1
   }
 
-  public class GroupCallCallback {
-    ctor public GroupCallCallback();
-    method public void onBroadcastSignalStrengthUpdated(int);
-    method public void onError(int, java.lang.String);
-    method public void onGroupCallStateChanged(int, int);
+  public abstract interface GroupCallCallback {
+    method public abstract void onBroadcastSignalStrengthUpdated(int);
+    method public abstract void onError(int, java.lang.String);
+    method public abstract void onGroupCallStateChanged(int, int);
     field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
   }
 
@@ -44281,6 +44339,11 @@
     field public static final int ERROR_UNABLE_TO_READ_SIM = 206; // 0xce
   }
 
+  public static class MbmsErrors.GroupCallErrors {
+    field public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502; // 0x1f6
+    field public static final int ERROR_UNABLE_TO_START_SERVICE = 501; // 0x1f5
+  }
+
   public static class MbmsErrors.InitializationErrors {
     field public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102; // 0x66
     field public static final int ERROR_DUPLICATE_INITIALIZE = 101; // 0x65
@@ -44293,12 +44356,11 @@
     field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
   }
 
-  public class MbmsGroupCallSessionCallback {
-    ctor public MbmsGroupCallSessionCallback();
-    method public void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
-    method public void onError(int, java.lang.String);
-    method public void onMiddlewareReady();
-    method public void onServiceInterfaceAvailable(java.lang.String, int);
+  public abstract interface MbmsGroupCallSessionCallback {
+    method public abstract void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
+    method public abstract void onError(int, java.lang.String);
+    method public abstract void onMiddlewareReady();
+    method public abstract void onServiceInterfaceAvailable(java.lang.String, int);
   }
 
   public class MbmsStreamingSessionCallback {
@@ -45599,9 +45661,14 @@
     method public abstract void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
   }
 
-  public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan {
+  public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan android.text.ParcelableSpan {
     ctor public LineHeightSpan.Standard(int);
+    ctor public LineHeightSpan.Standard(android.os.Parcel);
     method public void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
+    method public int describeContents();
+    method public int getHeight();
+    method public int getSpanTypeId();
+    method public void writeToParcel(android.os.Parcel, int);
   }
 
   public static abstract interface LineHeightSpan.WithDensity implements android.text.style.LineHeightSpan {
@@ -49088,7 +49155,7 @@
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public void onProvideAutofillStructure(android.view.ViewStructure, int);
     method public void onProvideAutofillVirtualStructure(android.view.ViewStructure, int);
-    method public boolean onProvideContentCaptureStructure(android.view.ViewStructure, int);
+    method public void onProvideContentCaptureStructure(android.view.ViewStructure, int);
     method public void onProvideStructure(android.view.ViewStructure);
     method public void onProvideVirtualStructure(android.view.ViewStructure);
     method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
@@ -49216,6 +49283,7 @@
     method public void setLayoutDirection(int);
     method public void setLayoutParams(android.view.ViewGroup.LayoutParams);
     method public final void setLeft(int);
+    method public void setLeftTopRightBottom(int, int, int, int);
     method public void setLongClickable(boolean);
     method protected final void setMeasuredDimension(int, int);
     method public void setMinimumHeight(int);
@@ -49304,6 +49372,8 @@
     method public final boolean startDragAndDrop(android.content.ClipData, android.view.View.DragShadowBuilder, java.lang.Object, int);
     method public boolean startNestedScroll(int);
     method public void stopNestedScroll();
+    method public void transformMatrixToGlobal(android.graphics.Matrix);
+    method public void transformMatrixToLocal(android.graphics.Matrix);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable);
     method public final void updateDragShadow(android.view.View.DragShadowBuilder);
@@ -51896,6 +51966,7 @@
     method public java.time.ZonedDateTime getTime();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.ConversationActions.Message> CREATOR;
+    field public static final android.app.Person PERSON_USER_LOCAL;
   }
 
   public static final class ConversationActions.Message.Builder {
@@ -52164,6 +52235,7 @@
     field public static final int STATUS_LINKS_APPLIED = 0; // 0x0
     field public static final int STATUS_NO_LINKS_APPLIED = 2; // 0x2
     field public static final int STATUS_NO_LINKS_FOUND = 1; // 0x1
+    field public static final int STATUS_UNSUPPORTED_CHARACTER = 4; // 0x4
   }
 
   public static final class TextLinks.Builder {
@@ -54157,6 +54229,7 @@
     ctor public ImageView(android.content.Context, android.util.AttributeSet);
     ctor public ImageView(android.content.Context, android.util.AttributeSet, int);
     ctor public ImageView(android.content.Context, android.util.AttributeSet, int, int);
+    method public void animateTransform(android.graphics.Matrix);
     method public final void clearColorFilter();
     method public boolean getAdjustViewBounds();
     method public boolean getBaselineAlignBottom();
@@ -55454,6 +55527,7 @@
     method public java.lang.CharSequence getText();
     method public android.view.textclassifier.TextClassifier getTextClassifier();
     method public final android.content.res.ColorStateList getTextColors();
+    method public android.text.TextDirectionHeuristic getTextDirectionHeuristic();
     method public java.util.Locale getTextLocale();
     method public android.os.LocaleList getTextLocales();
     method public android.text.PrecomputedText.Params getTextMetricsParams();
diff --git a/api/system-current.txt b/api/system-current.txt
index bf2c357..257b4f6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -158,6 +158,7 @@
     field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
     field public static final java.lang.String REBOOT = "android.permission.REBOOT";
     field public static final java.lang.String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE";
+    field public static final java.lang.String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY";
     field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
     field public static final java.lang.String RECEIVE_WIFI_CREDENTIAL_CHANGE = "android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE";
     field public static final java.lang.String RECOVERY = "android.permission.RECOVERY";
@@ -171,6 +172,7 @@
     field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
     field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
     field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
+    field public static final java.lang.String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
     field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
     field public static final java.lang.String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
     field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
@@ -441,10 +443,10 @@
   }
 
   public class KeyguardManager {
-    method public void setPrivateNotificationsAllowed(boolean);
-    method public boolean getPrivateNotificationsAllowed();
     method public android.content.Intent createConfirmFactoryResetCredentialIntent(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+    method public boolean getPrivateNotificationsAllowed();
     method public void requestDismissKeyguard(android.app.Activity, java.lang.CharSequence, android.app.KeyguardManager.KeyguardDismissCallback);
+    method public void setPrivateNotificationsAllowed(boolean);
   }
 
   public class Notification implements android.os.Parcelable {
@@ -1055,6 +1057,7 @@
     field public static final java.lang.String ACTION_BATTERY_LEVEL_CHANGED = "android.intent.action.BATTERY_LEVEL_CHANGED";
     field public static final java.lang.String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";
     field public static final java.lang.String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
+    field public static final java.lang.String ACTION_DEVICE_CUSTOMIZATION_READY = "android.intent.action.DEVICE_CUSTOMIZATION_READY";
     field public static final java.lang.String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
     field public static final java.lang.String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
     field public static final java.lang.String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
@@ -1234,6 +1237,7 @@
     method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
     method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public void sendDeviceCustomizationReadyBroadcast();
     method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
     method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
@@ -1820,9 +1824,19 @@
     field public static final android.os.Parcelable.Creator<android.hardware.location.ContextHubInfo> CREATOR;
   }
 
+  public class ContextHubIntentEvent {
+    method public static android.hardware.location.ContextHubIntentEvent fromIntent(android.content.Intent);
+    method public android.hardware.location.ContextHubInfo getContextHubInfo();
+    method public int getEventType();
+    method public int getNanoAppAbortCode();
+    method public long getNanoAppId();
+    method public android.hardware.location.NanoAppMessage getNanoAppMessage();
+  }
+
   public final class ContextHubManager {
     method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback, java.util.concurrent.Executor);
     method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback);
+    method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.app.PendingIntent, long);
     method public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(android.hardware.location.ContextHubInfo, long);
     method public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(android.hardware.location.ContextHubInfo, long);
     method public deprecated int[] findNanoAppOnHub(int, android.hardware.location.NanoAppFilter);
@@ -1839,6 +1853,18 @@
     method public deprecated int unloadNanoApp(int);
     method public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(android.hardware.location.ContextHubInfo, long);
     method public deprecated int unregisterCallback(android.hardware.location.ContextHubManager.Callback);
+    field public static final int EVENT_HUB_RESET = 6; // 0x6
+    field public static final int EVENT_NANOAPP_ABORTED = 4; // 0x4
+    field public static final int EVENT_NANOAPP_DISABLED = 3; // 0x3
+    field public static final int EVENT_NANOAPP_ENABLED = 2; // 0x2
+    field public static final int EVENT_NANOAPP_LOADED = 0; // 0x0
+    field public static final int EVENT_NANOAPP_MESSAGE = 5; // 0x5
+    field public static final int EVENT_NANOAPP_UNLOADED = 1; // 0x1
+    field public static final java.lang.String EXTRA_CONTEXT_HUB_INFO = "android.hardware.location.extra.CONTEXT_HUB_INFO";
+    field public static final java.lang.String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
+    field public static final java.lang.String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
+    field public static final java.lang.String EXTRA_NANOAPP_ABORT_CODE = "android.hardware.location.extra.NANOAPP_ABORT_CODE";
+    field public static final java.lang.String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID";
   }
 
   public static abstract deprecated class ContextHubManager.Callback {
@@ -3938,6 +3964,7 @@
   public final class ConfigUpdate {
     field public static final java.lang.String ACTION_UPDATE_CARRIER_ID_DB = "android.os.action.UPDATE_CARRIER_ID_DB";
     field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
+    field public static final java.lang.String ACTION_UPDATE_CONVERSATION_ACTIONS = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
     field public static final java.lang.String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
     field public static final java.lang.String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
     field public static final java.lang.String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID";
@@ -4936,12 +4963,47 @@
 
 package android.service.intelligence {
 
+  public final class FillCallback {
+    method public void onSuccess(android.service.intelligence.FillResponse);
+  }
+
+  public final class FillController {
+    method public void autofill(java.util.List<android.util.Pair<android.view.autofill.AutofillId, android.view.autofill.AutofillValue>>);
+  }
+
+  public final class FillRequest {
+    method public android.view.autofill.AutofillId getFocusedId();
+    method public android.service.intelligence.PresentationParams getPresentationParams();
+    method public android.service.intelligence.InteractionSessionId getSessionId();
+  }
+
+  public final class FillResponse implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.intelligence.FillResponse> CREATOR;
+  }
+
+  public static class FillResponse.Builder {
+    ctor public FillResponse.Builder();
+    method public android.service.intelligence.FillResponse build();
+    method public android.service.intelligence.FillResponse.Builder setFillWindow(android.service.intelligence.FillWindow);
+    method public android.service.intelligence.FillResponse.Builder setIgnoredIds(java.util.List<android.view.autofill.AutofillId>);
+  }
+
+  public final class FillWindow {
+    ctor public FillWindow();
+    method public void destroy();
+    method public boolean update(android.service.intelligence.PresentationParams.Area, android.view.View, long);
+    field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
+  }
+
   public abstract class IntelligenceService extends android.app.Service {
     ctor public IntelligenceService();
     method public void onActivitySnapshot(android.service.intelligence.InteractionSessionId, android.service.intelligence.SnapshotData);
     method public abstract void onContentCaptureEvent(android.service.intelligence.InteractionSessionId, java.util.List<android.view.intelligence.ContentCaptureEvent>);
     method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId);
     method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId);
+    method public void onFillRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.FillRequest, android.os.CancellationSignal, android.service.intelligence.FillController, android.service.intelligence.FillCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.IntelligenceService";
   }
 
@@ -4963,6 +5025,23 @@
     field public static final android.os.Parcelable.Creator<android.service.intelligence.InteractionSessionId> CREATOR;
   }
 
+  public abstract class PresentationParams {
+    method public int getFlags();
+    method public android.service.intelligence.PresentationParams.Area getFullArea();
+    method public android.service.intelligence.PresentationParams.Area getSuggestionArea();
+    field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
+    field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
+    field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
+    field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
+    field public static final int FLAG_HOST_IME = 16; // 0x10
+    field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
+  }
+
+  public static abstract class PresentationParams.Area {
+    method public android.graphics.Rect getBounds();
+    method public android.service.intelligence.PresentationParams.Area getSubArea(android.graphics.Rect);
+  }
+
   public final class SnapshotData implements android.os.Parcelable {
     method public int describeContents();
     method public android.app.assist.AssistContent getAssistContent();
@@ -5021,14 +5100,20 @@
     ctor public NotificationAssistantService();
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
+    method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
+    method public void onNotificationDirectReply(java.lang.String);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+    method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
     method public void onNotificationsSeen(java.util.List<java.lang.String>);
+    method public void onSuggestedReplySent(java.lang.String, java.lang.CharSequence, int);
     method public final void unsnoozeNotification(java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+    field public static final int SOURCE_FROM_APP = 0; // 0x0
+    field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
   }
 
   public final class NotificationStats implements android.os.Parcelable {
@@ -5182,6 +5267,16 @@
 
 }
 
+package android.service.sms {
+
+  public abstract class FinancialSmsService extends android.app.Service {
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.database.CursorWindow onGetSmsMessages(android.os.Bundle);
+    field public static final java.lang.String ACTION_FINANCIAL_SERVICE_INTENT = "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+  }
+
+}
+
 package android.service.textclassifier {
 
   public abstract class TextClassifierService extends android.app.Service {
@@ -7021,9 +7116,9 @@
     method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
     method public void onAppCallbackDied(int, int);
     method public android.os.IBinder onBind(android.content.Intent);
-    method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+    method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
     method public void stopGroupCall(int, long);
-    method public void updateGroupCall(int, long, int[], int[]);
+    method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
   }
 
   public class MbmsStreamingServiceBase extends android.os.Binder {
@@ -7515,7 +7610,7 @@
     method public default void onMovedToDisplay(int, android.content.res.Configuration);
     method public abstract void onOverScrolled(int, int, boolean, boolean);
     method public default void onProvideAutofillVirtualStructure(android.view.ViewStructure, int);
-    method public default boolean onProvideContentCaptureStructure(android.view.ViewStructure, int);
+    method public default void onProvideContentCaptureStructure(android.view.ViewStructure, int);
     method public abstract void onProvideVirtualStructure(android.view.ViewStructure);
     method public abstract void onScrollChanged(int, int, int, int);
     method public abstract void onSizeChanged(int, int, int, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 1c01cf1..8c710c1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -81,6 +81,10 @@
     field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
   }
 
+  public class AppDetailsActivity extends android.app.Activity {
+    ctor public AppDetailsActivity();
+  }
+
   public class AppOpsManager {
     method public java.util.List<android.app.AppOpsManager.HistoricalPackageOps> getAllHistoricPackagesOps(java.lang.String[], long, long);
     method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
@@ -985,6 +989,7 @@
 
   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
     field public static final java.lang.String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
+    field public static final java.lang.String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS = "autofill_smart_suggestion_emulation_flags";
     field public static final java.lang.String AUTOMATIC_POWER_SAVER_MODE = "automatic_power_saver_mode";
     field public static final java.lang.String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold";
     field public static final java.lang.String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
@@ -1154,13 +1159,19 @@
     ctor public NotificationAssistantService();
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
+    method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
+    method public void onNotificationDirectReply(java.lang.String);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+    method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
     method public void onNotificationsSeen(java.util.List<java.lang.String>);
+    method public void onSuggestedReplySent(java.lang.String, java.lang.CharSequence, int);
     method public final void unsnoozeNotification(java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
+    field public static final int SOURCE_FROM_APP = 0; // 0x0
+    field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
   }
 
   public abstract class NotificationListenerService extends android.app.Service {
@@ -1318,9 +1329,9 @@
     method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
     method public void onAppCallbackDied(int, int);
     method public android.os.IBinder onBind(android.content.Intent);
-    method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+    method public int startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback);
     method public void stopGroupCall(int, long);
-    method public void updateGroupCall(int, long, int[], int[]);
+    method public void updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>);
   }
 
   public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index a826ec7..3723fce 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -17,6 +17,7 @@
 package com.android.commands.bmgr;
 
 import android.annotation.IntDef;
+import android.annotation.UserIdInt;
 import android.app.backup.BackupManager;
 import android.app.backup.BackupManagerMonitor;
 import android.app.backup.BackupProgress;
@@ -29,6 +30,7 @@
 import android.app.backup.ISelectBackupTransportCallback;
 import android.app.backup.RestoreSet;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.os.Bundle;
@@ -37,8 +39,10 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -49,20 +53,34 @@
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
-public final class Bmgr {
-    IBackupManager mBmgr;
-    IRestoreSession mRestore;
+/**
+ * Adb shell command for {@link android.app.backup.IBackupManager}.
+ */
+public class Bmgr {
+    public static final String TAG = "Bmgr";
 
-    static final String BMGR_NOT_RUNNING_ERR =
+    private final IBackupManager mBmgr;
+    private IRestoreSession mRestore;
+
+    private static final String BMGR_NOT_RUNNING_ERR =
             "Error: Could not access the Backup Manager.  Is the system running?";
-    static final String TRANSPORT_NOT_RUNNING_ERR =
+    private static final String TRANSPORT_NOT_RUNNING_ERR =
             "Error: Could not access the backup transport.  Is the system running?";
-    static final String PM_NOT_RUNNING_ERR =
+    private static final String PM_NOT_RUNNING_ERR =
             "Error: Could not access the Package Manager.  Is the system running?";
 
     private String[] mArgs;
     private int mNextArg;
 
+    @VisibleForTesting
+    Bmgr(IBackupManager bmgr) {
+        mBmgr = bmgr;
+    }
+
+    Bmgr() {
+        mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
+    }
+
     public static void main(String[] args) {
         try {
             new Bmgr().run(args);
@@ -78,71 +96,73 @@
             return;
         }
 
-        if (!isBmgrActive()) {
+        mArgs = args;
+        mNextArg = 0;
+        int userId = parseUserId();
+        String op = nextArg();
+        Slog.v(TAG, "Running " + op + " for user:" + userId);
+
+        if (!isBmgrActive(userId)) {
             return;
         }
 
-        mArgs = args;
-        String op = args[0];
-        mNextArg = 1;
-
         if ("enabled".equals(op)) {
-            doEnabled();
+            doEnabled(userId);
             return;
         }
 
         if ("enable".equals(op)) {
-            doEnable();
+            doEnable(userId);
             return;
         }
 
         if ("run".equals(op)) {
-            doRun();
+            doRun(userId);
             return;
         }
 
         if ("backup".equals(op)) {
-            doBackup();
+            doBackup(userId);
             return;
         }
 
         if ("init".equals(op)) {
-            doInit();
+            doInit(userId);
             return;
         }
 
         if ("list".equals(op)) {
-            doList();
+            doList(userId);
             return;
         }
 
         if ("restore".equals(op)) {
-            doRestore();
+            doRestore(userId);
             return;
         }
 
         if ("transport".equals(op)) {
-            doTransport();
+            doTransport(userId);
             return;
         }
 
         if ("wipe".equals(op)) {
-            doWipe();
+            doWipe(userId);
             return;
         }
 
         if ("fullbackup".equals(op)) {
-            doFullTransportBackup();
+            doFullTransportBackup(userId);
             return;
         }
 
         if ("backupnow".equals(op)) {
-            doBackupNow();
+            doBackupNow(userId);
             return;
         }
 
         if ("cancel".equals(op)) {
-            doCancel();
+            doCancel(userId);
             return;
         }
 
@@ -155,15 +175,14 @@
         showUsage();
     }
 
-    private boolean isBmgrActive() {
-        mBmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
+    boolean isBmgrActive(@UserIdInt int userId) {
         if (mBmgr == null) {
             System.err.println(BMGR_NOT_RUNNING_ERR);
             return false;
         }
 
         try {
-            if (!mBmgr.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
+            if (!mBmgr.isBackupServiceActive(userId)) {
                 System.err.println(BMGR_NOT_RUNNING_ERR);
                 return false;
             }
@@ -180,7 +199,7 @@
         return enabled ? "enabled" : "disabled";
     }
 
-    private void doEnabled() {
+    private void doEnabled(@UserIdInt int userId) {
         try {
             boolean isEnabled = mBmgr.isBackupEnabled();
             System.out.println("Backup Manager currently "
@@ -191,7 +210,7 @@
         }
     }
 
-    private void doEnable() {
+    private void doEnable(@UserIdInt int userId) {
         String arg = nextArg();
         if (arg == null) {
             showUsage();
@@ -211,7 +230,7 @@
         }
     }
 
-    private void doRun() {
+    void doRun(@UserIdInt int userId) {
         try {
             mBmgr.backupNow();
         } catch (RemoteException e) {
@@ -220,7 +239,7 @@
         }
     }
 
-    private void doBackup() {
+    private void doBackup(@UserIdInt int userId) {
         String pkg = nextArg();
         if (pkg == null) {
             showUsage();
@@ -235,7 +254,7 @@
         }
     }
 
-    private void doFullTransportBackup() {
+    private void doFullTransportBackup(@UserIdInt int userId) {
         System.out.println("Performing full transport backup");
 
         String pkg;
@@ -354,8 +373,8 @@
         }
     }
 
-    private void backupNowAllPackages(boolean nonIncrementalBackup, @Monitor int monitorState) {
-        int userId = UserHandle.USER_SYSTEM;
+    private void backupNowAllPackages(@UserIdInt int userId, boolean nonIncrementalBackup,
+            @Monitor int monitorState) {
         IPackageManager mPm =
                 IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
         if (mPm == null) {
@@ -379,11 +398,13 @@
                 System.err.println(e.toString());
                 System.err.println(BMGR_NOT_RUNNING_ERR);
             }
-            backupNowPackages(Arrays.asList(filteredPackages), nonIncrementalBackup, monitorState);
+            backupNowPackages(userId, Arrays.asList(filteredPackages), nonIncrementalBackup,
+                    monitorState);
         }
     }
 
     private void backupNowPackages(
+            @UserIdInt int userId,
             List<String> packages, boolean nonIncrementalBackup, @Monitor int monitorState) {
         int flags = 0;
         if (nonIncrementalBackup) {
@@ -412,7 +433,7 @@
         }
     }
 
-    private void doBackupNow() {
+    private void doBackupNow(@UserIdInt int userId) {
         String pkg;
         boolean backupAll = false;
         boolean nonIncrementalBackup = false;
@@ -439,20 +460,20 @@
             if (allPkgs.size() == 0) {
                 System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
                         "incremental backup for all packages.");
-                backupNowAllPackages(nonIncrementalBackup, monitor);
+                backupNowAllPackages(userId, nonIncrementalBackup, monitor);
             } else {
                 System.err.println("Provide only '--all' flag or list of packages.");
             }
         } else if (allPkgs.size() > 0) {
             System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
                     "incremental backup for " + allPkgs.size() +" requested packages.");
-            backupNowPackages(allPkgs, nonIncrementalBackup, monitor);
+            backupNowPackages(userId, allPkgs, nonIncrementalBackup, monitor);
         } else {
             System.err.println("Provide '--all' flag or list of packages.");
         }
     }
 
-    private void doCancel() {
+    private void doCancel(@UserIdInt int userId) {
         String arg = nextArg();
         if ("backups".equals(arg)) {
             try {
@@ -467,7 +488,7 @@
         System.err.println("Unknown command.");
     }
 
-    private void doTransport() {
+    private void doTransport(@UserIdInt int userId) {
         try {
             String which = nextArg();
             if (which == null) {
@@ -531,7 +552,7 @@
         }
     }
 
-    private void doWipe() {
+    private void doWipe(@UserIdInt int userId) {
         String transport = nextArg();
         if (transport == null) {
             showUsage();
@@ -563,7 +584,7 @@
         }
     }
 
-    private void doInit() {
+    private void doInit(@UserIdInt int userId) {
         ArraySet<String> transports = new ArraySet<>();
         String transport;
         while ((transport = nextArg()) != null) {
@@ -586,7 +607,7 @@
         }
     }
 
-    private void doList() {
+    private void doList(@UserIdInt int userId) {
         String arg = nextArg();     // sets, transports, packages set#
         if ("transports".equals(arg)) {
             doListTransports();
@@ -603,8 +624,6 @@
 
             if ("sets".equals(arg)) {
                 doListRestoreSets();
-            } else if ("transports".equals(arg)) {
-                doListTransports();
             }
 
             mRestore.endRestoreSession();
@@ -717,7 +736,7 @@
         }
     }
 
-    private void doRestore() {
+    private void doRestore(@UserIdInt int userId) {
         String arg = nextArg();
         if (arg == null) {
             showUsage();
@@ -830,8 +849,18 @@
         return arg;
     }
 
+    private int parseUserId() {
+        String arg = nextArg();
+        if ("--user".equals(arg)) {
+            return UserHandle.parseUserArg(nextArg());
+        } else {
+            mNextArg--;
+            return UserHandle.USER_SYSTEM;
+        }
+    }
+
     private static void showUsage() {
-        System.err.println("usage: bmgr [backup|restore|list|transport|run]");
+        System.err.println("usage: bmgr [--user <userId>] [backup|restore|list|transport|run]");
         System.err.println("       bmgr backup PACKAGE");
         System.err.println("       bmgr enable BOOL");
         System.err.println("       bmgr enabled");
@@ -847,6 +876,10 @@
         System.err.println("       bmgr cancel backups");
         System.err.println("       bmgr init TRANSPORT...");
         System.err.println("");
+        System.err.println("The '--user' option specifies the user on which the operation is run.");
+        System.err.println("It must be the first argument before the operation.");
+        System.err.println("The default value is 0 which is the system user.");
+        System.err.println("");
         System.err.println("The 'backup' command schedules a backup pass for the named package.");
         System.err.println("Note that the backup pass will effectively be a no-op if the package");
         System.err.println("does not actually have changed data to store.");
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 5a6c813..d7922bc 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -18,7 +18,7 @@
     tidy: true,
     tidy_flags: [
         "-system-headers",
-        "-warnings-as-errors=*",
+// b/120024673       "-warnings-as-errors=*",
     ],
     srcs: [
         "libidmap2/BinaryStreamVisitor.cpp",
@@ -64,7 +64,7 @@
     tidy: true,
     tidy_flags: [
         "-system-headers",
-        "-warnings-as-errors=*",
+// b/120024673        "-warnings-as-errors=*",
     ],
     srcs: [
         "tests/BinaryStreamVisitorTests.cpp",
@@ -118,7 +118,7 @@
     tidy: true,
     tidy_flags: [
         "-system-headers",
-        "-warnings-as-errors=*",
+// b/120024673        "-warnings-as-errors=*",
     ],
     srcs: [
         "idmap2/Create.cpp",
@@ -165,7 +165,7 @@
     ],
     tidy_flags: [
         "-system-headers",
-        "-warnings-as-errors=*",
+// b/120024673        "-warnings-as-errors=*",
     ],
     srcs: [
         ":idmap2_aidl",
@@ -181,6 +181,7 @@
         "libutils",
         "libziparchive",
     ],
+    init_rc: ["idmap2d/idmap2d.rc"],
 }
 
 filegroup {
diff --git a/cmds/idmap2/idmap2d/idmap2d.rc b/cmds/idmap2/idmap2d/idmap2d.rc
new file mode 100644
index 0000000..203e7be
--- /dev/null
+++ b/cmds/idmap2/idmap2d/idmap2d.rc
@@ -0,0 +1,4 @@
+service idmap2d /system/bin/idmap2d
+    class main
+    user system
+    group system
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 3ee0a06..c8405a2 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -34,6 +34,8 @@
 import "frameworks/base/core/proto/android/telecomm/enums.proto";
 import "frameworks/base/core/proto/android/telephony/enums.proto";
 import "frameworks/base/core/proto/android/view/enums.proto";
+import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto";
+import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto";
 
 /**
  * The master atom class. This message defines all of the available
@@ -156,6 +158,7 @@
         ServiceLaunchReported service_launch_reported = 100;
         PhenotypeFlagStateChanged phenotype_flag_state_changed = 101;
         BinaryPushStateChanged binary_push_state_changed = 102;
+        DevicePolicyEvent device_policy_event = 103;
     }
 
     // Pulled events will start at field 10000.
@@ -205,6 +208,7 @@
         DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
         ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042;
         BatteryLevel battery_level = 10043;
+        BuildInformation build_information = 10044;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -1334,8 +1338,9 @@
  * Log bucketed battery charge cycles.
  *
  * Each bucket represents cycles of the battery past
- * a given charge point.  For example, bucket 1 is the
- * lowest 1/8th of the battery, and bucket 8 is 100%.
+ * a given charge point.  For example, if 10 cycle buckets are
+ * initialized, bucket 1 is the lowest 1/10th of the battery,
+ * and bucket 10 is 100%.
  *
  * Logged from:
  * /sys/class/power_supply/bms/cycle_count, via Vendor.
@@ -1349,6 +1354,8 @@
     optional int32 cycle_bucket_6 = 6;
     optional int32 cycle_bucket_7 = 7;
     optional int32 cycle_bucket_8 = 8;
+    optional int32 cycle_bucket_9 = 9;
+    optional int32 cycle_bucket_10 = 10;
 }
 
 /**
@@ -2421,8 +2428,8 @@
  */
 message CpuTimePerUid {
     optional int32 uid = 1 [(is_uid) = true];
-    optional uint64 user_time_millis = 2;
-    optional uint64 sys_time_millis = 3;
+    optional uint64 user_time_micros = 2;
+    optional uint64 sys_time_micros = 3;
 }
 
 /**
@@ -3331,6 +3338,40 @@
 }
 
 /**
+ * Pulls information about the device's build.
+ */
+message BuildInformation {
+    // Build.FINGERPRINT. A string that uniquely identifies this build. Do not parse.
+    // E.g. may be composed of the brand, product, device, release, id, incremental, type, and tags.
+    optional string fingerprint = 1;
+
+    // Build.BRAND. The consumer-visible brand with which the product/hardware will be associated.
+    optional string brand = 2;
+
+    // Build.PRODUCT. The name of the overall product.
+    optional string product = 3;
+
+    // Build.DEVICE. The name of the industrial design.
+    optional string device = 4;
+
+    // Build.VERSION.RELEASE. The user-visible version string.  E.g., "1.0" or "3.4b5" or "bananas".
+    optional string version_release = 5;
+
+    // Build.ID. E.g. a label like "M4-rc20".
+    optional string id = 6;
+
+    // Build.VERSION.INCREMENTAL. The internal value used by the underlying source control to
+    // represent this build.
+    optional string version_incremental = 7;
+
+    // Build.TYPE. The type of build, like "user" or "eng".
+    optional string type = 8;
+
+    // Build.TAGS. Comma-separated tags describing the build, like "unsigned,debug".
+    optional string tags = 9;
+}
+
+/**
  * Pulls on-device BatteryStats power use calculations for the overall device.
  */
 message DeviceCalculatedPowerUse {
@@ -3389,3 +3430,25 @@
     // (i.e. roughly since device was last significantly charged).
     optional float power_milli_amp_hours = 2;
 }
+
+/**
+ * Logs device policy features.
+ *
+ * Logged from:
+ *   frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+ *   packages/apps/ManagedProvisioning/src/com/android/managedprovisioning/
+ */
+message DevicePolicyEvent {
+    // The event id - unique for each event.
+    optional android.stats.devicepolicy.EventId event_id = 1;
+    // The admin package name.
+    optional string admin_package_name = 2;
+    // A generic integer parameter.
+    optional int32 integer_value = 3;
+    // A generic boolean parameter.
+    optional bool boolean_value = 4;
+    // A parameter specifying a time period in milliseconds.
+    optional uint64 time_period_millis = 5;
+    // 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];
+}
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index 3eb05a9..4e4b8f3 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -59,7 +59,7 @@
         }
         data->clear();
         for (const StatsLogEventWrapper& it : returned_value) {
-            data->push_back(make_shared<LogEvent>(it));
+            LogEvent::createLogEvents(it, *data);
         }
         VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
         return true;
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index e3f251a..f501574 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -32,9 +32,10 @@
 sp<UidMap> StatsPuller::mUidMap = nullptr;
 void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
 
-// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
 StatsPuller::StatsPuller(const int tagId)
     : mTagId(tagId) {
+    // Pullers can cause significant impact to system health and battery.
+    // So that we don't pull too frequently.
     mCoolDownNs = StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
     VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
 }
@@ -64,8 +65,8 @@
         data->setLogdWallClockTimestampNs(wallClockTimeNs);
     }
     if (ret && mCachedData.size() > 0) {
-      mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
-      (*data) = mCachedData;
+        mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
+        (*data) = mCachedData;
     }
     StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
     return ret;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ab635a0..f9b7982 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -54,52 +54,42 @@
         // wifi_bytes_transfer
         {android::util::WIFI_BYTES_TRANSFER,
          {{2, 3, 4, 5},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
         // wifi_bytes_transfer_by_fg_bg
         {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
          {{3, 4, 5, 6},
-          {2},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
         // mobile_bytes_transfer
         {android::util::MOBILE_BYTES_TRANSFER,
          {{2, 3, 4, 5},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
         // mobile_bytes_transfer_by_fg_bg
         {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
          {{3, 4, 5, 6},
-          {2},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
         // bluetooth_bytes_transfer
         {android::util::BLUETOOTH_BYTES_TRANSFER,
          {{2, 3},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
         // kernel_wakelock
         {android::util::KERNEL_WAKELOCK,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
         // subsystem_sleep_state
         {android::util::SUBSYSTEM_SLEEP_STATE,
-         {{}, {}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
+         {{}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
         // on_device_power_measurement
-        {android::util::ON_DEVICE_POWER_MEASUREMENT,
-         {{}, {}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
+        {android::util::ON_DEVICE_POWER_MEASUREMENT, {{}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
         // cpu_time_per_freq
         {android::util::CPU_TIME_PER_FREQ,
-         {{3},
-          {2},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+         {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
         // cpu_time_per_uid
         {android::util::CPU_TIME_PER_UID,
          {{2, 3},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
         // cpu_time_per_uid_freq
@@ -107,164 +97,140 @@
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_TIME_PER_UID_FREQ,
          {{4},
-          {2, 3},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
         // cpu_active_time
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_ACTIVE_TIME,
-         {{2},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
+         {{2}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
         // cpu_cluster_time
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_CLUSTER_TIME,
-         {{3},
-          {2},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
+         {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
         // wifi_activity_energy_info
         {android::util::WIFI_ACTIVITY_INFO,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
         // modem_activity_info
         {android::util::MODEM_ACTIVITY_INFO,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
         // bluetooth_activity_info
         {android::util::BLUETOOTH_ACTIVITY_INFO,
          {{},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
         // system_elapsed_realtime
         {android::util::SYSTEM_ELAPSED_REALTIME,
          {{},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
         // system_uptime
         {android::util::SYSTEM_UPTIME,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
         // remaining_battery_capacity
         {android::util::REMAINING_BATTERY_CAPACITY,
          {{},
-          {},
           1 * NS_PER_SEC,
           new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
         // full_battery_capacity
         {android::util::FULL_BATTERY_CAPACITY,
          {{},
-          {},
           1 * NS_PER_SEC,
           new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
         // battery_voltage
         {android::util::BATTERY_VOLTAGE,
-         {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
-         // battery_voltage
+         {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
+        // battery_level
         {android::util::BATTERY_LEVEL,
-         {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
+         {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
         // process_memory_state
         {android::util::PROCESS_MEMORY_STATE,
          {{4, 5, 6, 7, 8, 9},
-          {2, 3, 10},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
         // native_process_memory_state
         {android::util::NATIVE_PROCESS_MEMORY_STATE,
          {{3, 4, 5, 6},
-          {2, 7},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
         {android::util::PROCESS_MEMORY_HIGH_WATER_MARK,
          {{3},
-          {2},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
         // temperature
-        {android::util::TEMPERATURE, {{}, {}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
+        {android::util::TEMPERATURE, {{}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
         // binder_calls
         {android::util::BINDER_CALLS,
          {{4, 5, 6, 8, 12},
-          {2, 3, 7, 9, 10, 11, 13},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
         // binder_calls_exceptions
         {android::util::BINDER_CALLS_EXCEPTIONS,
          {{},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
         // looper_stats
         {android::util::LOOPER_STATS,
          {{5, 6, 7, 8, 9},
-          {2, 3, 4, 10},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
         // Disk Stats
         {android::util::DISK_STATS,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
         // Directory usage
         {android::util::DIRECTORY_USAGE,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
         // Size of app's code, data, and cache
         {android::util::APP_SIZE,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
         // Size of specific categories of files. Eg. Music.
         {android::util::CATEGORY_SIZE,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
         // Number of fingerprints registered to each user.
         {android::util::NUM_FINGERPRINTS,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
         // ProcStats.
         {android::util::PROC_STATS,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
         // ProcStatsPkgProc.
         {android::util::PROC_STATS_PKG_PROC,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
         // Disk I/O stats per uid.
         {android::util::DISK_IO,
-         {{2,3,4,5,6,7,8,9,10,11},
-          {},
+         {{2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
           3 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::DISK_IO)}},
         // PowerProfile constants for power model calculations.
         {android::util::POWER_PROFILE,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
         // Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses.
         {android::util::PROCESS_CPU_TIME,
-            {{} /* additive fields */, {} /* non additive fields */,
-             5 * NS_PER_SEC /* min cool-down in seconds*/,
-             new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
+         {{} /* additive fields */,
+          5 * NS_PER_SEC /* min cool-down in seconds*/,
+          new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
         {android::util::CPU_TIME_PER_THREAD_FREQ,
          {{7},
-          {2, 3, 4, 5, 6},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
         // DeviceCalculatedPowerUse.
         {android::util::DEVICE_CALCULATED_POWER_USE,
-         {{}, {}, 1 * NS_PER_SEC,
+         {{},
+          1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
         // DeviceCalculatedPowerBlameUid.
         {android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
-         {{}, {}, // BatteryStats already merged isolated with host ids so it's unnecessary here.
+         {{},  // BatteryStats already merged isolated with host ids so it's unnecessary here.
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
         // DeviceCalculatedPowerBlameOther.
         {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
-         {{}, {},
+         {{},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
+        // BuildInformation.
+        {android::util::BUILD_INFORMATION,
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index bbf5d9d..3350736 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -36,9 +36,6 @@
     // The field numbers of the fields that need to be summed when merging
     // isolated uid with host uid.
     std::vector<int> additiveFields;
-    // The field numbers of the fields that can't be merged when merging
-    // data belong to isolated uid and host uid.
-    std::vector<int> nonAdditiveFields;
     // How long should the puller wait before doing an actual pull again. Default
     // 1 sec. Set this to 0 if this is handled elsewhere.
     int64_t coolDownNs = 1 * NS_PER_SEC;
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index ea7fa97..0b9b6ab 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -25,67 +25,13 @@
 namespace os {
 namespace statsd {
 
+using std::list;
 using std::map;
+using std::set;
 using std::shared_ptr;
+using std::sort;
 using std::vector;
 
-namespace {
-bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
-                 const vector<int>& nonAdditiveFields) {
-    const auto& l_values = lhs->getValues();
-    const auto& r_values = rhs->getValues();
-
-    for (size_t i : nonAdditiveFields) {
-        // We store everything starting from index 0, so we need to use i-1
-        if (!(l_values.size() > i - 1 && r_values.size() > i - 1 &&
-              l_values[i - 1].mValue == r_values[i - 1].mValue)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-// merge rhs to lhs
-// when calling this function, all sanity check should be done already.
-// e.g., index boundary, nonAdditiveFields matching etc.
-bool mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
-                const vector<int>& additiveFields) {
-    vector<FieldValue>* host_values = lhs->getMutableValues();
-    const auto& child_values = rhs->getValues();
-    for (int i : additiveFields) {
-        Value& host = (*host_values)[i - 1].mValue;
-        const Value& child = (child_values[i - 1]).mValue;
-        if (child.getType() != host.getType()) {
-            return false;
-        }
-        switch (child.getType()) {
-            case INT:
-                host.setInt(host.int_value + child.int_value);
-                break;
-            case LONG:
-                host.setLong(host.long_value + child.long_value);
-                break;
-            default:
-                ALOGE("Tried to merge 2 fields with unsupported type");
-                return false;
-        }
-    }
-    return true;
-}
-
-bool tryMerge(vector<shared_ptr<LogEvent>>& data, int child_pos, const vector<int>& host_pos,
-              const vector<int>& nonAdditiveFields, const vector<int>& additiveFields) {
-    for (const auto& pos : host_pos) {
-        if (shouldMerge(data[pos], data[child_pos], nonAdditiveFields) &&
-            mergeEvent(data[pos], data[child_pos], additiveFields)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-}  // namespace
-
 /**
  * Process all data and merge isolated with host if necessary.
  * For example:
@@ -95,7 +41,7 @@
  *       int byte_send = 3;
  *       int byte_recv = 4;
  *   }
- *   additive fields are {3, 4}, non-additive field is {2}
+ *   additive fields are {3, 4}
  * If we pulled the following events (uid1_child is an isolated uid which maps to uid1):
  * [uid1, fg, 100, 200]
  * [uid1_child, fg, 100, 200]
@@ -104,65 +50,119 @@
  * We want to merge them and results should be:
  * [uid1, fg, 200, 400]
  * [uid1, bg, 100, 200]
+ *
+ * All atoms should be of the same tagId. All fields should be present.
  */
-void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
-                                int tagId) {
+void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
+                                      int tagId) {
     if (StatsPullerManager::kAllPullAtomInfo.find(tagId) ==
         StatsPullerManager::kAllPullAtomInfo.end()) {
         VLOG("Unknown pull atom id %d", tagId);
         return;
     }
-    int uidField;
-    auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
-    if (it == android::util::AtomsInfo::kAtomsWithUidField.end()) {
-        VLOG("No uid to merge for atom %d", tagId);
+    if ((android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) ==
+         android::util::AtomsInfo::kAtomsWithAttributionChain.end()) &&
+        (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
+         android::util::AtomsInfo::kAtomsWithUidField.end())) {
+        VLOG("No uid or attribution chain to merge, atom %d", tagId);
         return;
-    } else {
-        uidField = it->second;  // uidField is the field number in proto,
     }
-    const vector<int>& additiveFields =
-            StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
-    const vector<int>& nonAdditiveFields =
-            StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields;
 
-    // map of host uid to their position in the original vector
-    map<int, vector<int>> hostPosition;
-    vector<bool> toRemove = vector<bool>(data.size(), false);
-
-    for (size_t i = 0; i < data.size(); i++) {
-        vector<FieldValue>* valueList = data[i]->getMutableValues();
-
-        int uid;
-        if (uidField > 0 && (int)data[i]->getValues().size() >= uidField &&
-            (data[i]->getValues())[uidField - 1].mValue.getType() == INT) {
-            uid = (*data[i]->getMutableValues())[uidField - 1].mValue.int_value;
-        } else {
-            ALOGE("Malformed log, uid not found. %s", data[i]->ToString().c_str());
-            continue;
+    // 1. Map all isolated uid in-place to host uid
+    for (shared_ptr<LogEvent>& event : data) {
+        if (event->GetTagId() != tagId) {
+            ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId());
+            return;
         }
-
-        const int hostUid = uidMap->getHostUidOrSelf(uid);
-
-        if (hostUid != uid) {
-            (*valueList)[0].mValue.setInt(hostUid);
-        }
-        if (hostPosition.find(hostUid) == hostPosition.end()) {
-            hostPosition[hostUid].push_back(i);
+        if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) !=
+            android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
+            for (auto& value : *(event->getMutableValues())) {
+                if (value.mField.getPosAtDepth(0) > kAttributionField) {
+                    break;
+                }
+                if (isAttributionUidField(value)) {
+                    const int hostUid = uidMap->getHostUidOrSelf(value.mValue.int_value);
+                    value.mValue.setInt(hostUid);
+                }
+            }
         } else {
-            if (tryMerge(data, i, hostPosition[hostUid], nonAdditiveFields, additiveFields)) {
-                toRemove[i] = true;
-            } else {
-                hostPosition[hostUid].push_back(i);
+            auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
+            if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
+                int uidField = it->second;  // uidField is the field number in proto,
+                // starting from 1
+                if (uidField > 0 && (int)event->getValues().size() >= uidField &&
+                    (event->getValues())[uidField - 1].mValue.getType() == INT) {
+                    Value& value = (*event->getMutableValues())[uidField - 1].mValue;
+                    const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
+                    value.setInt(hostUid);
+                } else {
+                    ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
+                    return;
+                }
             }
         }
     }
 
+    // 2. sort the data, bit-wise
+    sort(data.begin(), data.end(),
+         [](const shared_ptr<LogEvent>& lhs, const shared_ptr<LogEvent>& rhs) {
+             if (lhs->size() != rhs->size()) {
+                 return lhs->size() < rhs->size();
+             }
+             const std::vector<FieldValue>& lhsValues = lhs->getValues();
+             const std::vector<FieldValue>& rhsValues = rhs->getValues();
+             for (int i = 0; i < (int)lhs->size(); i++) {
+                 if (lhsValues[i] != rhsValues[i]) {
+                     return lhsValues[i] < rhsValues[i];
+                 }
+             }
+             return false;
+         });
+
     vector<shared_ptr<LogEvent>> mergedData;
-    for (size_t i = 0; i < toRemove.size(); i++) {
-        if (!toRemove[i]) {
+    const vector<int>& additiveFieldsVec =
+            StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
+    const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end());
+    bool needMerge = true;
+
+    // 3. do the merge.
+    // The loop invariant is this: for every event, check if it differs on
+    // non-additive fields, or have different attribution chain length.
+    // If so, no need to merge, add itself to the result.
+    // Otherwise, merge the value onto the one immediately next to it.
+    for (int i = 0; i < (int)data.size() - 1; i++) {
+        // Size different, must be different chains.
+        if (data[i]->size() != data[i + 1]->size()) {
             mergedData.push_back(data[i]);
+            continue;
+        }
+        vector<FieldValue>* lhsValues = data[i]->getMutableValues();
+        vector<FieldValue>* rhsValues = data[i + 1]->getMutableValues();
+        needMerge = true;
+        for (int p = 0; p < (int)lhsValues->size(); p++) {
+            if ((*lhsValues)[p] != (*rhsValues)[p]) {
+                int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
+                // Differ on non-additive field, abort.
+                if (additiveFields.find(pos) == additiveFields.end()) {
+                    needMerge = false;
+                    break;
+                }
+            }
+        }
+        if (!needMerge) {
+            mergedData.push_back(data[i]);
+            continue;
+        }
+        // This should be infrequent operation.
+        for (int p = 0; p < (int)lhsValues->size(); p++) {
+            int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
+            if (additiveFields.find(pos) != additiveFields.end()) {
+                (*rhsValues)[p].mValue += (*lhsValues)[p].mValue;
+            }
         }
     }
+    mergedData.push_back(data.back());
+
     data.clear();
     data = mergedData;
 }
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
index fd4a4a2..f703e6c 100644
--- a/cmds/statsd/src/external/puller_util.h
+++ b/cmds/statsd/src/external/puller_util.h
@@ -25,8 +25,8 @@
 namespace os {
 namespace statsd {
 
-void mergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
-                                const sp<UidMap>& uidMap, int tagId);
+void mapAndMergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
+                                      const sp<UidMap>& uidMap, int tagId);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 343709a..3157037 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -113,7 +113,7 @@
 
     // Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
     // drops the metrics data in memory.
-    static const size_t kMaxMetricsBytesPerConfig = 256 * 1024;
+    static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
 
     // Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
     // data subscriber that it's time to call getData.
@@ -130,7 +130,7 @@
     static const int64_t kMinBroadcastPeriodNs = 60 * NS_PER_SEC;
 
     /* Min period between two checks of byte size per config key in nanoseconds. */
-    static const int64_t kMinByteSizeCheckPeriodNs = 10 * NS_PER_SEC;
+    static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC;
 
     // Maximum age (30 days) that files on disk can exist in seconds.
     static const int kMaxAgeSecond = 60 * 60 * 24 * 30;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 625294c..b9732a5 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -41,13 +41,28 @@
     }
 }
 
-LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper) {
+LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex) {
     mTagId = statsLogEventWrapper.getTagId();
     mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
     mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs();
     mLogUid = 0;
+    int workChainPosOffset = 0;
+    if (workChainIndex != -1) {
+        const WorkChain& wc = statsLogEventWrapper.getWorkChains()[workChainIndex];
+        // chains are at field 1, level 2
+        int depth = 2;
+        for (int i = 0; i < (int)wc.uids.size(); i++) {
+            int pos[] = {1, i + 1, 1};
+            mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.uids[i])));
+            pos[2]++;
+            mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.tags[i])));
+            mValues.back().mField.decorateLastPos(2);
+        }
+        mValues.back().mField.decorateLastPos(1);
+        workChainPosOffset = 1;
+    }
     for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) {
-        Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1));
+        Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1 + workChainPosOffset));
         switch (statsLogEventWrapper.getElements()[i].type) {
             case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT:
                 mValues.push_back(
@@ -79,6 +94,17 @@
     }
 }
 
+void LogEvent::createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
+                               std::vector<std::shared_ptr<LogEvent>>& logEvents) {
+    if (statsLogEventWrapper.getWorkChains().size() == 0) {
+        logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, -1));
+    } else {
+        for (size_t i = 0; i < statsLogEventWrapper.getWorkChains().size(); i++) {
+            logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, i));
+        }
+    }
+}
+
 LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
     mLogdTimestampNs = wallClockTimestampNs;
     mTagId = tagId;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 3d5b2ab..5408d17 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -65,7 +65,16 @@
      */
     explicit LogEvent(log_msg& msg);
 
-    explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper);
+    /**
+     * Creates LogEvent from StatsLogEventWrapper.
+     */
+    static void createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
+                                std::vector<std::shared_ptr<LogEvent>>& logEvents);
+
+    /**
+     * Construct one LogEvent from a StatsLogEventWrapper with the i-th work chain. -1 if no chain.
+     */
+    explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex);
 
     /**
      * Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index c3912ee..9fe84dc 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -165,7 +165,7 @@
     const bool mSkipZeroDiffOutput;
 
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
-  FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
+    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
     FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 4ac55b5..b317361 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -454,6 +454,16 @@
             ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
             return false;
         }
+        if (!metric.has_value_field()) {
+            ALOGW("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+            return false;
+        }
+        std::vector<Matcher> fieldMatchers;
+        translateFieldMatcher(metric.value_field(), &fieldMatchers);
+        if (fieldMatchers.size() < 1) {
+            ALOGW("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+            return false;
+        }
 
         int metricIndex = allMetricProducers.size();
         metricMap.insert({metric.id(), metricIndex});
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 6384757..90dfa87 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -394,6 +394,167 @@
     EXPECT_EQ(1.1f, item16.mValue.float_value);
 }
 
+TEST(LogEventTest, TestStatsLogEventWrapperNoChain) {
+    Parcel parcel;
+    // tag id
+    parcel.writeInt32(1);
+    // elapsed realtime
+    parcel.writeInt64(1111L);
+    // wallclock time
+    parcel.writeInt64(2222L);
+    // no chain
+    parcel.writeInt32(0);
+    // 2 data
+    parcel.writeInt32(2);
+    // int 6
+    parcel.writeInt32(1);
+    parcel.writeInt32(6);
+    // long 10
+    parcel.writeInt32(2);
+    parcel.writeInt64(10);
+    parcel.setDataPosition(0);
+
+    StatsLogEventWrapper statsLogEventWrapper;
+    EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
+    EXPECT_EQ(1, statsLogEventWrapper.getTagId());
+    EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
+    EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
+    EXPECT_EQ(0, statsLogEventWrapper.getWorkChains().size());
+    EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
+    EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
+    EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
+    LogEvent event(statsLogEventWrapper, -1);
+    EXPECT_EQ(1, event.GetTagId());
+    EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
+    EXPECT_EQ(2222L, event.GetLogdTimestampNs());
+    EXPECT_EQ(2, event.size());
+    EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
+    EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
+}
+
+TEST(LogEventTest, TestStatsLogEventWrapperWithChain) {
+    Parcel parcel;
+    // tag id
+    parcel.writeInt32(1);
+    // elapsed realtime
+    parcel.writeInt64(1111L);
+    // wallclock time
+    parcel.writeInt64(2222L);
+    // 3 chains
+    parcel.writeInt32(3);
+    // chain1, 2 nodes (1, "tag1") (2, "tag2")
+    parcel.writeInt32(2);
+    parcel.writeInt32(1);
+    parcel.writeString16(String16("tag1"));
+    parcel.writeInt32(2);
+    parcel.writeString16(String16("tag2"));
+    // chain2, 1 node (3, "tag3")
+    parcel.writeInt32(1);
+    parcel.writeInt32(3);
+    parcel.writeString16(String16("tag3"));
+    // chain3, 2 nodes (4, "") (5, "")
+    parcel.writeInt32(2);
+    parcel.writeInt32(4);
+    parcel.writeString16(String16(""));
+    parcel.writeInt32(5);
+    parcel.writeString16(String16(""));
+    // 2 data
+    parcel.writeInt32(2);
+    // int 6
+    parcel.writeInt32(1);
+    parcel.writeInt32(6);
+    // long 10
+    parcel.writeInt32(2);
+    parcel.writeInt64(10);
+    parcel.setDataPosition(0);
+
+    StatsLogEventWrapper statsLogEventWrapper;
+    EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
+    EXPECT_EQ(1, statsLogEventWrapper.getTagId());
+    EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
+    EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
+    EXPECT_EQ(3, statsLogEventWrapper.getWorkChains().size());
+    EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids.size());
+    EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[0].uids[0]);
+    EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids[1]);
+    EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].tags.size());
+    EXPECT_EQ("tag1", statsLogEventWrapper.getWorkChains()[0].tags[0]);
+    EXPECT_EQ("tag2", statsLogEventWrapper.getWorkChains()[0].tags[1]);
+    EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].uids.size());
+    EXPECT_EQ(3, statsLogEventWrapper.getWorkChains()[1].uids[0]);
+    EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].tags.size());
+    EXPECT_EQ("tag3", statsLogEventWrapper.getWorkChains()[1].tags[0]);
+    EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
+    EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
+    EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
+    EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].uids.size());
+    EXPECT_EQ(4, statsLogEventWrapper.getWorkChains()[2].uids[0]);
+    EXPECT_EQ(5, statsLogEventWrapper.getWorkChains()[2].uids[1]);
+    EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].tags.size());
+    EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[0]);
+    EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[1]);
+
+    LogEvent event(statsLogEventWrapper, -1);
+    EXPECT_EQ(1, event.GetTagId());
+    EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
+    EXPECT_EQ(2222L, event.GetLogdTimestampNs());
+    EXPECT_EQ(2, event.size());
+    EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
+    EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
+
+    LogEvent event1(statsLogEventWrapper, 0);
+
+    EXPECT_EQ(1, event1.GetTagId());
+    EXPECT_EQ(1111L, event1.GetElapsedTimestampNs());
+    EXPECT_EQ(2222L, event1.GetLogdTimestampNs());
+    EXPECT_EQ(6, event1.size());
+    EXPECT_EQ(1, event1.getValues()[0].mValue.int_value);
+    EXPECT_EQ(0x2010101, event1.getValues()[0].mField.getField());
+    EXPECT_EQ("tag1", event1.getValues()[1].mValue.str_value);
+    EXPECT_EQ(0x2010182, event1.getValues()[1].mField.getField());
+    EXPECT_EQ(2, event1.getValues()[2].mValue.int_value);
+    EXPECT_EQ(0x2010201, event1.getValues()[2].mField.getField());
+    EXPECT_EQ("tag2", event1.getValues()[3].mValue.str_value);
+    EXPECT_EQ(0x2018282, event1.getValues()[3].mField.getField());
+    EXPECT_EQ(6, event1.getValues()[4].mValue.int_value);
+    EXPECT_EQ(0x20000, event1.getValues()[4].mField.getField());
+    EXPECT_EQ(10, event1.getValues()[5].mValue.long_value);
+    EXPECT_EQ(0x30000, event1.getValues()[5].mField.getField());
+
+    LogEvent event2(statsLogEventWrapper, 1);
+
+    EXPECT_EQ(1, event2.GetTagId());
+    EXPECT_EQ(1111L, event2.GetElapsedTimestampNs());
+    EXPECT_EQ(2222L, event2.GetLogdTimestampNs());
+    EXPECT_EQ(4, event2.size());
+    EXPECT_EQ(3, event2.getValues()[0].mValue.int_value);
+    EXPECT_EQ(0x2010101, event2.getValues()[0].mField.getField());
+    EXPECT_EQ("tag3", event2.getValues()[1].mValue.str_value);
+    EXPECT_EQ(0x2018182, event2.getValues()[1].mField.getField());
+    EXPECT_EQ(6, event2.getValues()[2].mValue.int_value);
+    EXPECT_EQ(0x20000, event2.getValues()[2].mField.getField());
+    EXPECT_EQ(10, event2.getValues()[3].mValue.long_value);
+    EXPECT_EQ(0x30000, event2.getValues()[3].mField.getField());
+
+    LogEvent event3(statsLogEventWrapper, 2);
+
+    EXPECT_EQ(1, event3.GetTagId());
+    EXPECT_EQ(1111L, event3.GetElapsedTimestampNs());
+    EXPECT_EQ(2222L, event3.GetLogdTimestampNs());
+    EXPECT_EQ(6, event3.size());
+    EXPECT_EQ(4, event3.getValues()[0].mValue.int_value);
+    EXPECT_EQ(0x2010101, event3.getValues()[0].mField.getField());
+    EXPECT_EQ("", event3.getValues()[1].mValue.str_value);
+    EXPECT_EQ(0x2010182, event3.getValues()[1].mField.getField());
+    EXPECT_EQ(5, event3.getValues()[2].mValue.int_value);
+    EXPECT_EQ(0x2010201, event3.getValues()[2].mField.getField());
+    EXPECT_EQ("", event3.getValues()[3].mValue.str_value);
+    EXPECT_EQ(0x2018282, event3.getValues()[3].mField.getField());
+    EXPECT_EQ(6, event3.getValues()[4].mValue.int_value);
+    EXPECT_EQ(0x20000, event3.getValues()[4].mField.getField());
+    EXPECT_EQ(10, event3.getValues()[5].mValue.long_value);
+    EXPECT_EQ(0x30000, event3.getValues()[5].mField.getField());
+}
 
 TEST(LogEventTest, TestBinaryFieldAtom) {
     Atom launcherAtom;
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
index fc6e420..266ea35 100644
--- a/cmds/statsd/tests/external/puller_util_test.cpp
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -80,7 +80,7 @@
       .WillRepeatedly(Return(hostUid));
   EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
       .WillRepeatedly(ReturnArg<0>());
-  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
 
   vector<vector<int>> actual;
   extractIntoVector(inputData, actual);
@@ -120,7 +120,7 @@
       .WillRepeatedly(Return(hostUid));
   EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
       .WillRepeatedly(ReturnArg<0>());
-  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
 
   vector<vector<int>> actual;
   extractIntoVector(inputData, actual);
@@ -154,7 +154,7 @@
       .WillRepeatedly(Return(hostUid));
   EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
       .WillRepeatedly(ReturnArg<0>());
-  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
 
   // 20->32->31
   // 20->22->21
@@ -190,7 +190,7 @@
       .WillRepeatedly(Return(hostUid));
   EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
       .WillRepeatedly(ReturnArg<0>());
-  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
 
   // 20->32->31
   // 20->22->21
@@ -231,7 +231,7 @@
 
   sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
   EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
-  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
 
   vector<vector<int>> actual;
   extractIntoVector(inputData, actual);
@@ -256,7 +256,7 @@
   inputData.push_back(event);
 
   sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-  mergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
 
   EXPECT_EQ(2, (int)inputData.size());
 }
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 2dcf50f..c2e441b 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -24296,7 +24296,8 @@
 HSPLandroid/icu/util/CodePointMap;->getRange(ILandroid/icu/util/CodePointMap$RangeOption;ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
 HSPLandroid/icu/util/CodePointMap;->getRange(ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
 HSPLandroid/icu/util/CodePointMap;->iterator()Ljava/util/Iterator;
-HSPLandroid/icu/util/CodePointMap;->stringIterator(Ljava/lang/CharSequence;I)Landroid/icu/util/CodePointMap$StringIterator;HSPLandroid/icu/util/Currency$1;-><init>()V
+HSPLandroid/icu/util/CodePointMap;->stringIterator(Ljava/lang/CharSequence;I)Landroid/icu/util/CodePointMap$StringIterator;
+HSPLandroid/icu/util/Currency$1;-><init>()V
 HSPLandroid/icu/util/Currency$1;->createInstance(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
 HSPLandroid/icu/util/Currency$1;->createInstance(Ljava/lang/String;Ljava/lang/Void;)Landroid/icu/util/Currency;
 HSPLandroid/icu/util/Currency$CurrencyUsage;-><init>(Ljava/lang/String;I)V
@@ -32940,8 +32941,8 @@
 HSPLandroid/view/IWindowManager;->isWindowTraceEnabled()Z
 HSPLandroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V
 HSPLandroid/view/IWindowManager;->openSession(Landroid/view/IWindowSessionCallback;Lcom/android/internal/view/IInputMethodClient;Lcom/android/internal/view/IInputContext;)Landroid/view/IWindowSession;
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
+HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z;I)V
+HSPLandroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
 HSPLandroid/view/IWindowManager;->prepareAppTransition(IZ)V
 HSPLandroid/view/IWindowManager;->reenableKeyguard(Landroid/os/IBinder;)V
 HSPLandroid/view/IWindowManager;->refreshScreenCaptureDisabled(I)V
@@ -41154,7 +41155,7 @@
 HSPLcom/android/internal/telephony/TimeServiceHelper;->setListener(Lcom/android/internal/telephony/TimeServiceHelper$Listener;)V
 HSPLcom/android/internal/telephony/TimeZoneLookupHelper$CountryResult;->toString()Ljava/lang/String;
 HSPLcom/android/internal/telephony/TimeZoneLookupHelper$OffsetResult;->toString()Ljava/lang/String;
-HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->getCountryTimeZones(Ljava/lang/String;)Llibcore/util/CountryTimeZones;
+HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->getCountryTimeZones(Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
 HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->lookupByCountry(Ljava/lang/String;J)Lcom/android/internal/telephony/TimeZoneLookupHelper$CountryResult;
 HSPLcom/android/internal/telephony/TimeZoneLookupHelper;->lookupByNitzCountry(Lcom/android/internal/telephony/NitzData;Ljava/lang/String;)Lcom/android/internal/telephony/TimeZoneLookupHelper$OffsetResult;
 HSPLcom/android/internal/telephony/UiccSmsController;->disableCellBroadcastRangeForSubscriber(IIII)Z
@@ -51930,7 +51931,46 @@
 HSPLlibcore/reflect/Types;->getTypeArray(Llibcore/reflect/ListOfTypes;Z)[Ljava/lang/reflect/Type;
 HSPLlibcore/reflect/WildcardTypeImpl;->getLowerBounds()[Ljava/lang/reflect/Type;
 HSPLlibcore/reflect/WildcardTypeImpl;->getUpperBounds()[Ljava/lang/reflect/Type;
-HSPLlibcore/util/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/CountryTimeZones;->createValidated(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
+HSPLlibcore/timezone/CountryTimeZones;->getDefaultTimeZone()Landroid/icu/util/TimeZone;
+HSPLlibcore/timezone/CountryTimeZones;->getIcuTimeZones()Ljava/util/List;
+HSPLlibcore/timezone/CountryTimeZones;->isDefaultOkForCountryTimeZoneDetection(J)Z
+HSPLlibcore/timezone/CountryTimeZones;->isForCountryCode(Ljava/lang/String;)Z
+HSPLlibcore/timezone/CountryTimeZones;->lookupByOffsetWithBias(IZJLandroid/icu/util/TimeZone;)Llibcore/timezone/CountryTimeZones$OffsetResult;
+HSPLlibcore/timezone/TimeZoneDataFiles;->generateIcuDataPath()Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneDataFiles;->getTimeZoneFilePaths(Ljava/lang/String;)[Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneFinder$ReaderSupplier;->forFile(Ljava/lang/String;Ljava/nio/charset/Charset;)Llibcore/timezone/TimeZoneFinder$ReaderSupplier;
+HSPLlibcore/timezone/TimeZoneFinder$ReaderSupplier;->get()Ljava/io/Reader;
+HSPLlibcore/timezone/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;->processCountryZones(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Z
+HSPLlibcore/timezone/TimeZoneFinder$TimeZonesProcessor;->processHeader(Ljava/lang/String;)Z
+HSPLlibcore/timezone/TimeZoneFinder;->checkOnEndTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;)V
+HSPLlibcore/timezone/TimeZoneFinder;->consumeText(Lorg/xmlpull/v1/XmlPullParser;)Ljava/lang/String;
+HSPLlibcore/timezone/TimeZoneFinder;->createInstanceWithFallback([Ljava/lang/String;)Llibcore/timezone/TimeZoneFinder;
+HSPLlibcore/timezone/TimeZoneFinder;->findStartTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Z)Z
+HSPLlibcore/timezone/TimeZoneFinder;->getInstance()Llibcore/timezone/TimeZoneFinder;
+HSPLlibcore/timezone/TimeZoneFinder;->lookupCountryTimeZones(Ljava/lang/String;)Llibcore/timezone/CountryTimeZones;
+HSPLlibcore/timezone/TimeZoneFinder;->parseBooleanAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/Boolean;
+HSPLlibcore/timezone/TimeZoneFinder;->parseLongAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;
+HSPLlibcore/timezone/TimeZoneFinder;->parseTimeZoneMappings(Lorg/xmlpull/v1/XmlPullParser;)Ljava/util/List;
+HSPLlibcore/timezone/TimeZoneFinder;->processCountryZones(Lorg/xmlpull/v1/XmlPullParser;Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)Z
+HSPLlibcore/timezone/TimeZoneFinder;->processXml(Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData$1;->create(Ljava/lang/Object;)Ljava/lang/Object;
+HSPLlibcore/timezone/ZoneInfoDB$TzData$1;->create(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->close()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->finalize()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->getAvailableIDs()[Ljava/lang/String;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->getBufferIterator(Ljava/lang/String;)Llibcore/io/BufferIterator;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->hasTimeZone(Ljava/lang/String;)Z
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->loadData(Ljava/lang/String;)Z
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->loadTzDataWithFallback([Ljava/lang/String;)Llibcore/timezone/ZoneInfoDB$TzData;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->makeTimeZone(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->makeTimeZoneUncached(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readHeader()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readIndex(Llibcore/io/BufferIterator;II)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readZoneTab(Llibcore/io/BufferIterator;II)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->validateOffset(II)V
+HSPLlibcore/timezone/ZoneInfoDB;->getInstance()Llibcore/timezone/ZoneInfoDB$TzData;
 HSPLlibcore/util/BasicLruCache;-><init>(I)V
 HSPLlibcore/util/BasicLruCache;->create(Ljava/lang/Object;)Ljava/lang/Object;
 HSPLlibcore/util/BasicLruCache;->entryEvicted(Ljava/lang/Object;Ljava/lang/Object;)V
@@ -51938,12 +51978,6 @@
 HSPLlibcore/util/BasicLruCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
 HSPLlibcore/util/BasicLruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
 HSPLlibcore/util/CollectionUtils;->removeDuplicates(Ljava/util/List;Ljava/util/Comparator;)V
-HSPLlibcore/util/CountryTimeZones;->createValidated(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Llibcore/util/CountryTimeZones;
-HSPLlibcore/util/CountryTimeZones;->getDefaultTimeZone()Landroid/icu/util/TimeZone;
-HSPLlibcore/util/CountryTimeZones;->getIcuTimeZones()Ljava/util/List;
-HSPLlibcore/util/CountryTimeZones;->isDefaultOkForCountryTimeZoneDetection(J)Z
-HSPLlibcore/util/CountryTimeZones;->isForCountryCode(Ljava/lang/String;)Z
-HSPLlibcore/util/CountryTimeZones;->lookupByOffsetWithBias(IZJLandroid/icu/util/TimeZone;)Llibcore/util/CountryTimeZones$OffsetResult;
 HSPLlibcore/util/HexEncoding;->encode([BII)[C
 HSPLlibcore/util/NativeAllocationRegistry$CleanerRunner;->run()V
 HSPLlibcore/util/NativeAllocationRegistry$CleanerThunk;->run()V
@@ -51951,23 +51985,6 @@
 HSPLlibcore/util/NativeAllocationRegistry;->registerNativeAllocation(Ljava/lang/Object;J)Ljava/lang/Runnable;
 HSPLlibcore/util/SneakyThrow;->sneakyThrow(Ljava/lang/Throwable;)V
 HSPLlibcore/util/SneakyThrow;->sneakyThrow_(Ljava/lang/Throwable;)V
-HSPLlibcore/util/TimeZoneDataFiles;->generateIcuDataPath()Ljava/lang/String;
-HSPLlibcore/util/TimeZoneDataFiles;->getTimeZoneFilePaths(Ljava/lang/String;)[Ljava/lang/String;
-HSPLlibcore/util/TimeZoneFinder$ReaderSupplier;->forFile(Ljava/lang/String;Ljava/nio/charset/Charset;)Llibcore/util/TimeZoneFinder$ReaderSupplier;
-HSPLlibcore/util/TimeZoneFinder$ReaderSupplier;->get()Ljava/io/Reader;
-HSPLlibcore/util/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;->processCountryZones(Ljava/lang/String;Ljava/lang/String;ZLjava/util/List;Ljava/lang/String;)Z
-HSPLlibcore/util/TimeZoneFinder$TimeZonesProcessor;->processHeader(Ljava/lang/String;)Z
-HSPLlibcore/util/TimeZoneFinder;->checkOnEndTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;)V
-HSPLlibcore/util/TimeZoneFinder;->consumeText(Lorg/xmlpull/v1/XmlPullParser;)Ljava/lang/String;
-HSPLlibcore/util/TimeZoneFinder;->createInstanceWithFallback([Ljava/lang/String;)Llibcore/util/TimeZoneFinder;
-HSPLlibcore/util/TimeZoneFinder;->findStartTag(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Z)Z
-HSPLlibcore/util/TimeZoneFinder;->getInstance()Llibcore/util/TimeZoneFinder;
-HSPLlibcore/util/TimeZoneFinder;->lookupCountryTimeZones(Ljava/lang/String;)Llibcore/util/CountryTimeZones;
-HSPLlibcore/util/TimeZoneFinder;->parseBooleanAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Boolean;)Ljava/lang/Boolean;
-HSPLlibcore/util/TimeZoneFinder;->parseLongAttribute(Lorg/xmlpull/v1/XmlPullParser;Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;
-HSPLlibcore/util/TimeZoneFinder;->parseTimeZoneMappings(Lorg/xmlpull/v1/XmlPullParser;)Ljava/util/List;
-HSPLlibcore/util/TimeZoneFinder;->processCountryZones(Lorg/xmlpull/v1/XmlPullParser;Llibcore/util/TimeZoneFinder$TimeZonesProcessor;)Z
-HSPLlibcore/util/TimeZoneFinder;->processXml(Llibcore/util/TimeZoneFinder$TimeZonesProcessor;)V
 HSPLlibcore/util/ZoneInfo$WallTime;-><init>()V
 HSPLlibcore/util/ZoneInfo$WallTime;->copyFieldsFromCalendar()V
 HSPLlibcore/util/ZoneInfo$WallTime;->copyFieldsToCalendar()V
@@ -52007,22 +52024,6 @@
 HSPLlibcore/util/ZoneInfo;->hashCode()I
 HSPLlibcore/util/ZoneInfo;->inDaylightTime(Ljava/util/Date;)Z
 HSPLlibcore/util/ZoneInfo;->readTimeZone(Ljava/lang/String;Llibcore/io/BufferIterator;J)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData$1;->create(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLlibcore/util/ZoneInfoDB$TzData$1;->create(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->close()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->finalize()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->getAvailableIDs()[Ljava/lang/String;
-HSPLlibcore/util/ZoneInfoDB$TzData;->getBufferIterator(Ljava/lang/String;)Llibcore/io/BufferIterator;
-HSPLlibcore/util/ZoneInfoDB$TzData;->hasTimeZone(Ljava/lang/String;)Z
-HSPLlibcore/util/ZoneInfoDB$TzData;->loadData(Ljava/lang/String;)Z
-HSPLlibcore/util/ZoneInfoDB$TzData;->loadTzDataWithFallback([Ljava/lang/String;)Llibcore/util/ZoneInfoDB$TzData;
-HSPLlibcore/util/ZoneInfoDB$TzData;->makeTimeZone(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->makeTimeZoneUncached(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->readHeader()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->readIndex(Llibcore/io/BufferIterator;II)V
-HSPLlibcore/util/ZoneInfoDB$TzData;->readZoneTab(Llibcore/io/BufferIterator;II)V
-HSPLlibcore/util/ZoneInfoDB$TzData;->validateOffset(II)V
-HSPLlibcore/util/ZoneInfoDB;->getInstance()Llibcore/util/ZoneInfoDB$TzData;
 HSPLorg/apache/harmony/dalvik/ddmc/Chunk;-><init>(ILjava/nio/ByteBuffer;)V
 HSPLorg/apache/harmony/dalvik/ddmc/ChunkHandler;->putString(Ljava/nio/ByteBuffer;Ljava/lang/String;)V
 HSPLorg/apache/harmony/dalvik/ddmc/ChunkHandler;->type(Ljava/lang/String;)I
@@ -63455,13 +63456,21 @@
 Llibcore/reflect/TypeVariableImpl;
 Llibcore/reflect/Types;
 Llibcore/reflect/WildcardTypeImpl;
-Llibcore/util/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;
+Llibcore/timezone/-$$Lambda$TimeZoneFinder$ReaderSupplier$IAVNuAYizGfcsPtGXEBkDPhlBF0;
+Llibcore/timezone/CountryTimeZones$OffsetResult;
+Llibcore/timezone/CountryTimeZones$TimeZoneMapping;
+Llibcore/timezone/CountryTimeZones;
+Llibcore/timezone/TimeZoneDataFiles;
+Llibcore/timezone/TimeZoneFinder$ReaderSupplier;
+Llibcore/timezone/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;
+Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;
+Llibcore/timezone/TimeZoneFinder;
+Llibcore/timezone/ZoneInfoDB$TzData$1;
+Llibcore/timezone/ZoneInfoDB$TzData;
+Llibcore/timezone/ZoneInfoDB;
 Llibcore/util/BasicLruCache;
 Llibcore/util/CharsetUtils;
 Llibcore/util/CollectionUtils;
-Llibcore/util/CountryTimeZones$OffsetResult;
-Llibcore/util/CountryTimeZones$TimeZoneMapping;
-Llibcore/util/CountryTimeZones;
 Llibcore/util/EmptyArray;
 Llibcore/util/HexEncoding;
 Llibcore/util/NativeAllocationRegistry$CleanerRunner;
@@ -63469,18 +63478,10 @@
 Llibcore/util/NativeAllocationRegistry;
 Llibcore/util/Objects;
 Llibcore/util/SneakyThrow;
-Llibcore/util/TimeZoneDataFiles;
-Llibcore/util/TimeZoneFinder$ReaderSupplier;
-Llibcore/util/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;
-Llibcore/util/TimeZoneFinder$TimeZonesProcessor;
-Llibcore/util/TimeZoneFinder;
 Llibcore/util/ZoneInfo$CheckedArithmeticException;
 Llibcore/util/ZoneInfo$OffsetInterval;
 Llibcore/util/ZoneInfo$WallTime;
 Llibcore/util/ZoneInfo;
-Llibcore/util/ZoneInfoDB$TzData$1;
-Llibcore/util/ZoneInfoDB$TzData;
-Llibcore/util/ZoneInfoDB;
 Lorg/apache/harmony/dalvik/NativeTestTarget;
 Lorg/apache/harmony/dalvik/ddmc/Chunk;
 Lorg/apache/harmony/dalvik/ddmc/ChunkHandler;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 8a770b9..25bd033 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -189,10 +189,10 @@
 Landroid/app/IUiModeManager;->disableCarMode(I)V
 Landroid/app/IUserSwitchObserver$Stub;-><init>()V
 Landroid/app/IWallpaperManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IWallpaperManager;
-Landroid/app/IWallpaperManager;->getHeightHint()I
+Landroid/app/IWallpaperManager;->getHeightHint(I)I
 Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
 Landroid/app/IWallpaperManager;->getWallpaperInfo(I)Landroid/app/WallpaperInfo;
-Landroid/app/IWallpaperManager;->getWidthHint()I
+Landroid/app/IWallpaperManager;->getWidthHint(I)I
 Landroid/app/IWallpaperManager;->hasNamedWallpaper(Ljava/lang/String;)Z
 Landroid/app/IWallpaperManager;->setWallpaperComponent(Landroid/content/ComponentName;)V
 Landroid/app/IWallpaperManagerCallback$Stub;-><init>()V
@@ -1435,7 +1435,7 @@
 Landroid/view/IWindowManager$Stub$Proxy;->getBaseDisplayDensity(I)I
 Landroid/view/IWindowManager$Stub$Proxy;->getDockedStackSide()I
 Landroid/view/IWindowManager$Stub$Proxy;->getInitialDisplayDensity(I)I
-Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar()Z
+Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar(I)Z
 Landroid/view/IWindowManager$Stub$Proxy;->watchRotation(Landroid/view/IRotationWatcher;I)I
 Landroid/view/IWindowManager$Stub;-><init>()V
 Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
@@ -1447,7 +1447,7 @@
 Landroid/view/IWindowManager;->getDockedStackSide()I
 Landroid/view/IWindowManager;->getInitialDisplayDensity(I)I
 Landroid/view/IWindowManager;->getInitialDisplaySize(ILandroid/graphics/Point;)V
-Landroid/view/IWindowManager;->hasNavigationBar()Z
+Landroid/view/IWindowManager;->hasNavigationBar(I)Z
 Landroid/view/IWindowManager;->isKeyguardLocked()Z
 Landroid/view/IWindowManager;->isKeyguardSecure()Z
 Landroid/view/IWindowManager;->isSafeModeEnabled()Z
@@ -1461,7 +1461,6 @@
 Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
 Landroid/view/IWindowManager;->showStrictModeViolation(Z)V
 Landroid/view/IWindowManager;->thawRotation()V
-Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
 Landroid/view/IWindowSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowSession;
 Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V
 Landroid/view/IWindowSession;->getInTouchMode()Z
@@ -1488,86 +1487,10 @@
 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/AsYouTypeFormatter;->clear()V
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->getRememberedPosition()I
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigit(C)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;->inputDigitAndRememberPosition(C)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getDescriptionForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/util/Locale;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;->getInstance()Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;
-Lcom/android/i18n/phonenumbers/NumberParseException;->getErrorType()Lcom/android/i18n/phonenumbers/NumberParseException$ErrorType;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getDomesticCarrierCodeFormattingRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getFormat()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getLeadingDigitsPattern(I)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getNationalPrefixFormattingRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->getPattern()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$NumberFormat;->leadingDigitsPatternSize()I
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getCountryCode()I
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getGeneralDesc()Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixForParsing()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getNationalPrefixTransformRule()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->getPreferredExtnPrefix()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasNationalPrefix()Z
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->hasPreferredExtnPrefix()Z
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->intlNumberFormats()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadata;->numberFormats()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;-><init>()V
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneMetadataCollection;->getMetadataList()Ljava/util/List;
-Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;->getNationalNumberPattern()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_DEFAULT_COUNTRY:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITHOUT_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_IDD:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->FROM_NUMBER_WITH_PLUS_SIGN:Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
 Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->values()[Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->clearCountryCode()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCode()I
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getCountryCodeSource()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getExtension()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->getNationalNumber()J
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasCountryCode()Z
-Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->hasExtension()Z
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->end()I
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->number()Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->rawString()Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberMatch;->start()I
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;->POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->EXACT_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NOT_A_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NO_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->SHORT_NSN_MATCH:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
 Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->E164:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->INTERNATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->NATIONAL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->RFC3966:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
 Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->FIXED_LINE_OR_MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->MOBILE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PAGER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PERSONAL_NUMBER:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->PREMIUM_RATE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->SHARED_COST:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->TOLL_FREE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->UAN:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
 Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->values()[Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOICEMAIL:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->VOIP:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->IS_POSSIBLE:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->TOO_LONG:Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->findNumbers(Ljava/lang/CharSequence;Ljava/lang/String;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$Leniency;J)Ljava/lang/Iterable;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->format(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->formatInOriginalFormat(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Ljava/lang/String;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getAsYouTypeFormatter(Ljava/lang/String;)Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getCountryCodeForRegion(Ljava/lang/String;)I
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getInstance()Lcom/android/i18n/phonenumbers/PhoneNumberUtil;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNationalSignificantNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getNumberType(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->getRegionCodeForNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Ljava/lang/String;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isNumberMatch(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$MatchType;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isPossibleNumberWithReason(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Lcom/android/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;
-Lcom/android/i18n/phonenumbers/PhoneNumberUtil;->isValidNumber(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;)Z
 Lcom/android/ims/ImsCall;->deflect(Ljava/lang/String;)V
 Lcom/android/ims/ImsCall;->isMultiparty()Z
 Lcom/android/ims/ImsCall;->reject(I)V
@@ -2691,7 +2614,6 @@
 Lcom/android/internal/telephony/dataconnection/DataConnection;->log(Ljava/lang/String;)V
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mActivatingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mApnContexts:Ljava/util/HashMap;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mDcFailCause:Lcom/android/internal/telephony/dataconnection/DcFailCause;
@@ -4139,77 +4061,6 @@
 Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageScrollStateChanged(I)V
 Lcom/android/internal/widget/ViewPager$OnPageChangeListener;->onPageSelected(I)V
 Lcom/android/internal/widget/ViewPager;->getCurrentItem()I
-Lcom/android/okhttp/Connection;->getSocket()Ljava/net/Socket;
-Lcom/android/okhttp/ConnectionPool;->connections:Ljava/util/Deque;
-Lcom/android/okhttp/ConnectionPool;->keepAliveDurationNs:J
-Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I
-Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/HttpHandler;-><init>()V
-Lcom/android/okhttp/HttpsHandler;-><init>()V
-Lcom/android/okhttp/HttpUrl$Builder;->build()Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/HttpUrl;->encodedPath()Ljava/lang/String;
-Lcom/android/okhttp/HttpUrl;->newBuilder()Lcom/android/okhttp/HttpUrl$Builder;
-Lcom/android/okhttp/HttpUrl;->parse(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/HttpUrl;->query()Ljava/lang/String;
-Lcom/android/okhttp/internal/http/HeaderParser;->skipUntil(Ljava/lang/String;ILjava/lang/String;)I
-Lcom/android/okhttp/internal/http/HeaderParser;->skipWhitespace(Ljava/lang/String;I)I
-Lcom/android/okhttp/internal/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String;
-Lcom/android/okhttp/internal/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date;
-Lcom/android/okhttp/internal/http/HttpEngine;->getConnection()Lcom/android/okhttp/Connection;
-Lcom/android/okhttp/internal/http/HttpEngine;->hasResponse()Z
-Lcom/android/okhttp/internal/http/HttpEngine;->httpStream:Lcom/android/okhttp/internal/http/HttpStream;
-Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest(Lcom/android/okhttp/Request;)Lcom/android/okhttp/Request;
-Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest:Lcom/android/okhttp/Request;
-Lcom/android/okhttp/internal/http/HttpEngine;->priorResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/internal/http/HttpEngine;->readResponse()V
-Lcom/android/okhttp/internal/http/HttpEngine;->sendRequest()V
-Lcom/android/okhttp/internal/http/HttpEngine;->sentRequestMillis:J
-Lcom/android/okhttp/internal/http/HttpEngine;->userResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/internal/http/HttpEngine;->writingRequestHeaders()V
-Lcom/android/okhttp/internal/http/RouteSelector;->hasNext()Z
-Lcom/android/okhttp/internal/huc/HttpsURLConnectionImpl;->delegate:Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;
-Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->client:Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/internal/huc/HttpURLConnectionImpl;->httpEngine:Lcom/android/okhttp/internal/http/HttpEngine;
-Lcom/android/okhttp/internal/Internal;-><init>()V
-Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Internal;->addLenient(Lcom/android/okhttp/Headers$Builder;Ljava/lang/String;Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Internal;->apply(Lcom/android/okhttp/ConnectionSpec;Ljavax/net/ssl/SSLSocket;Z)V
-Lcom/android/okhttp/internal/Internal;->callEngineGetStreamAllocation(Lcom/android/okhttp/Call;)Lcom/android/okhttp/internal/http/StreamAllocation;
-Lcom/android/okhttp/internal/Internal;->callEnqueue(Lcom/android/okhttp/Call;Lcom/android/okhttp/Callback;Z)V
-Lcom/android/okhttp/internal/Internal;->connectionBecameIdle(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)Z
-Lcom/android/okhttp/internal/Internal;->get(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/Address;Lcom/android/okhttp/internal/http/StreamAllocation;)Lcom/android/okhttp/internal/io/RealConnection;
-Lcom/android/okhttp/internal/Internal;->getHttpUrlChecked(Ljava/lang/String;)Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/internal/Internal;->instance:Lcom/android/okhttp/internal/Internal;
-Lcom/android/okhttp/internal/Internal;->internalCache(Lcom/android/okhttp/OkHttpClient;)Lcom/android/okhttp/internal/InternalCache;
-Lcom/android/okhttp/internal/Internal;->put(Lcom/android/okhttp/ConnectionPool;Lcom/android/okhttp/internal/io/RealConnection;)V
-Lcom/android/okhttp/internal/Internal;->routeDatabase(Lcom/android/okhttp/ConnectionPool;)Lcom/android/okhttp/internal/RouteDatabase;
-Lcom/android/okhttp/internal/Internal;->setCache(Lcom/android/okhttp/OkHttpClient;Lcom/android/okhttp/internal/InternalCache;)V
-Lcom/android/okhttp/internal/Platform;->get()Lcom/android/okhttp/internal/Platform;
-Lcom/android/okhttp/internal/Platform;->logW(Ljava/lang/String;)V
-Lcom/android/okhttp/internal/Util;->closeAll(Ljava/io/Closeable;Ljava/io/Closeable;)V
-Lcom/android/okhttp/internal/Util;->closeQuietly(Ljava/io/Closeable;)V
-Lcom/android/okhttp/internal/Util;->EMPTY_BYTE_ARRAY:[B
-Lcom/android/okhttp/internal/Util;->UTF_8:Ljava/nio/charset/Charset;
-Lcom/android/okhttp/OkHttpClient;-><init>()V
-Lcom/android/okhttp/OkHttpClient;->connectionPool:Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/OkHttpClient;->DEFAULT_PROTOCOLS:Ljava/util/List;
-Lcom/android/okhttp/OkHttpClient;->dns:Lcom/android/okhttp/Dns;
-Lcom/android/okhttp/OkHttpClient;->getConnectionPool()Lcom/android/okhttp/ConnectionPool;
-Lcom/android/okhttp/OkHttpClient;->getCookieHandler()Ljava/net/CookieHandler;
-Lcom/android/okhttp/OkHttpClient;->getHostnameVerifier()Ljavax/net/ssl/HostnameVerifier;
-Lcom/android/okhttp/OkHttpClient;->getProxy()Ljava/net/Proxy;
-Lcom/android/okhttp/OkHttpClient;->getProxySelector()Ljava/net/ProxySelector;
-Lcom/android/okhttp/OkHttpClient;->getSslSocketFactory()Ljavax/net/ssl/SSLSocketFactory;
-Lcom/android/okhttp/OkHttpClient;->setProtocols(Ljava/util/List;)Lcom/android/okhttp/OkHttpClient;
-Lcom/android/okhttp/OkHttpClient;->setRetryOnConnectionFailure(Z)V
-Lcom/android/okhttp/Request;->headers:Lcom/android/okhttp/Headers;
-Lcom/android/okhttp/Request;->method:Ljava/lang/String;
-Lcom/android/okhttp/Request;->url:Lcom/android/okhttp/HttpUrl;
-Lcom/android/okhttp/Response;->code:I
-Lcom/android/okhttp/Response;->headers:Lcom/android/okhttp/Headers;
-Lcom/android/okhttp/Response;->message:Ljava/lang/String;
-Lcom/android/okhttp/Response;->networkResponse:Lcom/android/okhttp/Response;
-Lcom/android/okhttp/Response;->protocol:Lcom/android/okhttp/Protocol;
 Lcom/android/server/net/BaseNetworkObserver;-><init>()V
 Lcom/android/server/net/NetlinkTracker;-><init>(Ljava/lang/String;Lcom/android/server/net/NetlinkTracker$Callback;)V
 Lcom/android/server/net/NetlinkTracker;->clearLinkProperties()V
@@ -4515,7 +4366,6 @@
 Lcom/google/android/util/AbstractMessageParser$Token$Type;->SMILEY:Lcom/google/android/util/AbstractMessageParser$Token$Type;
 Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
 Lcom/google/android/util/AbstractMessageParser$Token$Type;->YOUTUBE_VIDEO:Lcom/google/android/util/AbstractMessageParser$Token$Type;
-Lcom/sun/nio/file/ExtendedWatchEventModifier;->FILE_TREE:Lcom/sun/nio/file/ExtendedWatchEventModifier;
 Lgov/nist/core/Debug;->printStackTrace(Ljava/lang/Exception;)V
 Lgov/nist/core/GenericObject;-><init>()V
 Lgov/nist/core/GenericObject;->dbgPrint()V
@@ -4655,178 +4505,9 @@
 Lgov/nist/javax/sip/address/SipUri;->setUserParam(Ljava/lang/String;)V
 Lgov/nist/javax/sip/parser/URLParser;-><init>(Ljava/lang/String;)V
 Lgov/nist/javax/sip/parser/URLParser;->sipURL(Z)Lgov/nist/javax/sip/address/SipUri;
-Ljava/lang/DexCache;->dexFile:J
-Ljava/lang/invoke/SerializedLambda;-><init>(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
-Ljava/lang/invoke/SerializedLambda;->getCapturedArg(I)Ljava/lang/Object;
-Ljava/lang/invoke/SerializedLambda;->getCapturedArgCount()I
-Ljava/lang/invoke/SerializedLambda;->getCapturingClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodName()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodSignature()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplMethodKind()I
-Ljava/lang/invoke/SerializedLambda;->getImplMethodName()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplMethodSignature()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getInstantiatedMethodType()Ljava/lang/String;
-Ljava/lang/UNIXProcess;->pid:I
-Ljava/net/AddressCache$AddressCacheEntry;-><init>(Ljava/lang/Object;)V
-Ljava/net/AddressCache$AddressCacheEntry;->expiryNanos:J
-Ljava/net/AddressCache$AddressCacheEntry;->value:Ljava/lang/Object;
-Ljava/net/AddressCache$AddressCacheKey;->mHostname:Ljava/lang/String;
-Ljava/net/AddressCache;->cache:Llibcore/util/BasicLruCache;
-Ljava/net/Inet6AddressImpl;->addressCache:Ljava/net/AddressCache;
-Ljava/net/PlainSocketImpl;-><init>()V
-Ljava/nio/DirectByteBuffer;->cleaner()Lsun/misc/Cleaner;
-Ljava/nio/file/FileTreeWalker;->followLinks:Z
-Ljava/nio/file/FileTreeWalker;->linkOptions:[Ljava/nio/file/LinkOption;
-Ljava/nio/file/FileTreeWalker;->maxDepth:I
-Ljava/util/zip/ZipFile$ZipEntryIterator;->nextElement()Ljava/util/zip/ZipEntry;
 Ljunit/framework/TestCase;->fName:Ljava/lang/String;
 Ljunit/framework/TestSuite;->isPublicTestMethod(Ljava/lang/reflect/Method;)Z
 Ljunit/framework/TestSuite;->isTestMethod(Ljava/lang/reflect/Method;)Z
-Llibcore/icu/DateIntervalFormat;->formatDateRange(JJILjava/lang/String;)Ljava/lang/String;
-Llibcore/icu/ICU;->CACHED_PATTERNS:Llibcore/util/BasicLruCache;
-Llibcore/icu/ICU;->getBestDateTimePattern(Ljava/lang/String;Ljava/util/Locale;)Ljava/lang/String;
-Llibcore/icu/ICU;->getBestDateTimePatternNative(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-Llibcore/icu/ICU;->getDateFormatOrder(Ljava/lang/String;)[C
-Llibcore/icu/LocaleData;->firstDayOfWeek:Ljava/lang/Integer;
-Llibcore/icu/LocaleData;->get(Ljava/util/Locale;)Llibcore/icu/LocaleData;
-Llibcore/icu/LocaleData;->longStandAloneWeekdayNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->mapInvalidAndNullLocales(Ljava/util/Locale;)Ljava/util/Locale;
-Llibcore/icu/LocaleData;->minimalDaysInFirstWeek:Ljava/lang/Integer;
-Llibcore/icu/LocaleData;->shortMonthNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->shortStandAloneMonthNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->shortStandAloneWeekdayNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->timeFormat_Hm:Ljava/lang/String;
-Llibcore/icu/LocaleData;->timeFormat_hm:Ljava/lang/String;
-Llibcore/icu/LocaleData;->today:Ljava/lang/String;
-Llibcore/icu/LocaleData;->tomorrow:Ljava/lang/String;
-Llibcore/icu/LocaleData;->zeroDigit:C
-Llibcore/icu/TimeZoneNames;->forLocale(Ljava/util/Locale;)[Ljava/lang/String;
-Llibcore/io/AsynchronousCloseMonitor;->signalBlockedThreads(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;-><init>(Llibcore/io/Os;)V
-Llibcore/io/BlockGuardOs;->chmod(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->chown(Ljava/lang/String;II)V
-Llibcore/io/BlockGuardOs;->close(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;->fchmod(Ljava/io/FileDescriptor;I)V
-Llibcore/io/BlockGuardOs;->fchown(Ljava/io/FileDescriptor;II)V
-Llibcore/io/BlockGuardOs;->fdatasync(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;->fstat(Ljava/io/FileDescriptor;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->fstatvfs(Ljava/io/FileDescriptor;)Landroid/system/StructStatVfs;
-Llibcore/io/BlockGuardOs;->lchown(Ljava/lang/String;II)V
-Llibcore/io/BlockGuardOs;->link(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->lseek(Ljava/io/FileDescriptor;JI)J
-Llibcore/io/BlockGuardOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->mkdir(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->mkfifo(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor;
-Llibcore/io/BlockGuardOs;->posix_fallocate(Ljava/io/FileDescriptor;JJ)V
-Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I
-Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;[BIIJ)I
-Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I
-Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;[BIIJ)I
-Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I
-Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;[BII)I
-Llibcore/io/BlockGuardOs;->readlink(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/BlockGuardOs;->readv(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I
-Llibcore/io/BlockGuardOs;->realpath(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/BlockGuardOs;->remove(Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->rename(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs;
-Llibcore/io/BlockGuardOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I
-Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;[BII)I
-Llibcore/io/BlockGuardOs;->writev(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I
-Llibcore/io/BufferIterator;->readByte()B
-Llibcore/io/BufferIterator;->readByteArray([BII)V
-Llibcore/io/BufferIterator;->readInt()I
-Llibcore/io/BufferIterator;->readIntArray([III)V
-Llibcore/io/BufferIterator;->seek(I)V
-Llibcore/io/BufferIterator;->skip(I)V
-Llibcore/io/DropBox;->addText(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;-><init>(Llibcore/io/Os;)V
-Llibcore/io/ForwardingOs;->access(Ljava/lang/String;I)Z
-Llibcore/io/ForwardingOs;->chmod(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->chown(Ljava/lang/String;II)V
-Llibcore/io/ForwardingOs;->getenv(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/ForwardingOs;->lchown(Ljava/lang/String;II)V
-Llibcore/io/ForwardingOs;->link(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/ForwardingOs;->mkdir(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->mkfifo(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor;
-Llibcore/io/ForwardingOs;->os:Llibcore/io/Os;
-Llibcore/io/ForwardingOs;->readlink(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/ForwardingOs;->remove(Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->removexattr(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->rename(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V
-Llibcore/io/ForwardingOs;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Llibcore/io/ForwardingOs;->setxattr(Ljava/lang/String;Ljava/lang/String;[BI)V
-Llibcore/io/ForwardingOs;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/ForwardingOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs;
-Llibcore/io/ForwardingOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->sysconf(I)J
-Llibcore/io/ForwardingOs;->unlink(Ljava/lang/String;)V
-Llibcore/io/IoBridge;->isConnected(Ljava/io/FileDescriptor;Ljava/net/InetAddress;III)Z
-Llibcore/io/IoUtils;->closeQuietly(Ljava/io/FileDescriptor;)V
-Llibcore/io/IoUtils;->closeQuietly(Ljava/lang/AutoCloseable;)V
-Llibcore/io/IoUtils;->closeQuietly(Ljava/net/Socket;)V
-Llibcore/io/IoUtils;->readFileAsByteArray(Ljava/lang/String;)[B
-Llibcore/io/IoUtils;->readFileAsString(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/IoUtils;->setBlocking(Ljava/io/FileDescriptor;Z)V
-Llibcore/io/MemoryMappedFile;->bigEndianIterator()Llibcore/io/BufferIterator;
-Llibcore/io/MemoryMappedFile;->mmapRO(Ljava/lang/String;)Llibcore/io/MemoryMappedFile;
-Llibcore/io/Os;->chmod(Ljava/lang/String;I)V
-Llibcore/io/Os;->close(Ljava/io/FileDescriptor;)V
-Llibcore/io/Os;->connect(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V
-Llibcore/io/Os;->gai_strerror(I)Ljava/lang/String;
-Llibcore/io/Os;->remove(Ljava/lang/String;)V
-Llibcore/io/Os;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V
-Llibcore/io/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Llibcore/io/Os;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/Os;->strerror(I)Ljava/lang/String;
-Llibcore/io/Os;->sysconf(I)J
-Llibcore/io/Streams;->readAsciiLine(Ljava/io/InputStream;)Ljava/lang/String;
-Llibcore/io/Streams;->readFully(Ljava/io/InputStream;)[B
-Llibcore/io/Streams;->readFully(Ljava/io/InputStream;[B)V
-Llibcore/io/Streams;->readSingleByte(Ljava/io/InputStream;)I
-Llibcore/io/Streams;->skipAll(Ljava/io/InputStream;)V
-Llibcore/io/Streams;->writeSingleByte(Ljava/io/OutputStream;I)V
-Llibcore/net/event/NetworkEventDispatcher;->addListener(Llibcore/net/event/NetworkEventListener;)V
-Llibcore/net/event/NetworkEventDispatcher;->getInstance()Llibcore/net/event/NetworkEventDispatcher;
-Llibcore/net/event/NetworkEventListener;-><init>()V
-Llibcore/net/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String;
-Llibcore/net/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date;
-Llibcore/net/MimeUtils;->guessExtensionFromMimeType(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/net/MimeUtils;->guessMimeTypeFromExtension(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/net/NetworkSecurityPolicy;->isCleartextTrafficPermitted()Z
-Llibcore/util/BasicLruCache;-><init>(I)V
-Llibcore/util/BasicLruCache;->evictAll()V
-Llibcore/util/BasicLruCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Llibcore/util/BasicLruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-Llibcore/util/EmptyArray;->BYTE:[B
-Llibcore/util/EmptyArray;->INT:[I
-Llibcore/util/EmptyArray;->OBJECT:[Ljava/lang/Object;
-Llibcore/util/ZoneInfoDB$TzData;-><init>()V
-Lorg/apache/harmony/dalvik/ddmc/Chunk;-><init>(ILjava/nio/ByteBuffer;)V
-Lorg/apache/harmony/dalvik/ddmc/ChunkHandler;->CHUNK_ORDER:Ljava/nio/ByteOrder;
-Lorg/apache/harmony/dalvik/ddmc/DdmServer;->broadcast(I)V
-Lorg/apache/harmony/dalvik/ddmc/DdmServer;->sendChunk(Lorg/apache/harmony/dalvik/ddmc/Chunk;)V
-Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;->getThreadStats()[B
-Lorg/apache/harmony/xml/dom/ElementImpl;->localName:Ljava/lang/String;
-Lorg/apache/harmony/xml/ExpatAttributes;-><init>()V
-Lorg/apache/harmony/xml/ExpatParser$EntityParser;->depth:I
-Lorg/apache/harmony/xml/ExpatParser;-><init>(Ljava/lang/String;Lorg/apache/harmony/xml/ExpatReader;ZLjava/lang/String;Ljava/lang/String;)V
-Lorg/apache/harmony/xml/ExpatParser;->append([BII)V
-Lorg/apache/harmony/xml/ExpatParser;->append([CII)V
-Lorg/apache/harmony/xml/ExpatParser;->attributes:Lorg/apache/harmony/xml/ExpatAttributes;
-Lorg/apache/harmony/xml/ExpatParser;->cloneAttributes()Lorg/xml/sax/Attributes;
-Lorg/apache/harmony/xml/ExpatParser;->finish()V
-Lorg/apache/harmony/xml/ExpatParser;->xmlReader:Lorg/apache/harmony/xml/ExpatReader;
-Lorg/apache/harmony/xml/ExpatReader;-><init>()V
-Lorg/apache/harmony/xml/ExpatReader;->contentHandler:Lorg/xml/sax/ContentHandler;
 Lorg/apache/xalan/extensions/ExpressionContext;->getContextNode()Lorg/w3c/dom/Node;
 Lorg/apache/xalan/extensions/ExpressionContext;->getErrorListener()Ljavax/xml/transform/ErrorListener;
 Lorg/apache/xalan/extensions/ExpressionContext;->getVariableOrParam(Lorg/apache/xml/utils/QName;)Lorg/apache/xpath/objects/XObject;
@@ -5505,431 +5186,3 @@
 Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutput(Ljava/io/Writer;)V
 Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutputProperty(Ljava/lang/String;Ljava/lang/String;)V
 Lorg/ccil/cowan/tagsoup/XMLWriter;->setPrefix(Ljava/lang/String;Ljava/lang/String;)V
-Lorg/xml/sax/helpers/NamespaceSupport$Context;-><init>(Lorg/xml/sax/helpers/NamespaceSupport;)V
-Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;-><init>(Lorg/xml/sax/helpers/ParserAdapter;)V
-Lsun/misc/ASCIICaseInsensitiveComparator;->CASE_INSENSITIVE_ORDER:Ljava/util/Comparator;
-Lsun/misc/ASCIICaseInsensitiveComparator;->lowerCaseHashCode(Ljava/lang/String;)I
-Lsun/misc/BASE64Decoder;-><init>()V
-Lsun/misc/BASE64Decoder;->pem_convert_array:[B
-Lsun/misc/BASE64Encoder;-><init>()V
-Lsun/misc/BASE64Encoder;->pem_array:[C
-Lsun/misc/CEFormatException;-><init>(Ljava/lang/String;)V
-Lsun/misc/CEStreamExhausted;-><init>()V
-Lsun/misc/CharacterDecoder;-><init>()V
-Lsun/misc/CharacterEncoder;-><init>()V
-Lsun/misc/CharacterEncoder;->encodeBuffer([B)Ljava/lang/String;
-Lsun/misc/CharacterEncoder;->encodeBufferPrefix(Ljava/io/OutputStream;)V
-Lsun/misc/CharacterEncoder;->pStream:Ljava/io/PrintStream;
-Lsun/misc/Cleaner;->create(Ljava/lang/Object;Ljava/lang/Runnable;)Lsun/misc/Cleaner;
-Lsun/misc/FloatingDecimal;->$assertionsDisabled:Z
-Lsun/misc/FloatingDecimal;->getHexDigit(Ljava/lang/String;I)I
-Lsun/misc/FloatingDecimal;->stripLeadingZeros(Ljava/lang/String;)Ljava/lang/String;
-Lsun/misc/FormattedFloatingDecimal$Form;->COMPATIBLE:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal$Form;->DECIMAL_FLOAT:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal$Form;->SCIENTIFIC:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal;->$assertionsDisabled:Z
-Lsun/misc/FpUtils;->$assertionsDisabled:Z
-Lsun/misc/FpUtils;->rawCopySign(DD)D
-Lsun/misc/HexDumpEncoder;-><init>()V
-Lsun/misc/HexDumpEncoder;->currentByte:I
-Lsun/misc/HexDumpEncoder;->offset:I
-Lsun/misc/HexDumpEncoder;->thisLine:[B
-Lsun/misc/HexDumpEncoder;->thisLineLength:I
-Lsun/misc/IOUtils;->readFully(Ljava/io/InputStream;IZ)[B
-Lsun/misc/JarIndex;-><init>([Ljava/lang/String;)V
-Lsun/misc/JarIndex;->write(Ljava/io/OutputStream;)V
-Lsun/misc/MessageUtils;-><init>()V
-Lsun/misc/MetaIndex;->forJar(Ljava/io/File;)Lsun/misc/MetaIndex;
-Lsun/misc/MetaIndex;->registerDirectory(Ljava/io/File;)V
-Lsun/misc/VM;->maxDirectMemory()J
-Lsun/net/ftp/FtpClient;-><init>()V
-Lsun/net/util/IPAddressUtil;->isIPv4LiteralAddress(Ljava/lang/String;)Z
-Lsun/net/util/IPAddressUtil;->isIPv6LiteralAddress(Ljava/lang/String;)Z
-Lsun/net/www/MessageHeader;-><init>()V
-Lsun/net/www/MessageHeader;-><init>(Ljava/io/InputStream;)V
-Lsun/net/www/MessageHeader;->add(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/MessageHeader;->findValue(Ljava/lang/String;)Ljava/lang/String;
-Lsun/net/www/MessageHeader;->prepend(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/MessageHeader;->print(Ljava/io/PrintStream;)V
-Lsun/net/www/MessageHeader;->set(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/ParseUtil;->decode(Ljava/lang/String;)Ljava/lang/String;
-Lsun/net/www/ParseUtil;->encodePath(Ljava/lang/String;Z)Ljava/lang/String;
-Lsun/net/www/ParseUtil;->fileToEncodedURL(Ljava/io/File;)Ljava/net/URL;
-Lsun/net/www/URLConnection;-><init>(Ljava/net/URL;)V
-Lsun/net/www/URLConnection;->setProperties(Lsun/net/www/MessageHeader;)V
-Lsun/nio/ch/DirectBuffer;->address()J
-Lsun/nio/ch/FileChannelImpl;->unmap0(JJ)I
-Lsun/nio/ch/SelectorImpl;->publicSelectedKeys:Ljava/util/Set;
-Lsun/nio/ch/SelectorImpl;->selectedKeys:Ljava/util/Set;
-Lsun/nio/cs/HistoricallyNamedCharset;->historicalName()Ljava/lang/String;
-Lsun/nio/cs/ThreadLocalCoders;->decoderFor(Ljava/lang/Object;)Ljava/nio/charset/CharsetDecoder;
-Lsun/nio/fs/BasicFileAttributesHolder;->get()Ljava/nio/file/attribute/BasicFileAttributes;
-Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/Class;)V
-Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/String;)V
-Lsun/reflect/misc/ReflectUtil;->isPackageAccessible(Ljava/lang/Class;)Z
-Lsun/reflect/misc/ReflectUtil;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z
-Lsun/reflect/Reflection;->ensureMemberAccess(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;I)V
-Lsun/reflect/Reflection;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z
-Lsun/security/action/GetBooleanAction;-><init>(Ljava/lang/String;)V
-Lsun/security/action/GetIntegerAction;-><init>(Ljava/lang/String;I)V
-Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;)V
-Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/jca/GetInstance$Instance;->impl:Ljava/lang/Object;
-Lsun/security/jca/GetInstance$Instance;->provider:Ljava/security/Provider;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/security/Provider;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/JCAUtil;->getSecureRandom()Ljava/security/SecureRandom;
-Lsun/security/jca/ProviderConfig;->argument:Ljava/lang/String;
-Lsun/security/jca/ProviderConfig;->CL_STRING:[Ljava/lang/Class;
-Lsun/security/jca/ProviderConfig;->disableLoad()V
-Lsun/security/jca/ProviderConfig;->hasArgument()Z
-Lsun/security/jca/ProviderList;->getService(Ljava/lang/String;Ljava/lang/String;)Ljava/security/Provider$Service;
-Lsun/security/jca/Providers;->getProviderList()Lsun/security/jca/ProviderList;
-Lsun/security/jca/Providers;->startJarVerification()Ljava/lang/Object;
-Lsun/security/jca/Providers;->stopJarVerification(Ljava/lang/Object;)V
-Lsun/security/pkcs/ContentInfo;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V
-Lsun/security/pkcs/ContentInfo;-><init>([B)V
-Lsun/security/pkcs/ContentInfo;->DATA_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/ContentInfo;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/pkcs/ContentInfo;->getData()[B
-Lsun/security/pkcs/ParsingException;-><init>(Ljava/lang/String;)V
-Lsun/security/pkcs/PKCS7;-><init>([B)V
-Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Ljava/security/cert/X509CRL;[Lsun/security/pkcs/SignerInfo;)V
-Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Lsun/security/pkcs/SignerInfo;)V
-Lsun/security/pkcs/PKCS7;->encodeSignedData(Ljava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS7;->getCertificates()[Ljava/security/cert/X509Certificate;
-Lsun/security/pkcs/PKCS7;->getContentInfo()Lsun/security/pkcs/ContentInfo;
-Lsun/security/pkcs/PKCS7;->getSignerInfos()[Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS7;->verify(Lsun/security/pkcs/SignerInfo;[B)Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS7;->verify([B)[Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS8Key;-><init>()V
-Lsun/security/pkcs/PKCS8Key;->algid:Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/PKCS8Key;->encodedKey:[B
-Lsun/security/pkcs/PKCS8Key;->key:[B
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/lang/Object;)V
-Lsun/security/pkcs/PKCS9Attribute;->CONTENT_TYPE_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->derEncode(Ljava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS9Attribute;->EMAIL_ADDRESS_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->getValue()Ljava/lang/Object;
-Lsun/security/pkcs/PKCS9Attribute;->MESSAGE_DIGEST_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->SIGNING_TIME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;Z)V
-Lsun/security/pkcs/PKCS9Attributes;-><init>([Lsun/security/pkcs/PKCS9Attribute;)V
-Lsun/security/pkcs/PKCS9Attributes;->encode(BLjava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS9Attributes;->getAttribute(Ljava/lang/String;)Lsun/security/pkcs/PKCS9Attribute;
-Lsun/security/pkcs/PKCS9Attributes;->getAttributeValue(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Object;
-Lsun/security/pkcs/PKCS9Attributes;->getDerEncoding()[B
-Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/PKCS9Attributes;Lsun/security/x509/AlgorithmId;[BLsun/security/pkcs/PKCS9Attributes;)V
-Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/x509/AlgorithmId;[B)V
-Lsun/security/pkcs/SignerInfo;->getCertificate(Lsun/security/pkcs/PKCS7;)Ljava/security/cert/X509Certificate;
-Lsun/security/pkcs/SignerInfo;->getCertificateChain(Lsun/security/pkcs/PKCS7;)Ljava/util/ArrayList;
-Lsun/security/pkcs/SignerInfo;->getDigestAlgorithmId()Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/SignerInfo;->getDigestEncryptionAlgorithmId()Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/SignerInfo;->getEncryptedDigest()[B
-Lsun/security/provider/certpath/X509CertificatePair;->clearCache()V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;)V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;Ljava/lang/String;)V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/util/List;)V
-Lsun/security/provider/certpath/X509CertPath;->certs:Ljava/util/List;
-Lsun/security/provider/certpath/X509CertPath;->getEncodingsStatic()Ljava/util/Iterator;
-Lsun/security/provider/X509Factory;->addToCache(Lsun/security/util/Cache;[BLjava/lang/Object;)V
-Lsun/security/provider/X509Factory;->certCache:Lsun/security/util/Cache;
-Lsun/security/provider/X509Factory;->crlCache:Lsun/security/util/Cache;
-Lsun/security/provider/X509Factory;->getFromCache(Lsun/security/util/Cache;[B)Ljava/lang/Object;
-Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509Certificate;)Lsun/security/x509/X509CertImpl;
-Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509CRL;)Lsun/security/x509/X509CRLImpl;
-Lsun/security/timestamp/TimestampToken;-><init>([B)V
-Lsun/security/timestamp/TimestampToken;->getDate()Ljava/util/Date;
-Lsun/security/timestamp/TimestampToken;->getHashAlgorithm()Lsun/security/x509/AlgorithmId;
-Lsun/security/timestamp/TimestampToken;->getHashedMessage()[B
-Lsun/security/timestamp/TimestampToken;->getNonce()Ljava/math/BigInteger;
-Lsun/security/util/BitArray;-><init>(I[B)V
-Lsun/security/util/BitArray;->toByteArray()[B
-Lsun/security/util/Cache;-><init>()V
-Lsun/security/util/Cache;->clear()V
-Lsun/security/util/Cache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Lsun/security/util/Cache;->newHardMemoryCache(I)Lsun/security/util/Cache;
-Lsun/security/util/Cache;->put(Ljava/lang/Object;Ljava/lang/Object;)V
-Lsun/security/util/Debug;->getInstance(Ljava/lang/String;)Lsun/security/util/Debug;
-Lsun/security/util/Debug;->println()V
-Lsun/security/util/Debug;->println(Ljava/lang/String;)V
-Lsun/security/util/Debug;->toHexString(Ljava/math/BigInteger;)Ljava/lang/String;
-Lsun/security/util/DerIndefLenConverter;-><init>()V
-Lsun/security/util/DerIndefLenConverter;->convert([B)[B
-Lsun/security/util/DerIndefLenConverter;->data:[B
-Lsun/security/util/DerIndefLenConverter;->dataPos:I
-Lsun/security/util/DerIndefLenConverter;->dataSize:I
-Lsun/security/util/DerIndefLenConverter;->isIndefinite(I)Z
-Lsun/security/util/DerIndefLenConverter;->newData:[B
-Lsun/security/util/DerIndefLenConverter;->numOfTotalLenBytes:I
-Lsun/security/util/DerIndefLenConverter;->parseLength()I
-Lsun/security/util/DerIndefLenConverter;->parseTag()V
-Lsun/security/util/DerIndefLenConverter;->parseValue(I)V
-Lsun/security/util/DerIndefLenConverter;->writeLengthAndValue()V
-Lsun/security/util/DerIndefLenConverter;->writeTag()V
-Lsun/security/util/DerInputStream;-><init>([B)V
-Lsun/security/util/DerInputStream;->available()I
-Lsun/security/util/DerInputStream;->getBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerInputStream;->getBitString()[B
-Lsun/security/util/DerInputStream;->getDerValue()Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getInteger()I
-Lsun/security/util/DerInputStream;->getOctetString()[B
-Lsun/security/util/DerInputStream;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/DerInputStream;->getSequence(I)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getSet(I)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getSet(IZ)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getUTCTime()Ljava/util/Date;
-Lsun/security/util/DerInputStream;->getUTF8String()Ljava/lang/String;
-Lsun/security/util/DerInputStream;->mark(I)V
-Lsun/security/util/DerInputStream;->peekByte()I
-Lsun/security/util/DerInputStream;->reset()V
-Lsun/security/util/DerInputStream;->subStream(IZ)Lsun/security/util/DerInputStream;
-Lsun/security/util/DerInputStream;->tag:B
-Lsun/security/util/DerOutputStream;-><init>()V
-Lsun/security/util/DerOutputStream;-><init>(I)V
-Lsun/security/util/DerOutputStream;->putBitString([B)V
-Lsun/security/util/DerOutputStream;->putBoolean(Z)V
-Lsun/security/util/DerOutputStream;->putDerValue(Lsun/security/util/DerValue;)V
-Lsun/security/util/DerOutputStream;->putIA5String(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->putInteger(I)V
-Lsun/security/util/DerOutputStream;->putInteger(Ljava/math/BigInteger;)V
-Lsun/security/util/DerOutputStream;->putNull()V
-Lsun/security/util/DerOutputStream;->putOctetString([B)V
-Lsun/security/util/DerOutputStream;->putOID(Lsun/security/util/ObjectIdentifier;)V
-Lsun/security/util/DerOutputStream;->putOrderedSetOf(B[Lsun/security/util/DerEncoder;)V
-Lsun/security/util/DerOutputStream;->putPrintableString(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->putSequence([Lsun/security/util/DerValue;)V
-Lsun/security/util/DerOutputStream;->putUTCTime(Ljava/util/Date;)V
-Lsun/security/util/DerOutputStream;->putUTF8String(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->write(BLsun/security/util/DerOutputStream;)V
-Lsun/security/util/DerOutputStream;->write(B[B)V
-Lsun/security/util/DerValue;-><init>(B[B)V
-Lsun/security/util/DerValue;-><init>(Ljava/io/InputStream;)V
-Lsun/security/util/DerValue;-><init>(Ljava/lang/String;)V
-Lsun/security/util/DerValue;-><init>([B)V
-Lsun/security/util/DerValue;-><init>([BII)V
-Lsun/security/util/DerValue;->buffer:Lsun/security/util/DerInputBuffer;
-Lsun/security/util/DerValue;->createTag(BZB)B
-Lsun/security/util/DerValue;->data:Lsun/security/util/DerInputStream;
-Lsun/security/util/DerValue;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/util/DerValue;->getAsString()Ljava/lang/String;
-Lsun/security/util/DerValue;->getBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerValue;->getBitString()[B
-Lsun/security/util/DerValue;->getData()Lsun/security/util/DerInputStream;
-Lsun/security/util/DerValue;->getDataBytes()[B
-Lsun/security/util/DerValue;->getOctetString()[B
-Lsun/security/util/DerValue;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/DerValue;->getPositiveBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerValue;->getUnalignedBitString()Lsun/security/util/BitArray;
-Lsun/security/util/DerValue;->isConstructed()Z
-Lsun/security/util/DerValue;->isContextSpecific()Z
-Lsun/security/util/DerValue;->isContextSpecific(B)Z
-Lsun/security/util/DerValue;->isPrintableStringChar(C)Z
-Lsun/security/util/DerValue;->resetTag(B)V
-Lsun/security/util/DerValue;->tag:B
-Lsun/security/util/DerValue;->toByteArray()[B
-Lsun/security/util/DerValue;->toDerInputStream()Lsun/security/util/DerInputStream;
-Lsun/security/util/ManifestDigester$Entry;->digest(Ljava/security/MessageDigest;)[B
-Lsun/security/util/ManifestDigester$Entry;->digestWorkaround(Ljava/security/MessageDigest;)[B
-Lsun/security/util/ManifestDigester;-><init>([B)V
-Lsun/security/util/ManifestDigester;->get(Ljava/lang/String;Z)Lsun/security/util/ManifestDigester$Entry;
-Lsun/security/util/ManifestDigester;->manifestDigest(Ljava/security/MessageDigest;)[B
-Lsun/security/util/MemoryCache$HardCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;J)V
-Lsun/security/util/MemoryCache$SoftCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;JLjava/lang/ref/ReferenceQueue;)V
-Lsun/security/util/ObjectIdentifier;-><init>(Ljava/lang/String;)V
-Lsun/security/util/ObjectIdentifier;-><init>([I)V
-Lsun/security/util/ObjectIdentifier;->equals(Lsun/security/util/ObjectIdentifier;)Z
-Lsun/security/util/ObjectIdentifier;->newInternal([I)Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/PropertyExpander;->expand(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/util/ResourcesMgr;->getString(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/util/SecurityConstants;->CREATE_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->GET_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->MODIFY_THREADGROUP_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->MODIFY_THREAD_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SignatureFileVerifier;->isBlockOrSF(Ljava/lang/String;)Z
-Lsun/security/x509/AccessDescription;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/AccessDescription;->getAccessLocation()Lsun/security/x509/GeneralName;
-Lsun/security/x509/AccessDescription;->getAccessMethod()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;-><init>()V
-Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;)V
-Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/security/AlgorithmParameters;)V
-Lsun/security/x509/AlgorithmId;->derEncode(Ljava/io/OutputStream;)V
-Lsun/security/x509/AlgorithmId;->DSA_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->EC_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->encode()[B
-Lsun/security/x509/AlgorithmId;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/AlgorithmId;->equals(Lsun/security/x509/AlgorithmId;)Z
-Lsun/security/x509/AlgorithmId;->getAlgorithmId(Ljava/lang/String;)Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/AlgorithmId;->getDigAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/x509/AlgorithmId;->getEncAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/x509/AlgorithmId;->getEncodedParams()[B
-Lsun/security/x509/AlgorithmId;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->getParameters()Ljava/security/AlgorithmParameters;
-Lsun/security/x509/AlgorithmId;->MD2_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->MD5_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->params:Lsun/security/util/DerValue;
-Lsun/security/x509/AlgorithmId;->parse(Lsun/security/util/DerValue;)Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/AlgorithmId;->RSAEncryption_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->sha1WithRSAEncryption_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA256_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA384_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA512_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AttributeNameEnumeration;-><init>()V
-Lsun/security/x509/AVA;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V
-Lsun/security/x509/AVA;->getDerValue()Lsun/security/util/DerValue;
-Lsun/security/x509/AVA;->getObjectIdentifier()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVA;->getValueString()Ljava/lang/String;
-Lsun/security/x509/AVA;->toRFC2253CanonicalString()Ljava/lang/String;
-Lsun/security/x509/AVAComparator;->INSTANCE:Ljava/util/Comparator;
-Lsun/security/x509/AVAKeyword;->getOID(Ljava/lang/String;ILjava/util/Map;)Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVAKeyword;->isCompliant(I)Z
-Lsun/security/x509/AVAKeyword;->keyword:Ljava/lang/String;
-Lsun/security/x509/AVAKeyword;->keywordMap:Ljava/util/Map;
-Lsun/security/x509/AVAKeyword;->oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVAKeyword;->oidMap:Ljava/util/Map;
-Lsun/security/x509/CertificateAlgorithmId;-><init>(Lsun/security/x509/AlgorithmId;)V
-Lsun/security/x509/CertificateExtensions;-><init>()V
-Lsun/security/x509/CertificateExtensions;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/CertificateExtensions;->encode(Ljava/io/OutputStream;Z)V
-Lsun/security/x509/CertificateExtensions;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/CertificateExtensions;->set(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/x509/CertificateIssuerName;-><init>(Lsun/security/x509/X500Name;)V
-Lsun/security/x509/CertificateSerialNumber;-><init>(I)V
-Lsun/security/x509/CertificateSerialNumber;-><init>(Ljava/math/BigInteger;)V
-Lsun/security/x509/CertificateSubjectName;-><init>(Lsun/security/x509/X500Name;)V
-Lsun/security/x509/CertificateSubjectName;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/CertificateValidity;-><init>(Ljava/util/Date;Ljava/util/Date;)V
-Lsun/security/x509/CertificateVersion;-><init>(I)V
-Lsun/security/x509/CertificateX509Key;-><init>(Ljava/security/PublicKey;)V
-Lsun/security/x509/CRLDistributionPointsExtension;->encodeThis()V
-Lsun/security/x509/CRLNumberExtension;-><init>(Ljava/lang/Boolean;Ljava/lang/Object;)V
-Lsun/security/x509/CRLNumberExtension;->encodeThis()V
-Lsun/security/x509/CRLNumberExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/Extension;-><init>(Lsun/security/x509/Extension;)V
-Lsun/security/x509/Extension;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/Extension;->getExtensionId()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/GeneralName;-><init>(Lsun/security/x509/GeneralNameInterface;)V
-Lsun/security/x509/GeneralName;->getName()Lsun/security/x509/GeneralNameInterface;
-Lsun/security/x509/GeneralName;->getType()I
-Lsun/security/x509/GeneralNames;-><init>()V
-Lsun/security/x509/GeneralNames;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/GeneralNames;->add(Lsun/security/x509/GeneralName;)Lsun/security/x509/GeneralNames;
-Lsun/security/x509/GeneralNames;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/GeneralNames;->isEmpty()Z
-Lsun/security/x509/KeyIdentifier;-><init>(Ljava/security/PublicKey;)V
-Lsun/security/x509/KeyIdentifier;->getIdentifier()[B
-Lsun/security/x509/KeyIdentifier;->octetString:[B
-Lsun/security/x509/KeyUsageExtension;-><init>([Z)V
-Lsun/security/x509/KeyUsageExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/NetscapeCertTypeExtension;-><init>([B)V
-Lsun/security/x509/NetscapeCertTypeExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/OIDMap$OIDInfo;->clazz:Ljava/lang/Class;
-Lsun/security/x509/OIDMap;->getClass(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Class;
-Lsun/security/x509/OIDMap;->nameMap:Ljava/util/Map;
-Lsun/security/x509/OIDMap;->oidMap:Ljava/util/Map;
-Lsun/security/x509/PKIXExtensions;->CertificateIssuer_Id:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/SerialNumber;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/SubjectAlternativeNameExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/SubjectKeyIdentifierExtension;-><init>([B)V
-Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/UniqueIdentity;->encode(Lsun/security/util/DerOutputStream;B)V
-Lsun/security/x509/URIName;->getName()Ljava/lang/String;
-Lsun/security/x509/URIName;->getScheme()Ljava/lang/String;
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X500Name;-><init>([B)V
-Lsun/security/x509/X500Name;->allAvas()Ljava/util/List;
-Lsun/security/x509/X500Name;->asX500Name(Ljavax/security/auth/x500/X500Principal;)Lsun/security/x509/X500Name;
-Lsun/security/x509/X500Name;->asX500Principal()Ljavax/security/auth/x500/X500Principal;
-Lsun/security/x509/X500Name;->commonName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->countryName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->DNQUALIFIER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->DOMAIN_COMPONENT_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/X500Name;->GENERATIONQUALIFIER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->getCommonName()Ljava/lang/String;
-Lsun/security/x509/X500Name;->GIVENNAME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->INITIALS_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->ipAddress_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->isEmpty()Z
-Lsun/security/x509/X500Name;->localityName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->orgName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->orgUnitName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->SERIALNUMBER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->stateName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->streetAddress_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->SURNAME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->title_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->userid_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/x509/X509CertInfo;)V
-Lsun/security/x509/X509CertImpl;-><init>([B)V
-Lsun/security/x509/X509CertImpl;->algId:Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/X509CertImpl;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/X509CertImpl;->getEncodedInternal()[B
-Lsun/security/x509/X509CertImpl;->parse(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CertImpl;->readOnly:Z
-Lsun/security/x509/X509CertImpl;->sign(Ljava/security/PrivateKey;Ljava/lang/String;)V
-Lsun/security/x509/X509CertImpl;->signature:[B
-Lsun/security/x509/X509CertImpl;->signedCert:[B
-Lsun/security/x509/X509CertInfo;-><init>()V
-Lsun/security/x509/X509CertInfo;-><init>([B)V
-Lsun/security/x509/X509CertInfo;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/X509CertInfo;->set(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/x509/X509CRLEntryImpl;->getExtension(Lsun/security/util/ObjectIdentifier;)Lsun/security/x509/Extension;
-Lsun/security/x509/X509CRLImpl;-><init>(Ljava/io/InputStream;)V
-Lsun/security/x509/X509CRLImpl;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CRLImpl;-><init>([B)V
-Lsun/security/x509/X509CRLImpl;->getEncodedInternal()[B
-Lsun/security/x509/X509Key;-><init>()V
-Lsun/security/x509/X509Key;->algid:Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/X509Key;->encodedKey:[B
-Lsun/security/x509/X509Key;->key:[B
-Lsun/security/x509/X509Key;->parse(Lsun/security/util/DerValue;)Ljava/security/PublicKey;
-Lsun/security/x509/X509Key;->unusedBits:I
-Lsun/util/calendar/AbstractCalendar;->getDayOfWeekDateOnOrBefore(JI)J
-Lsun/util/calendar/AbstractCalendar;->getTimeOfDayValue(Lsun/util/calendar/CalendarDate;)J
-Lsun/util/calendar/BaseCalendar$Date;->getNormalizedYear()I
-Lsun/util/calendar/BaseCalendar$Date;->setNormalizedYear(I)V
-Lsun/util/calendar/CalendarDate;->getDayOfMonth()I
-Lsun/util/calendar/CalendarDate;->getMonth()I
-Lsun/util/calendar/CalendarDate;->getTimeOfDay()J
-Lsun/util/calendar/CalendarDate;->getYear()I
-Lsun/util/calendar/CalendarDate;->setDate(III)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setDayOfMonth(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setHours(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setMillis(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setMinutes(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setSeconds(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarSystem;->forName(Ljava/lang/String;)Lsun/util/calendar/CalendarSystem;
-Lsun/util/calendar/CalendarSystem;->getGregorianCalendar()Lsun/util/calendar/Gregorian;
-Lsun/util/calendar/CalendarSystem;->getTime(Lsun/util/calendar/CalendarDate;)J
-Lsun/util/calendar/CalendarSystem;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarSystem;->validate(Lsun/util/calendar/CalendarDate;)Z
-Lsun/util/calendar/CalendarUtils;->floorDivide(II)I
-Lsun/util/calendar/CalendarUtils;->floorDivide(JJ)J
-Lsun/util/calendar/CalendarUtils;->mod(II)I
-Lsun/util/calendar/CalendarUtils;->mod(JJ)J
-Lsun/util/calendar/Era;-><init>(Ljava/lang/String;Ljava/lang/String;JZ)V
-Lsun/util/calendar/Era;->getAbbreviation()Ljava/lang/String;
-Lsun/util/calendar/Era;->getName()Ljava/lang/String;
-Lsun/util/calendar/Era;->getSinceDate()Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/ImmutableGregorianDate;->unsupported()V
-Lsun/util/calendar/LocalGregorianCalendar$Date;->getNormalizedYear()I
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setEra(Lsun/util/calendar/Era;)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setNormalizedYear(I)V
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setYear(I)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar;->normalize(Lsun/util/calendar/CalendarDate;)Z
-Lsun/util/calendar/LocalGregorianCalendar;->validate(Lsun/util/calendar/CalendarDate;)Z
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 575ba34..a21fa30 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -147,8 +147,8 @@
 Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
 Landroid/view/IWindowManager;->endProlongedAnimations()V
 Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
-Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V
-Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
+Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;ZI)V
+Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
 Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V
 Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V
 Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 550e795..5940c45 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -6172,6 +6172,9 @@
 libcore.reflect.Types
 libcore.reflect.WildcardTypeImpl
 libcore.timezone.TimeZoneDataFiles
+libcore.timezone.ZoneInfoDB
+libcore.timezone.ZoneInfoDB$TzData
+libcore.timezone.ZoneInfoDB$TzData$1
 libcore.util.BasicLruCache
 libcore.util.CharsetUtils
 libcore.util.CollectionUtils
@@ -6184,9 +6187,6 @@
 libcore.util.ZoneInfo
 libcore.util.ZoneInfo$CheckedArithmeticException
 libcore.util.ZoneInfo$WallTime
-libcore.util.ZoneInfoDB
-libcore.util.ZoneInfoDB$TzData
-libcore.util.ZoneInfoDB$TzData$1
 org.apache.harmony.dalvik.NativeTestTarget
 org.apache.harmony.dalvik.ddmc.Chunk
 org.apache.harmony.dalvik.ddmc.ChunkHandler
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 83fab7e..05bb9a1 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -827,6 +827,8 @@
     /** The screen observation manager. Always access via {@link #getIntelligenceManager()}. */
     @Nullable private IntelligenceManager mIntelligenceManager;
 
+    private final ArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
+            new ArrayList<Application.ActivityLifecycleCallbacks>();
 
     static final class NonConfigurationInstances {
         Object activity;
@@ -1065,6 +1067,288 @@
     }
 
     /**
+     * Register an {@link Application.ActivityLifecycleCallbacks} instance that receives
+     * lifecycle callbacks for only this Activity.
+     * <p>
+     * In relation to any
+     * {@link Application#registerActivityLifecycleCallbacks Application registered callbacks},
+     * the callbacks registered here will always occur nested within those callbacks. This means:
+     * <ul>
+     *     <li>Pre events will first be sent to Application registered callbacks, then to callbacks
+     *     registered here.</li>
+     *     <li>{@link Application.ActivityLifecycleCallbacks#onActivityCreated(Activity, Bundle)},
+     *     {@link Application.ActivityLifecycleCallbacks#onActivityStarted(Activity)}, and
+     *     {@link Application.ActivityLifecycleCallbacks#onActivityResumed(Activity)} will
+     *     be sent first to Application registered callbacks, then to callbacks registered here.
+     *     For all other events, callbacks registered here will be sent first.</li>
+     *     <li>Post events will first be sent to callbacks registered here, then to
+     *     Application registered callbacks.</li>
+     * </ul>
+     * <p>
+     * If multiple callbacks are registered here, they receive events in a first in (up through
+     * {@link Application.ActivityLifecycleCallbacks#onActivityPostResumed}, last out
+     * ordering.
+     * <p>
+     * It is strongly recommended to register this in the constructor of your Activity to ensure
+     * you get all available callbacks. As this callback is associated with only this Activity,
+     * it is not usually necessary to {@link #unregisterActivityLifecycleCallbacks unregister} it
+     * unless you specifically do not want to receive further lifecycle callbacks.
+     *
+     * @param callback The callback instance to register
+     */
+    public void registerActivityLifecycleCallbacks(
+            @NonNull Application.ActivityLifecycleCallbacks callback) {
+        synchronized (mActivityLifecycleCallbacks) {
+            mActivityLifecycleCallbacks.add(callback);
+        }
+    }
+
+    /**
+     * Unregister an {@link Application.ActivityLifecycleCallbacks} previously registered
+     * with {@link #registerActivityLifecycleCallbacks}. It will not receive any further
+     * callbacks.
+     *
+     * @param callback The callback instance to unregister
+     * @see #registerActivityLifecycleCallbacks
+     */
+    public void unregisterActivityLifecycleCallbacks(
+            @NonNull Application.ActivityLifecycleCallbacks callback) {
+        synchronized (mActivityLifecycleCallbacks) {
+            mActivityLifecycleCallbacks.remove(callback);
+        }
+    }
+
+    private void dispatchActivityPreCreated(@Nullable Bundle savedInstanceState) {
+        getApplication().dispatchActivityPreCreated(this, savedInstanceState);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreCreated(this,
+                        savedInstanceState);
+            }
+        }
+    }
+
+    private void dispatchActivityCreated(@Nullable Bundle savedInstanceState) {
+        getApplication().dispatchActivityCreated(this, savedInstanceState);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityCreated(this,
+                        savedInstanceState);
+            }
+        }
+    }
+
+    private void dispatchActivityPostCreated(@Nullable Bundle savedInstanceState) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostCreated(this,
+                        savedInstanceState);
+            }
+        }
+        getApplication().dispatchActivityPostCreated(this, savedInstanceState);
+    }
+
+    private void dispatchActivityPreStarted() {
+        getApplication().dispatchActivityPreStarted(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStarted(this);
+            }
+        }
+    }
+
+    private void dispatchActivityStarted() {
+        getApplication().dispatchActivityStarted(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStarted(this);
+            }
+        }
+    }
+
+    private void dispatchActivityPostStarted() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPostStarted(this);
+            }
+        }
+        getApplication().dispatchActivityPostStarted(this);
+    }
+
+    private void dispatchActivityPreResumed() {
+        getApplication().dispatchActivityPreResumed(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreResumed(this);
+            }
+        }
+    }
+
+    private void dispatchActivityResumed() {
+        getApplication().dispatchActivityResumed(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityResumed(this);
+            }
+        }
+    }
+
+    private void dispatchActivityPostResumed() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostResumed(this);
+            }
+        }
+        getApplication().dispatchActivityPostResumed(this);
+    }
+
+    private void dispatchActivityPrePaused() {
+        getApplication().dispatchActivityPrePaused(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPrePaused(this);
+            }
+        }
+    }
+
+    private void dispatchActivityPaused() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPaused(this);
+            }
+        }
+        getApplication().dispatchActivityPaused(this);
+    }
+
+    private void dispatchActivityPostPaused() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostPaused(this);
+            }
+        }
+        getApplication().dispatchActivityPostPaused(this);
+    }
+
+    private void dispatchActivityPreStopped() {
+        getApplication().dispatchActivityPreStopped(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStopped(this);
+            }
+        }
+    }
+
+    private void dispatchActivityStopped() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStopped(this);
+            }
+        }
+        getApplication().dispatchActivityStopped(this);
+    }
+
+    private void dispatchActivityPostStopped() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPostStopped(this);
+            }
+        }
+        getApplication().dispatchActivityPostStopped(this);
+    }
+
+    private void dispatchActivityPreSaveInstanceState(@NonNull Bundle outState) {
+        getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPreSaveInstanceState(this, outState);
+            }
+        }
+    }
+
+    private void dispatchActivitySaveInstanceState(@NonNull Bundle outState) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivitySaveInstanceState(this, outState);
+            }
+        }
+        getApplication().dispatchActivitySaveInstanceState(this, outState);
+    }
+
+    private void dispatchActivityPostSaveInstanceState(@NonNull Bundle outState) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPostSaveInstanceState(this, outState);
+            }
+        }
+        getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+    }
+
+    private void dispatchActivityPreDestroyed() {
+        getApplication().dispatchActivityPreDestroyed(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPreDestroyed(this);
+            }
+        }
+    }
+
+    private void dispatchActivityDestroyed() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityDestroyed(this);
+            }
+        }
+        getApplication().dispatchActivityDestroyed(this);
+    }
+
+    private void dispatchActivityPostDestroyed() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPostDestroyed(this);
+            }
+        }
+        getApplication().dispatchActivityPostDestroyed(this);
+    }
+
+    private Object[] collectActivityLifecycleCallbacks() {
+        Object[] callbacks = null;
+        synchronized (mActivityLifecycleCallbacks) {
+            if (mActivityLifecycleCallbacks.size() > 0) {
+                callbacks = mActivityLifecycleCallbacks.toArray();
+            }
+        }
+        return callbacks;
+    }
+
+    /**
      * Called when the activity is starting.  This is where most initialization
      * should go: calling {@link #setContentView(int)} to inflate the
      * activity's UI, using {@link #findViewById} to programmatically interact
@@ -1119,7 +1403,7 @@
                     ? mLastNonConfigurationInstances.fragments : null);
         }
         mFragments.dispatchCreate();
-        getApplication().dispatchActivityCreated(this, savedInstanceState);
+        dispatchActivityCreated(savedInstanceState);
         if (mVoiceInteractor != null) {
             mVoiceInteractor.attachActivity(this);
         }
@@ -1355,7 +1639,7 @@
 
         mFragments.doLoaderStart();
 
-        getApplication().dispatchActivityStarted(this);
+        dispatchActivityStarted();
 
         if (mAutoFillResetNeeded) {
             getAutofillManager().onVisibleForAutofill();
@@ -1426,7 +1710,7 @@
     @CallSuper
     protected void onResume() {
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
-        getApplication().dispatchActivityResumed(this);
+        dispatchActivityResumed();
         mActivityTransitionState.onResume(this, isTopOfTask());
         enableAutofillCompatibilityIfNeeded();
         if (mAutoFillResetNeeded) {
@@ -1642,13 +1926,13 @@
      * @param outState The bundle to save the state to.
      */
     final void performSaveInstanceState(@NonNull Bundle outState) {
-        getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+        dispatchActivityPreSaveInstanceState(outState);
         onSaveInstanceState(outState);
         saveManagedDialogs(outState);
         mActivityTransitionState.saveState(outState);
         storeHasCurrentPermissionRequest(outState);
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
-        getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+        dispatchActivityPostSaveInstanceState(outState);
     }
 
     /**
@@ -1662,13 +1946,13 @@
      */
     final void performSaveInstanceState(@NonNull Bundle outState,
             @NonNull PersistableBundle outPersistentState) {
-        getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+        dispatchActivityPreSaveInstanceState(outState);
         onSaveInstanceState(outState, outPersistentState);
         saveManagedDialogs(outState);
         storeHasCurrentPermissionRequest(outState);
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
                 ", " + outPersistentState);
-        getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+        dispatchActivityPostSaveInstanceState(outState);
     }
 
     /**
@@ -1731,7 +2015,7 @@
             outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
             getAutofillManager().onSaveInstanceState(outState);
         }
-        getApplication().dispatchActivitySaveInstanceState(this, outState);
+        dispatchActivitySaveInstanceState(outState);
     }
 
     /**
@@ -1831,7 +2115,7 @@
     @CallSuper
     protected void onPause() {
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
-        getApplication().dispatchActivityPaused(this);
+        dispatchActivityPaused();
         if (mAutoFillResetNeeded) {
             if (!mAutoFillIgnoreFirstResumePause) {
                 if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this);
@@ -2015,7 +2299,7 @@
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
         if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
         mActivityTransitionState.onStop();
-        getApplication().dispatchActivityStopped(this);
+        dispatchActivityStopped();
         mTranslucentCallback = null;
         mCalled = true;
 
@@ -2104,7 +2388,7 @@
             mActionBar.onDestroy();
         }
 
-        getApplication().dispatchActivityDestroyed(this);
+        dispatchActivityDestroyed();
 
         notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
 
@@ -7284,7 +7568,7 @@
 
     @UnsupportedAppUsage
     final void performCreate(Bundle icicle, PersistableBundle persistentState) {
-        getApplication().dispatchActivityPreCreated(this, icicle);
+        dispatchActivityPreCreated(icicle);
         mCanEnterPictureInPicture = true;
         restoreHasCurrentPermissionRequest(icicle);
         if (persistentState != null) {
@@ -7299,7 +7583,7 @@
                 com.android.internal.R.styleable.Window_windowNoDisplay, false);
         mFragments.dispatchActivityCreated();
         mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
-        getApplication().dispatchActivityPostCreated(this, icicle);
+        dispatchActivityPostCreated(icicle);
     }
 
     final void performNewIntent(@NonNull Intent intent) {
@@ -7308,7 +7592,7 @@
     }
 
     final void performStart(String reason) {
-        getApplication().dispatchActivityPreStarted(this);
+        dispatchActivityPreStarted();
         mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
         mFragments.noteStateNotSaved();
         mCalled = false;
@@ -7351,7 +7635,7 @@
         }
 
         mActivityTransitionState.enterReady(this);
-        getApplication().dispatchActivityPostStarted(this);
+        dispatchActivityPostStarted();
     }
 
     /**
@@ -7406,7 +7690,7 @@
     }
 
     final void performResume(boolean followedByPause, String reason) {
-        getApplication().dispatchActivityPreResumed(this);
+        dispatchActivityPreResumed();
         performRestart(true /* start */, reason);
 
         mFragments.execPendingActions();
@@ -7456,11 +7740,11 @@
                 "Activity " + mComponent.toShortString() +
                 " did not call through to super.onPostResume()");
         }
-        getApplication().dispatchActivityPostResumed(this);
+        dispatchActivityPostResumed();
     }
 
     final void performPause() {
-        getApplication().dispatchActivityPrePaused(this);
+        dispatchActivityPrePaused();
         mDoReportFullyDrawn = false;
         mFragments.dispatchPause();
         mCalled = false;
@@ -7473,7 +7757,7 @@
                     "Activity " + mComponent.toShortString() +
                     " did not call through to super.onPause()");
         }
-        getApplication().dispatchActivityPostPaused(this);
+        dispatchActivityPostPaused();
     }
 
     final void performUserLeaving() {
@@ -7489,7 +7773,7 @@
         mCanEnterPictureInPicture = false;
 
         if (!mStopped) {
-            getApplication().dispatchActivityPreStopped(this);
+            dispatchActivityPreStopped();
             if (mWindow != null) {
                 mWindow.closeAllPanels();
             }
@@ -7524,13 +7808,13 @@
             }
 
             mStopped = true;
-            getApplication().dispatchActivityPostStopped(this);
+            dispatchActivityPostStopped();
         }
         mResumed = false;
     }
 
     final void performDestroy() {
-        getApplication().dispatchActivityPreDestroyed(this);
+        dispatchActivityPreDestroyed();
         mDestroyed = true;
         mWindow.destroy();
         mFragments.dispatchDestroy();
@@ -7540,7 +7824,7 @@
         if (mVoiceInteractor != null) {
             mVoiceInteractor.detachActivity();
         }
-        getApplication().dispatchActivityPostDestroyed(this);
+        dispatchActivityPostDestroyed();
     }
 
     final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode,
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 1edd7f5..af3da0c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -243,6 +243,8 @@
     public abstract void ensureBootCompleted();
     public abstract void updateOomLevelsForDisplay(int displayId);
     public abstract boolean isActivityStartsLoggingEnabled();
+    /** Returns true if the background activity starts is enabled. */
+    public abstract boolean isBackgroundActivityStartsEnabled();
     public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);
 
     /** Input dispatch timeout to a window, start the ANR process. */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 805fb68..41166dd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -157,7 +157,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.org.conscrypt.OpenSSLSocketImpl;
 import com.android.org.conscrypt.TrustedCertificateStore;
@@ -5324,16 +5323,6 @@
         }
     }
 
-    /**
-     * Updates the application info.
-     *
-     * This only works in the system process. Must be called on the main thread.
-     */
-    public void handleSystemApplicationInfoChanged(@NonNull ApplicationInfo ai) {
-        Preconditions.checkState(mSystemThread, "Must only be called in the system process");
-        handleApplicationInfoChanged(ai);
-    }
-
     void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
         // Updates triggered by package installation go through a package update
         // receiver. Here we try to capture ApplicationInfo changes that are
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 9b13420..2f0f14aa 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -35,7 +35,7 @@
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
 
 import java.io.IOException;
 import java.lang.annotation.Retention;
diff --git a/core/java/android/app/AppDetailsActivity.java b/core/java/android/app/AppDetailsActivity.java
index cd36e63..b71af88 100644
--- a/core/java/android/app/AppDetailsActivity.java
+++ b/core/java/android/app/AppDetailsActivity.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.TestApi;
 import android.content.Intent;
 import android.os.Bundle;
 
@@ -24,7 +25,9 @@
  *
  * @hide
  */
+@TestApi
 public class AppDetailsActivity extends Activity {
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 63a41ec..3069be6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1180,11 +1180,11 @@
             Manifest.permission.ACTIVITY_RECOGNITION,
             Manifest.permission.SMS_FINANCIAL_TRANSACTIONS,
             Manifest.permission.READ_MEDIA_AUDIO,
-            Manifest.permission.WRITE_MEDIA_AUDIO,
+            null, // no permission for OP_WRITE_MEDIA_AUDIO
             Manifest.permission.READ_MEDIA_VIDEO,
-            Manifest.permission.WRITE_MEDIA_VIDEO,
+            null, // no permission for OP_WRITE_MEDIA_VIDEO
             Manifest.permission.READ_MEDIA_IMAGES,
-            Manifest.permission.WRITE_MEDIA_IMAGES,
+            null, // no permission for OP_WRITE_MEDIA_IMAGES
     };
 
     /**
@@ -1462,11 +1462,11 @@
             AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION
             AppOpsManager.MODE_DEFAULT, // SMS_FINANCIAL_TRANSACTIONS
             AppOpsManager.MODE_ALLOWED, // READ_MEDIA_AUDIO
-            AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_AUDIO
+            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_AUDIO
             AppOpsManager.MODE_ALLOWED, // READ_MEDIA_VIDEO
-            AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_VIDEO
+            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_VIDEO
             AppOpsManager.MODE_ALLOWED, // READ_MEDIA_IMAGES
-            AppOpsManager.MODE_ALLOWED, // WRITE_MEDIA_IMAGES
+            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES
     };
 
     /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 8bb704d..7312b2c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2046,8 +2046,6 @@
             StorageManager storage) {
         if (app.isInternal()) {
             return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
-        } else if (app.isExternalAsec()) {
-            return storage.getPrimaryPhysicalVolume();
         } else {
             return storage.findVolumeByUuid(app.volumeUuid);
         }
@@ -2983,4 +2981,13 @@
             throw e.rethrowAsRuntimeException();
         }
     }
+
+    @Override
+    public void sendDeviceCustomizationReadyBroadcast() {
+        try {
+            mPM.sendDeviceCustomizationReadyBroadcast();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 5ef4be1..00547b4 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -87,24 +87,24 @@
 
     /**
      * Sets the dimension hint for the wallpaper. These hints indicate the desired
-     * minimum width and height for the wallpaper.
+     * minimum width and height for the wallpaper in a particular display.
      */
-    void setDimensionHints(in int width, in int height, in String callingPackage);
+    void setDimensionHints(in int width, in int height, in String callingPackage, int displayId);
 
     /**
-     * Returns the desired minimum width for the wallpaper.
+     * Returns the desired minimum width for the wallpaper in a particular display.
      */
-    int getWidthHint();
+    int getWidthHint(int displayId);
 
     /**
-     * Returns the desired minimum height for the wallpaper.
+     * Returns the desired minimum height for the wallpaper in a particular display.
      */
-    int getHeightHint();
+    int getHeightHint(int displayId);
 
     /**
      * Sets extra padding that we would like the wallpaper to have outside of the display.
      */
-    void setDisplayPadding(in Rect padding, in String callingPackage);
+    void setDisplayPadding(in Rect padding, in String callingPackage, int displayId);
 
     /**
      * Returns the name of the wallpaper. Private API.
diff --git a/core/java/android/app/Notification.aidl b/core/java/android/app/Notification.aidl
index 9d8129c..8a7156e 100644
--- a/core/java/android/app/Notification.aidl
+++ b/core/java/android/app/Notification.aidl
@@ -17,3 +17,4 @@
 package android.app;
 
 parcelable Notification;
+parcelable Notification.Action;
\ No newline at end of file
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f2a3e44..5002a81 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1338,6 +1338,11 @@
     private int mBadgeIcon = BADGE_ICON_NONE;
 
     /**
+     * Determines whether the platform can generate contextual actions for a notification.
+     */
+    private boolean mAllowSystemGeneratedContextualActions = true;
+
+    /**
      * Structure to encapsulate a named action that can be shown as part of this notification.
      * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
      * selected by the user.
@@ -2238,6 +2243,8 @@
         if (parcel.readInt() != 0) {
             mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
         }
+
+        mAllowSystemGeneratedContextualActions = parcel.readBoolean();
     }
 
     @Override
@@ -2353,6 +2360,7 @@
         that.mSettingsText = this.mSettingsText;
         that.mGroupAlertBehavior = this.mGroupAlertBehavior;
         that.mAppOverlayIntent = this.mAppOverlayIntent;
+        that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;
 
         if (!heavy) {
             that.lightenPayload(); // will clean out extras
@@ -2681,6 +2689,8 @@
             parcel.writeInt(0);
         }
 
+        parcel.writeBoolean(mAllowSystemGeneratedContextualActions);
+
         // mUsesStandardHeader is not written because it should be recomputed in listeners
     }
 
@@ -3101,6 +3111,10 @@
         return mAppOverlayIntent;
     }
 
+    public boolean getAllowSystemGeneratedContextualActions() {
+        return mAllowSystemGeneratedContextualActions;
+    }
+
     /**
      * The small icon representing this notification in the status bar and content view.
      *
@@ -4422,15 +4436,15 @@
             return bitmap;
         }
 
-        private void bindProfileBadge(RemoteViews contentView) {
+        private void bindProfileBadge(RemoteViews contentView, StandardTemplateParams p) {
             Bitmap profileBadge = getProfileBadge();
 
             if (profileBadge != null) {
                 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
                 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
-                if (isColorized()) {
+                if (isColorized(p)) {
                     contentView.setDrawableTint(R.id.profile_badge, false,
-                            getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP);
+                            getPrimaryTextColor(p), PorterDuff.Mode.SRC_ATOP);
                 }
             }
         }
@@ -4493,16 +4507,6 @@
                     result);
         }
 
-        /**
-         * @param hasProgress whether the progress bar should be shown and set
-         * @param result
-         */
-        private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
-                TemplateBindResult result) {
-            return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
-                    .fillTextsFrom(this), result);
-        }
-
         private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p,
                 TemplateBindResult result) {
             RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
@@ -4510,15 +4514,15 @@
             resetStandardTemplate(contentView);
 
             final Bundle ex = mN.extras;
-            updateBackgroundColor(contentView);
-            bindNotificationHeader(contentView, p.ambient, p.headerTextSecondary);
+            updateBackgroundColor(contentView, p);
+            bindNotificationHeader(contentView, p);
             bindLargeIconAndReply(contentView, p, result);
-            boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
+            boolean showProgress = handleProgressBar(contentView, ex, p);
             if (p.title != null) {
                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
                 contentView.setTextViewText(R.id.title, processTextSpans(p.title));
                 if (!p.ambient) {
-                    setTextViewColorPrimary(contentView, R.id.title);
+                    setTextViewColorPrimary(contentView, R.id.title, p);
                 }
                 contentView.setViewLayoutWidth(R.id.title, showProgress
                         ? ViewGroup.LayoutParams.WRAP_CONTENT
@@ -4529,7 +4533,7 @@
                         : com.android.internal.R.id.text;
                 contentView.setTextViewText(textId, processTextSpans(p.text));
                 if (!p.ambient) {
-                    setTextViewColorSecondary(contentView, textId);
+                    setTextViewColorSecondary(contentView, textId, p);
                 }
                 contentView.setViewVisibility(textId, View.VISIBLE);
             }
@@ -4546,8 +4550,9 @@
             return text;
         }
 
-        private void setTextViewColorPrimary(RemoteViews contentView, int id) {
-            ensureColors();
+        private void setTextViewColorPrimary(RemoteViews contentView, int id,
+                StandardTemplateParams p) {
+            ensureColors(p);
             contentView.setTextColor(id, mPrimaryTextColor);
         }
 
@@ -4556,42 +4561,63 @@
         }
 
         /**
-         * @return the primary text color
+         * Return the primary text color using the existing template params
          * @hide
          */
         @VisibleForTesting
         public int getPrimaryTextColor() {
-            ensureColors();
+            return getPrimaryTextColor(mParams);
+        }
+
+        /**
+         * @param p the template params to inflate this with
+         * @return the primary text color
+         * @hide
+         */
+        @VisibleForTesting
+        public int getPrimaryTextColor(StandardTemplateParams p) {
+            ensureColors(p);
             return mPrimaryTextColor;
         }
 
         /**
-         * @return the secondary text color
+         * Return the secondary text color using the existing template params
          * @hide
          */
         @VisibleForTesting
         public int getSecondaryTextColor() {
-            ensureColors();
+            return getSecondaryTextColor(mParams);
+        }
+
+        /**
+         * @param p the template params to inflate this with
+         * @return the secondary text color
+         * @hide
+         */
+        @VisibleForTesting
+        public int getSecondaryTextColor(StandardTemplateParams p) {
+            ensureColors(p);
             return mSecondaryTextColor;
         }
 
-        private void setTextViewColorSecondary(RemoteViews contentView, int id) {
-            ensureColors();
+        private void setTextViewColorSecondary(RemoteViews contentView, int id,
+                StandardTemplateParams p) {
+            ensureColors(p);
             contentView.setTextColor(id, mSecondaryTextColor);
         }
 
-        private void ensureColors() {
-            int backgroundColor = getBackgroundColor();
+        private void ensureColors(StandardTemplateParams p) {
+            int backgroundColor = getBackgroundColor(p);
             if (mPrimaryTextColor == COLOR_INVALID
                     || mSecondaryTextColor == COLOR_INVALID
                     || mTextColorsAreForBackground != backgroundColor) {
                 mTextColorsAreForBackground = backgroundColor;
-                if (!hasForegroundColor() || !isColorized()) {
+                if (!hasForegroundColor() || !isColorized(p)) {
                     mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext,
                             backgroundColor, mInNightMode);
                     mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext,
                             backgroundColor, mInNightMode);
-                    if (backgroundColor != COLOR_DEFAULT && isColorized()) {
+                    if (backgroundColor != COLOR_DEFAULT && isColorized(p)) {
                         mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
                                 mPrimaryTextColor, backgroundColor, 4.5);
                         mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
@@ -4659,10 +4685,11 @@
             }
         }
 
-        private void updateBackgroundColor(RemoteViews contentView) {
-            if (isColorized()) {
+        private void updateBackgroundColor(RemoteViews contentView,
+                StandardTemplateParams p) {
+            if (isColorized(p)) {
                 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
-                        getBackgroundColor());
+                        getBackgroundColor(p));
             } else {
                 // Clear it!
                 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
@@ -4685,19 +4712,20 @@
             remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
         }
 
-        private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
+        private boolean handleProgressBar(RemoteViews contentView, Bundle ex,
+                StandardTemplateParams p) {
             final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
             final int progress = ex.getInt(EXTRA_PROGRESS, 0);
             final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
-            if (hasProgress && (max != 0 || ind)) {
+            if (p.hasProgress && (max != 0 || ind)) {
                 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
                 contentView.setProgressBar(
                         R.id.progress, max, progress, ind);
                 contentView.setProgressBackgroundTintList(
                         R.id.progress, ColorStateList.valueOf(mContext.getColor(
                                 R.color.notification_progress_background_color)));
-                if (mN.color != COLOR_DEFAULT) {
-                    ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
+                if (getRawColor(p) != COLOR_DEFAULT) {
+                    ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor(p));
                     contentView.setProgressTintList(R.id.progress, colorStateList);
                     contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
                 }
@@ -4710,8 +4738,8 @@
 
         private void bindLargeIconAndReply(RemoteViews contentView, StandardTemplateParams p,
                 TemplateBindResult result) {
-            boolean largeIconShown = bindLargeIcon(contentView, p.hideLargeIcon || p.ambient);
-            boolean replyIconShown = bindReplyIcon(contentView, p.hideReplyIcon || p.ambient);
+            boolean largeIconShown = bindLargeIcon(contentView, p);
+            boolean replyIconShown = bindReplyIcon(contentView, p);
             contentView.setViewVisibility(R.id.right_icon_container,
                     largeIconShown || replyIconShown ? View.VISIBLE : View.GONE);
             int marginEnd = calculateMarginEnd(largeIconShown, replyIconShown);
@@ -4759,15 +4787,15 @@
          * Bind the large icon.
          * @return if the largeIcon is visible
          */
-        private boolean bindLargeIcon(RemoteViews contentView, boolean hideLargeIcon) {
+        private boolean bindLargeIcon(RemoteViews contentView, StandardTemplateParams p) {
             if (mN.mLargeIcon == null && mN.largeIcon != null) {
                 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
             }
-            boolean showLargeIcon = mN.mLargeIcon != null && !hideLargeIcon;
+            boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon && !p.ambient;
             if (showLargeIcon) {
                 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
                 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
-                processLargeLegacyIcon(mN.mLargeIcon, contentView);
+                processLargeLegacyIcon(mN.mLargeIcon, contentView, p);
             }
             return showLargeIcon;
         }
@@ -4776,8 +4804,8 @@
          * Bind the reply icon.
          * @return if the reply icon is visible
          */
-        private boolean bindReplyIcon(RemoteViews contentView, boolean hideReplyIcon) {
-            boolean actionVisible = !hideReplyIcon;
+        private boolean bindReplyIcon(RemoteViews contentView, StandardTemplateParams p) {
+            boolean actionVisible = !p.hideReplyIcon && !p.ambient;
             Action action = null;
             if (actionVisible) {
                 action = findReplyAction();
@@ -4787,7 +4815,7 @@
                 contentView.setViewVisibility(R.id.reply_icon_action, View.VISIBLE);
                 contentView.setDrawableTint(R.id.reply_icon_action,
                         false /* targetBackground */,
-                        getNeutralColor(),
+                        getNeutralColor(p),
                         PorterDuff.Mode.SRC_ATOP);
                 contentView.setOnClickPendingIntent(R.id.reply_icon_action, action.actionIntent);
                 contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs);
@@ -4814,41 +4842,41 @@
             return null;
         }
 
-        private void bindNotificationHeader(RemoteViews contentView, boolean ambient,
-                CharSequence secondaryHeaderText) {
-            bindSmallIcon(contentView, ambient);
-            bindHeaderAppName(contentView, ambient);
-            if (!ambient) {
+        private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) {
+            bindSmallIcon(contentView, p);
+            bindHeaderAppName(contentView, p);
+            if (!p.ambient) {
                 // Ambient view does not have these
-                bindHeaderText(contentView);
-                bindHeaderTextSecondary(contentView, secondaryHeaderText);
-                bindHeaderChronometerAndTime(contentView);
-                bindProfileBadge(contentView);
+                bindHeaderText(contentView, p);
+                bindHeaderTextSecondary(contentView, p);
+                bindHeaderChronometerAndTime(contentView, p);
+                bindProfileBadge(contentView, p);
             }
-            bindActivePermissions(contentView, ambient);
-            bindExpandButton(contentView);
+            bindActivePermissions(contentView, p);
+            bindExpandButton(contentView, p);
             mN.mUsesStandardHeader = true;
         }
 
-        private void bindActivePermissions(RemoteViews contentView, boolean ambient) {
-            int color = ambient ? resolveAmbientColor() : getNeutralColor();
+        private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) {
+            int color = p.ambient ? resolveAmbientColor(p) : getNeutralColor(p);
             contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
             contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
             contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
         }
 
-        private void bindExpandButton(RemoteViews contentView) {
-            int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
+        private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) {
+            int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p);
             contentView.setDrawableTint(R.id.expand_button, false, color,
                     PorterDuff.Mode.SRC_ATOP);
             contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
                     color);
         }
 
-        private void bindHeaderChronometerAndTime(RemoteViews contentView) {
+        private void bindHeaderChronometerAndTime(RemoteViews contentView,
+                StandardTemplateParams p) {
             if (showsTimeOrChronometer()) {
                 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
-                setTextViewColorSecondary(contentView, R.id.time_divider);
+                setTextViewColorSecondary(contentView, R.id.time_divider, p);
                 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
                     contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
                     contentView.setLong(R.id.chronometer, "setBase",
@@ -4856,11 +4884,11 @@
                     contentView.setBoolean(R.id.chronometer, "setStarted", true);
                     boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
                     contentView.setChronometerCountDown(R.id.chronometer, countsDown);
-                    setTextViewColorSecondary(contentView, R.id.chronometer);
+                    setTextViewColorSecondary(contentView, R.id.chronometer, p);
                 } else {
                     contentView.setViewVisibility(R.id.time, View.VISIBLE);
                     contentView.setLong(R.id.time, "setTime", mN.when);
-                    setTextViewColorSecondary(contentView, R.id.time);
+                    setTextViewColorSecondary(contentView, R.id.time, p);
                 }
             } else {
                 // We still want a time to be set but gone, such that we can show and hide it
@@ -4869,36 +4897,36 @@
             }
         }
 
-        private void bindHeaderText(RemoteViews contentView) {
-            CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
-            if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
+        private void bindHeaderText(RemoteViews contentView, StandardTemplateParams p) {
+            CharSequence summaryText = p.summaryText;
+            if (summaryText == null && mStyle != null && mStyle.mSummaryTextSet
                     && mStyle.hasSummaryInHeader()) {
-                headerText = mStyle.mSummaryText;
+                summaryText = mStyle.mSummaryText;
             }
-            if (headerText == null
+            if (summaryText == null
                     && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
                     && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
-                headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
+                summaryText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
             }
-            if (headerText != null) {
+            if (summaryText != null) {
                 // TODO: Remove the span entirely to only have the string with propper formating.
                 contentView.setTextViewText(R.id.header_text, processTextSpans(
-                        processLegacyText(headerText)));
-                setTextViewColorSecondary(contentView, R.id.header_text);
+                        processLegacyText(summaryText)));
+                setTextViewColorSecondary(contentView, R.id.header_text, p);
                 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
                 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
-                setTextViewColorSecondary(contentView, R.id.header_text_divider);
+                setTextViewColorSecondary(contentView, R.id.header_text_divider, p);
             }
         }
 
-        private void bindHeaderTextSecondary(RemoteViews contentView, CharSequence secondaryText) {
-            if (!TextUtils.isEmpty(secondaryText)) {
+        private void bindHeaderTextSecondary(RemoteViews contentView, StandardTemplateParams p) {
+            if (!TextUtils.isEmpty(p.headerTextSecondary)) {
                 contentView.setTextViewText(R.id.header_text_secondary, processTextSpans(
-                        processLegacyText(secondaryText)));
-                setTextViewColorSecondary(contentView, R.id.header_text_secondary);
+                        processLegacyText(p.headerTextSecondary)));
+                setTextViewColorSecondary(contentView, R.id.header_text_secondary, p);
                 contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE);
                 contentView.setViewVisibility(R.id.header_text_secondary_divider, View.VISIBLE);
-                setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider);
+                setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider, p);
             }
         }
 
@@ -4936,23 +4964,27 @@
 
             return String.valueOf(name);
         }
-        private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
+        private void bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p) {
             contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
-            if (isColorized() && !ambient) {
-                setTextViewColorPrimary(contentView, R.id.app_name_text);
+            if (isColorized(p)) {
+                setTextViewColorPrimary(contentView, R.id.app_name_text, p);
             } else {
                 contentView.setTextColor(R.id.app_name_text,
-                        ambient ? resolveAmbientColor() : getSecondaryTextColor());
+                        p.ambient ? resolveAmbientColor(p) : getSecondaryTextColor(p));
             }
         }
 
-        private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
+        private boolean isColorized(StandardTemplateParams p) {
+            return p.allowColorization && !p.ambient && mN.isColorized();
+        }
+
+        private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) {
             if (mN.mSmallIcon == null && mN.icon != 0) {
                 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
             }
             contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
             contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel);
-            processSmallIconColor(mN.mSmallIcon, contentView, ambient);
+            processSmallIconColor(mN.mSmallIcon, contentView, p);
         }
 
         /**
@@ -5027,8 +5059,7 @@
                     boolean actionHasValidInput = hasValidRemoteInput(action);
                     validRemoteInput |= actionHasValidInput;
 
-                    final RemoteViews button = generateActionButton(action, emphazisedMode,
-                            p.ambient);
+                    final RemoteViews button = generateActionButton(action, emphazisedMode, p);
                     if (actionHasValidInput && !emphazisedMode) {
                         // Clear the drawable
                         button.setInt(R.id.action0, "setBackgroundResource", 0);
@@ -5049,20 +5080,20 @@
                         View.VISIBLE);
                 big.setTextViewText(R.id.notification_material_reply_text_1,
                         processTextSpans(replyText[0]));
-                setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
+                setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p);
                 big.setViewVisibility(R.id.notification_material_reply_progress,
                         showSpinner ? View.VISIBLE : View.GONE);
                 big.setProgressIndeterminateTintList(
                         R.id.notification_material_reply_progress,
                         ColorStateList.valueOf(
-                                isColorized() ? getPrimaryTextColor() : resolveContrastColor()));
+                                isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p)));
 
                 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])
                         && p.maxRemoteInputHistory > 1) {
                     big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
                     big.setTextViewText(R.id.notification_material_reply_text_2,
                             processTextSpans(replyText[1]));
-                    setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
+                    setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p);
 
                     if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])
                             && p.maxRemoteInputHistory > 2) {
@@ -5070,7 +5101,7 @@
                                 R.id.notification_material_reply_text_3, View.VISIBLE);
                         big.setTextViewText(R.id.notification_material_reply_text_3,
                                 processTextSpans(replyText[2]));
-                        setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
+                        setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p);
                     }
                 }
             }
@@ -5161,18 +5192,23 @@
          * @hide
          */
         public RemoteViews makeNotificationHeader(boolean ambient) {
-            Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
-            mN.extras.putBoolean(EXTRA_COLORIZED, false);
+            return makeNotificationHeader(mParams.reset().ambient(ambient).fillTextsFrom(this));
+        }
+
+        /**
+         * Construct a RemoteViews for the final notification header only. This will not be
+         * colorized.
+         *
+         * @param p the template params to inflate this with
+         */
+        private RemoteViews makeNotificationHeader(StandardTemplateParams p) {
+            // Headers on their own are never colorized
+            p.disallowColorization();
             RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
-                    ambient ? R.layout.notification_template_ambient_header
+                    p.ambient ? R.layout.notification_template_ambient_header
                             : R.layout.notification_template_header);
             resetNotificationHeader(header);
-            bindNotificationHeader(header, ambient, null);
-            if (colorized != null) {
-                mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
-            } else {
-                mN.extras.remove(EXTRA_COLORIZED);
-            }
+            bindNotificationHeader(header, p);
             return header;
         }
 
@@ -5315,24 +5351,15 @@
          * @hide
          */
         public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
-            int color = mN.color;
-            mN.color = COLOR_DEFAULT;
-            CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
-            if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
-                CharSequence newSummary = createSummaryText();
-                if (!TextUtils.isEmpty(newSummary)) {
-                    mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
-                }
+            StandardTemplateParams p = mParams.reset()
+                    .forceDefaultColor()
+                    .ambient(false)
+                    .fillTextsFrom(this);
+            if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) {
+                p.summaryText(createSummaryText());
             }
-
-            RemoteViews header = makeNotificationHeader(false /* ambient */);
+            RemoteViews header = makeNotificationHeader(p);
             header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
-            if (summary != null) {
-                mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
-            } else {
-                mN.extras.remove(EXTRA_SUB_TEXT);
-            }
-            mN.color = color;
             return header;
         }
 
@@ -5361,7 +5388,7 @@
         }
 
         private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
-                boolean ambient) {
+                StandardTemplateParams p) {
             final boolean tombstone = (action.actionIntent == null);
             RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
                     emphazisedMode ? getEmphasizedActionLayoutResource()
@@ -5378,7 +5405,7 @@
                 // change the background bgColor
                 CharSequence title = action.title;
                 ColorStateList[] outResultColor = null;
-                int background = resolveBackgroundColor();
+                int background = resolveBackgroundColor(p);
                 if (isLegacy()) {
                     title = ContrastColorUtil.clearColorSpans(title);
                 } else {
@@ -5386,7 +5413,7 @@
                     title = ensureColorSpanContrast(title, background, outResultColor);
                 }
                 button.setTextViewText(R.id.action0, processTextSpans(title));
-                setTextViewColorPrimary(button, R.id.action0);
+                setTextViewColorPrimary(button, R.id.action0, p);
                 int rippleColor;
                 boolean hasColorOverride = outResultColor != null && outResultColor[0] != null;
                 if (hasColorOverride) {
@@ -5397,11 +5424,12 @@
                             background, mInNightMode);
                     button.setTextColor(R.id.action0, textColor);
                     rippleColor = textColor;
-                } else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) {
-                    rippleColor = resolveContrastColor();
+                } else if (getRawColor(p) != COLOR_DEFAULT && !isColorized(p)
+                        && mTintActionButtons) {
+                    rippleColor = resolveContrastColor(p);
                     button.setTextColor(R.id.action0, rippleColor);
                 } else {
-                    rippleColor = getPrimaryTextColor();
+                    rippleColor = getPrimaryTextColor(p);
                 }
                 // We only want about 20% alpha for the ripple
                 rippleColor = (rippleColor & 0x00ffffff) | 0x33000000;
@@ -5413,13 +5441,15 @@
             } else {
                 button.setTextViewText(R.id.action0, processTextSpans(
                         processLegacyText(action.title)));
-                if (isColorized() && !ambient) {
-                    setTextViewColorPrimary(button, R.id.action0);
-                } else if (mN.color != COLOR_DEFAULT && mTintActionButtons) {
+                if (isColorized(p)) {
+                    setTextViewColorPrimary(button, R.id.action0, p);
+                } else if (getRawColor(p) != COLOR_DEFAULT && mTintActionButtons) {
                     button.setTextColor(R.id.action0,
-                            ambient ? resolveAmbientColor() : resolveContrastColor());
+                            p.ambient ? resolveAmbientColor(p) : resolveContrastColor(p));
                 }
             }
+            button.setIntTag(R.id.action0, R.id.notification_action_index_tag,
+                    mActions.indexOf(action));
             return button;
         }
 
@@ -5525,15 +5555,15 @@
          * Apply any necessariy colors to the small icon
          */
         private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
-                boolean ambient) {
+                StandardTemplateParams p) {
             boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
             int color;
-            if (ambient) {
-                color = resolveAmbientColor();
-            } else if (isColorized()) {
-                color = getPrimaryTextColor();
+            if (p.ambient) {
+                color = resolveAmbientColor(p);
+            } else if (isColorized(p)) {
+                color = getPrimaryTextColor(p);
             } else {
-                color = resolveContrastColor();
+                color = resolveContrastColor(p);
             }
             if (colorable) {
                 contentView.setDrawableTint(R.id.icon, false, color,
@@ -5549,11 +5579,12 @@
          * if it's grayscale).
          */
         // TODO: also check bounds, transparency, that sort of thing.
-        private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
+        private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView,
+                StandardTemplateParams p) {
             if (largeIcon != null && isLegacy()
                     && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
                 // resolve color will fall back to the default when legacy
-                contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(),
+                contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(p),
                         PorterDuff.Mode.SRC_ATOP);
             }
         }
@@ -5564,29 +5595,43 @@
             }
         }
 
-        int resolveContrastColor() {
-            if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
+        int resolveContrastColor(StandardTemplateParams p) {
+            int rawColor = getRawColor(p);
+            if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
                 return mCachedContrastColor;
             }
 
             int color;
             int background = mContext.getColor(
                     com.android.internal.R.color.notification_material_background_color);
-            if (mN.color == COLOR_DEFAULT) {
-                ensureColors();
+            if (rawColor == COLOR_DEFAULT) {
+                ensureColors(p);
                 color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode);
             } else {
-                color = ContrastColorUtil.resolveContrastColor(mContext, mN.color,
+                color = ContrastColorUtil.resolveContrastColor(mContext, rawColor,
                         background, mInNightMode);
             }
             if (Color.alpha(color) < 255) {
                 // alpha doesn't go well for color filters, so let's blend it manually
                 color = ContrastColorUtil.compositeColors(color, background);
             }
-            mCachedContrastColorIsFor = mN.color;
+            mCachedContrastColorIsFor = rawColor;
             return mCachedContrastColor = color;
         }
 
+        /**
+         * Return the raw color of this Notification, which doesn't necessarily satisfy contrast.
+         *
+         * @see #resolveContrastColor(StandardTemplateParams) for the contrasted color
+         * @param p the template params to inflate this with
+         */
+        private int getRawColor(StandardTemplateParams p) {
+            if (p.forceDefaultColor) {
+                return COLOR_DEFAULT;
+            }
+            return mN.color;
+        }
+
         int resolveNeutralColor() {
             if (mNeutralColor != COLOR_INVALID) {
                 return mNeutralColor;
@@ -5602,13 +5647,14 @@
             return mNeutralColor;
         }
 
-        int resolveAmbientColor() {
-            if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
+        int resolveAmbientColor(StandardTemplateParams p) {
+            int rawColor = getRawColor(p);
+            if (mCachedAmbientColorIsFor == rawColor && mCachedAmbientColorIsFor != COLOR_INVALID) {
                 return mCachedAmbientColor;
             }
-            final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, mN.color);
+            final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, rawColor);
 
-            mCachedAmbientColorIsFor = mN.color;
+            mCachedAmbientColorIsFor = rawColor;
             return mCachedAmbientColor = contrasted;
         }
 
@@ -5657,6 +5703,15 @@
         }
 
         /**
+         * Determines whether the platform can generate contextual actions for a notification.
+         * By default this is true.
+         */
+        public Builder setAllowSystemGeneratedContextualActions(boolean allowed) {
+            mN.mAllowSystemGeneratedContextualActions = allowed;
+            return this;
+        }
+
+        /**
          * @deprecated Use {@link #build()} instead.
          */
         @Deprecated
@@ -5831,9 +5886,9 @@
             return R.layout.notification_material_action_tombstone;
         }
 
-        private int getBackgroundColor() {
-            if (isColorized()) {
-                return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
+        private int getBackgroundColor(StandardTemplateParams p) {
+            if (isColorized(p)) {
+                return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : getRawColor(p);
             } else {
                 return COLOR_DEFAULT;
             }
@@ -5841,10 +5896,11 @@
 
         /**
          * Gets a neutral color that can be used for icons or similar that should not stand out.
+         * @param p the template params to inflate this with
          */
-        private int getNeutralColor() {
-            if (isColorized()) {
-                return getSecondaryTextColor();
+        private int getNeutralColor(StandardTemplateParams p) {
+            if (isColorized(p)) {
+                return getSecondaryTextColor(p);
             } else {
                 return resolveNeutralColor();
             }
@@ -5852,9 +5908,10 @@
 
         /**
          * Same as getBackgroundColor but also resolved the default color to the background.
+         * @param p the template params to inflate this with
          */
-        private int resolveBackgroundColor() {
-            int backgroundColor = getBackgroundColor();
+        private int resolveBackgroundColor(StandardTemplateParams p) {
+            int backgroundColor = getBackgroundColor(p);
             if (backgroundColor == COLOR_DEFAULT) {
                 backgroundColor = mContext.getColor(
                         com.android.internal.R.color.notification_material_background_color);
@@ -5862,10 +5919,6 @@
             return backgroundColor;
         }
 
-        private boolean isColorized() {
-            return mN.isColorized();
-        }
-
         private boolean shouldTintActionButtons() {
             return mTintActionButtons;
         }
@@ -5891,7 +5944,7 @@
             mBackgroundColor = backgroundColor;
             mForegroundColor = foregroundColor;
             mTextColorsAreForBackground = COLOR_INVALID;
-            ensureColors();
+            ensureColors(mParams.reset().fillTextsFrom(this));
         }
 
         /**
@@ -6159,30 +6212,30 @@
         }
 
         protected RemoteViews getStandardView(int layoutId) {
-            return getStandardView(layoutId, null);
+            StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder);
+            return getStandardView(layoutId, p, null);
         }
 
+
         /**
          * Get the standard view for this style.
          *
-         * @param layoutId The layout id to use
+         * @param layoutId The layout id to use.
+         * @param p the params for this inflation.
          * @param result The result where template bind information is saved.
          * @return A remoteView for this style.
          * @hide
          */
-        protected RemoteViews getStandardView(int layoutId, TemplateBindResult result) {
+        protected RemoteViews getStandardView(int layoutId, StandardTemplateParams p,
+                TemplateBindResult result) {
             checkBuilder();
 
-            // Nasty.
-            CharSequence oldBuilderContentTitle =
-                    mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
             if (mBigContentTitle != null) {
-                mBuilder.setContentTitle(mBigContentTitle);
+                p.title = mBigContentTitle;
             }
 
-            RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, result);
-
-            mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
+            RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, p,
+                    result);
 
             if (mBigContentTitle != null && mBigContentTitle.equals("")) {
                 contentView.setViewVisibility(R.id.line1, View.GONE);
@@ -6477,12 +6530,13 @@
                 mBuilder.mN.largeIcon = null;
             }
 
+            StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder);
             RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource(),
-                    null /* result */);
+                    p, null /* result */);
             if (mSummaryTextSet) {
                 contentView.setTextViewText(R.id.text, mBuilder.processTextSpans(
                         mBuilder.processLegacyText(mSummaryText)));
-                mBuilder.setTextViewColorSecondary(contentView, R.id.text);
+                mBuilder.setTextViewColorSecondary(contentView, R.id.text, p);
                 contentView.setViewVisibility(R.id.text, View.VISIBLE);
             }
             mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
@@ -6675,24 +6729,24 @@
          * @hide
          */
         public RemoteViews makeBigContentView() {
-
-            // Nasty
-            CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
-            mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
-
+            StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder).text(null);
             TemplateBindResult result = new TemplateBindResult();
-            RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), result);
+            RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), p,
+                    result);
             contentView.setInt(R.id.big_text, "setImageEndMargin", result.getIconMarginEnd());
 
-            mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
-
             CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
             if (TextUtils.isEmpty(bigTextText)) {
                 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
                 // experience
-                bigTextText = mBuilder.processLegacyText(text);
+                bigTextText = mBuilder.processLegacyText(
+                        mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT));
             }
-            applyBigTextContentView(mBuilder, contentView, bigTextText);
+            contentView.setTextViewText(R.id.big_text, mBuilder.processTextSpans(bigTextText));
+            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());
 
             return contentView;
         }
@@ -6710,14 +6764,6 @@
             return !Objects.equals(String.valueOf(getBigText()), String.valueOf(newS.getBigText()));
         }
 
-        static void applyBigTextContentView(Builder builder,
-                RemoteViews contentView, CharSequence bigTextText) {
-            contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText));
-            builder.setTextViewColorSecondary(contentView, R.id.big_text);
-            contentView.setViewVisibility(R.id.big_text,
-                    TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
-            contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
-        }
     }
 
     /**
@@ -7201,24 +7247,26 @@
                 isOneToOne = !isGroupConversation();
             }
             TemplateBindResult bindResult = new TemplateBindResult();
+            StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).title(
+                    conversationTitle).text(null)
+                    .hideLargeIcon(hideRightIcons || isOneToOne)
+                    .hideReplyIcon(hideRightIcons)
+                    .headerTextSecondary(conversationTitle);
             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
                     mBuilder.getMessagingLayoutResource(),
-                    mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
-                            .hideLargeIcon(hideRightIcons || isOneToOne)
-                            .hideReplyIcon(hideRightIcons)
-                            .headerTextSecondary(conversationTitle),
+                    p,
                     bindResult);
             addExtras(mBuilder.mN.extras);
             // also update the end margin if there is an image
             contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
                     bindResult.getIconMarginEnd());
             contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
-                    mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
-                            : mBuilder.resolveContrastColor());
+                    mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
+                            : mBuilder.resolveContrastColor(p));
             contentView.setInt(R.id.status_bar_latest_event_content, "setSenderTextColor",
-                    mBuilder.getPrimaryTextColor());
+                    mBuilder.getPrimaryTextColor(p));
             contentView.setInt(R.id.status_bar_latest_event_content, "setMessageTextColor",
-                    mBuilder.getSecondaryTextColor());
+                    mBuilder.getSecondaryTextColor(p));
             contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd",
                     displayImagesAtEnd);
             contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement",
@@ -7682,15 +7730,9 @@
          * @hide
          */
         public RemoteViews makeBigContentView() {
-            // Remove the content text so it disappears unless you have a summary
-            // Nasty
-            CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
-            mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
-
+            StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder).text(null);
             TemplateBindResult result = new TemplateBindResult();
-            RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), result);
-
-            mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
+            RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), p, result);
 
             int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
                     R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
@@ -7737,7 +7779,7 @@
                     contentView.setViewVisibility(rowIds[i], View.VISIBLE);
                     contentView.setTextViewText(rowIds[i],
                             mBuilder.processTextSpans(mBuilder.processLegacyText(str)));
-                    mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
+                    mBuilder.setTextViewColorSecondary(contentView, rowIds[i], p);
                     contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
                     handleInboxImageMargin(contentView, rowIds[i], first,
                             result.getIconMarginEnd());
@@ -7974,7 +8016,7 @@
         }
 
         private void bindMediaActionButton(RemoteViews container, @IdRes int buttonId,
-                Action action, int color) {
+                Action action, StandardTemplateParams p) {
             final boolean tombstone = (action.actionIntent == null);
             container.setViewVisibility(buttonId, View.VISIBLE);
             container.setImageViewIcon(buttonId, action.getIcon());
@@ -7985,8 +8027,8 @@
             Configuration currentConfig = resources.getConfiguration();
             boolean inNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
                     == Configuration.UI_MODE_NIGHT_YES;
-            int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
-                    ? color
+            int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized(p)
+                    ? getActionColor(p)
                     : ContrastColorUtil.resolveColor(mBuilder.mContext,
                             Notification.COLOR_DEFAULT, inNightMode);
 
@@ -8008,8 +8050,10 @@
         }
 
         private RemoteViews makeMediaContentView() {
+            StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).fillTextsFrom(
+                    mBuilder);
             RemoteViews view = mBuilder.applyStandardTemplate(
-                    R.layout.notification_template_material_media, false, /* hasProgress */
+                    R.layout.notification_template_material_media, p,
                     null /* result */);
 
             final int numActions = mBuilder.mActions.size();
@@ -8024,7 +8068,7 @@
             for (int i = 0; i < MAX_MEDIA_BUTTONS_IN_COMPACT; i++) {
                 if (i < numActionsToShow) {
                     final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
-                    bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, getActionColor());
+                    bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, p);
                 } else {
                     view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
                 }
@@ -8039,9 +8083,9 @@
             return view;
         }
 
-        private int getActionColor() {
-            return mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
-                    : mBuilder.resolveContrastColor();
+        private int getActionColor(StandardTemplateParams p) {
+            return mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
+                    : mBuilder.resolveContrastColor(p);
         }
 
         private RemoteViews makeMediaBigContentView() {
@@ -8053,13 +8097,14 @@
             if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
                 return null;
             }
+            StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).fillTextsFrom(
+                    mBuilder);
             RemoteViews big = mBuilder.applyStandardTemplate(
-                    R.layout.notification_template_material_big_media, false, null /* result */);
+                    R.layout.notification_template_material_big_media, p , null /* result */);
 
             for (int i = 0; i < MAX_MEDIA_BUTTONS; i++) {
                 if (i < actionCount) {
-                    bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i),
-                            getActionColor());
+                    bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i), p);
                 } else {
                     big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
                 }
@@ -8315,7 +8360,7 @@
                 // Need to clone customContent before adding, because otherwise it can no longer be
                 // parceled independently of remoteViews.
                 customContent = customContent.clone();
-                customContent.overrideTextColors(mBuilder.getPrimaryTextColor());
+                customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams));
                 remoteViews.removeAllViews(id);
                 remoteViews.addView(id, customContent);
                 remoteViews.setReapplyDisallowed();
@@ -9858,17 +9903,23 @@
         CharSequence title;
         CharSequence text;
         CharSequence headerTextSecondary;
+        CharSequence summaryText;
         int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
         boolean hideLargeIcon;
         boolean hideReplyIcon;
+        boolean allowColorization  = true;
+        boolean forceDefaultColor = false;
 
         final StandardTemplateParams reset() {
             hasProgress = true;
             ambient = false;
             title = null;
             text = null;
+            summaryText = null;
             headerTextSecondary = null;
             maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
+            allowColorization = true;
+            forceDefaultColor = false;
             return this;
         }
 
@@ -9887,6 +9938,11 @@
             return this;
         }
 
+        final StandardTemplateParams summaryText(CharSequence text) {
+            this.summaryText = text;
+            return this;
+        }
+
         final StandardTemplateParams headerTextSecondary(CharSequence text) {
             this.headerTextSecondary = text;
             return this;
@@ -9902,6 +9958,16 @@
             return this;
         }
 
+        final StandardTemplateParams disallowColorization() {
+            this.allowColorization = false;
+            return this;
+        }
+
+        final StandardTemplateParams forceDefaultColor() {
+            this.forceDefaultColor = true;
+            return this;
+        }
+
         final StandardTemplateParams ambient(boolean ambient) {
             Preconditions.checkState(title == null && text == null, "must set ambient before text");
             this.ambient = ambient;
@@ -9918,6 +9984,7 @@
                 text = extras.getCharSequence(EXTRA_TEXT);
             }
             this.text = b.processLegacyText(text, ambient);
+            this.summaryText = extras.getCharSequence(EXTRA_SUB_TEXT);
             return this;
         }
 
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index a2dae3b..0abc998 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -127,8 +127,8 @@
         if (obj instanceof Person) {
             final Person other = (Person) obj;
             return Objects.equals(mName, other.mName)
-                    && mIcon == null ? other.mIcon == null :
-                    (other.mIcon != null && mIcon.sameAs(other.mIcon))
+                    && (mIcon == null ? other.mIcon == null :
+                    (other.mIcon != null && mIcon.sameAs(other.mIcon)))
                     && Objects.equals(mUri, other.mUri)
                     && Objects.equals(mKey, other.mKey)
                     && mIsBot == other.mIsBot
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index e33d1fe..3ea3da2 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -36,6 +36,7 @@
 import android.util.AttributeSet;
 import android.util.Printer;
 import android.util.Xml;
+import android.view.SurfaceHolder;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -79,6 +80,7 @@
     final boolean mShowMetadataInPreview;
     final boolean mSupportsAmbientMode;
     final String mSettingsSliceUri;
+    final boolean mSupportMultipleDisplays;
 
     /**
      * Constructor.
@@ -143,6 +145,9 @@
                     false);
             mSettingsSliceUri = sa.getString(
                     com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
+            mSupportMultipleDisplays = sa.getBoolean(
+                    com.android.internal.R.styleable.Wallpaper_supportsMultipleDisplays,
+                    false);
 
             sa.recycle();
         } catch (NameNotFoundException e) {
@@ -163,6 +168,7 @@
         mShowMetadataInPreview = source.readInt() != 0;
         mSupportsAmbientMode = source.readInt() != 0;
         mSettingsSliceUri = source.readString();
+        mSupportMultipleDisplays = source.readInt() != 0;
         mService = ResolveInfo.CREATOR.createFromParcel(source);
     }
     
@@ -358,6 +364,19 @@
         return Uri.parse(mSettingsSliceUri);
     }
 
+    /**
+     * Returns whether this wallpaper service can support multiple engines to render on each surface
+     * independently. An example use case is a multi-display set-up where the wallpaper service can
+     * render surfaces to each of the connected displays.
+     *
+     * @see WallpaperService#onCreateEngine()
+     * @see WallpaperService.Engine#onCreate(SurfaceHolder)
+     * @return {@code true} if multiple engines can render independently on each surface.
+     */
+    public boolean supportsMultipleDisplays() {
+        return mSupportMultipleDisplays;
+    }
+
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "Service:");
         mService.dump(pw, prefix + "  ");
@@ -387,6 +406,7 @@
         dest.writeInt(mShowMetadataInPreview ? 1 : 0);
         dest.writeInt(mSupportsAmbientMode ? 1 : 0);
         dest.writeString(mSettingsSliceUri);
+        dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
         mService.writeToParcel(dest, flags);
     }
 
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index bebe79e..27471ca 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1485,7 +1485,7 @@
             throw new RuntimeException(new DeadSystemException());
         }
         try {
-            return sGlobals.mService.getWidthHint();
+            return sGlobals.mService.getWidthHint(mContext.getDisplayId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1511,7 +1511,7 @@
             throw new RuntimeException(new DeadSystemException());
         }
         try {
-            return sGlobals.mService.getHeightHint();
+            return sGlobals.mService.getHeightHint(mContext.getDisplayId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1572,7 +1572,7 @@
                 throw new RuntimeException(new DeadSystemException());
             } else {
                 sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight,
-                        mContext.getOpPackageName());
+                        mContext.getOpPackageName(), mContext.getDisplayId());
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1597,7 +1597,8 @@
                 Log.w(TAG, "WallpaperService not running");
                 throw new RuntimeException(new DeadSystemException());
             } else {
-                sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName());
+                sGlobals.mService.setDisplayPadding(padding, mContext.getOpPackageName(),
+                        mContext.getDisplayId());
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 24ee7f7..98d2a40 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -53,6 +53,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -87,6 +88,7 @@
 import com.android.org.conscrypt.TrustedCertificateStore;
 
 import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1930,6 +1932,48 @@
     public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3;
 
     /**
+     * Callback used in {@link #installSystemUpdate} to indicate that there was an error while
+     * trying to install an update.
+     */
+    public abstract static class InstallUpdateCallback {
+        /** Represents an unknown error while trying to install an update. */
+        public static final int UPDATE_ERROR_UNKNOWN = 1;
+
+        /** Represents the update file being intended for different OS version. */
+        public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2;
+
+        /**
+         * Represents the update file being wrong, i.e. payloads are mismatched, wrong compressions
+         * method.
+         */
+        public static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3;
+
+        /** Represents that the file could not be found. */
+        public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4;
+
+        /** Represents the battery being too low to apply an update. */
+        public static final int UPDATE_ERROR_BATTERY_LOW = 5;
+
+        /** Method invoked when there was an error while installing an update. */
+        public void onInstallUpdateError(
+                @InstallUpdateCallbackErrorConstants int errorCode, String errorMessage) {
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "UPDATE_ERROR_" }, value = {
+            InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+            InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION,
+            InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+            InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND,
+            InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstallUpdateCallbackErrorConstants {}
+
+    /**
      * Return true if the given administrator component is currently active (enabled) in the system.
      *
      * @param admin The administrator component to check for.
@@ -6796,7 +6840,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface CreateAndManageUserFlags {}
 
-
     /**
      * Called by a device owner to create a user with the specified name and a given component of
      * the calling package as profile owner. The UserHandle returned by this method should not be
@@ -9827,6 +9870,62 @@
     }
 
     /**
+     * Called by device owner to install a system update from the given file. The device will be
+     * rebooted in order to finish installing the update. Note that if the device is rebooted, this
+     * doesn't necessarily mean that the update has been applied successfully. The caller should
+     * additionally check the system version with {@link android.os.Build#FINGERPRINT} or {@link
+     * android.os.Build.VERSION}. If an error occurs during processing the OTA before the reboot,
+     * the caller will be notified by {@link InstallUpdateCallback}. If device does not have
+     * sufficient battery level, the installation will fail with error {@link
+     * InstallUpdateCallback#UPDATE_ERROR_BATTERY_LOW}.
+     *
+     * @param admin The {@link DeviceAdminReceiver} that this request is associated with.
+     * @param updateFilePath An Uri of the file that contains the update. The file should be
+     * readable by the calling app.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback A callback object that will inform the caller when installing an update
+     * fails.
+     */
+    public void installSystemUpdate(
+            @NonNull ComponentName admin, @NonNull Uri updateFilePath,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull InstallUpdateCallback callback) {
+        throwIfParentInstance("installUpdate");
+        if (mService == null) {
+            return;
+        }
+        try (ParcelFileDescriptor fileDescriptor = mContext.getContentResolver()
+                    .openFileDescriptor(updateFilePath, "r")) {
+            mService.installUpdateFromFile(
+                    admin, fileDescriptor, new StartInstallingUpdateCallback.Stub() {
+                        @Override
+                        public void onStartInstallingUpdateError(
+                                int errorCode, String errorMessage) {
+                            executeCallback(errorCode, errorMessage, executor, callback);
+                        }
+                    });
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (FileNotFoundException e) {
+            Log.w(TAG, e);
+            executeCallback(
+                    InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND, Log.getStackTraceString(e),
+                    executor, callback);
+        } catch (IOException e) {
+            Log.w(TAG, e);
+            executeCallback(
+                    InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e),
+                    executor, callback);
+        }
+    }
+
+    private void executeCallback(int errorCode, String errorMessage,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull InstallUpdateCallback callback) {
+        executor.execute(() -> callback.onInstallUpdateError(errorCode, errorMessage));
+    }
+
+    /**
      * Returns the system-wide Private DNS mode.
      *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with.
@@ -9893,4 +9992,111 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Whitelists a package that is allowed to access cross profile calendar APIs.
+     *
+     * <p>Called by a profile owner of a managed profile.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName name of the package to be whitelisted.
+     * @throws SecurityException if {@code admin} is not a profile owner.
+     *
+     * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+     * @see #getCrossProfileCalendarPackages(ComponentName)
+     */
+    public void addCrossProfileCalendarPackage(@NonNull ComponentName admin,
+            @NonNull String packageName) {
+        throwIfParentInstance("addCrossProfileCalendarPackage");
+        if (mService != null) {
+            try {
+                mService.addCrossProfileCalendarPackage(admin, packageName);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Removes a package that was allowed to access cross profile calendar APIs
+     * from the whitelist.
+     *
+     * <p>Called by a profile owner of a managed profile.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName name of the package to be removed from the whitelist.
+     * @return {@code true} if the package is successfully removed from the whitelist,
+     * {@code false} otherwise.
+     * @throws SecurityException if {@code admin} is not a profile owner.
+     *
+     * @see #addCrossProfileCalendarPackage(ComponentName, String)
+     * @see #getCrossProfileCalendarPackages(ComponentName)
+     */
+    public boolean removeCrossProfileCalendarPackage(@NonNull ComponentName admin,
+            @NonNull String packageName) {
+        throwIfParentInstance("removeCrossProfileCalendarPackage");
+        if (mService != null) {
+            try {
+                return mService.removeCrossProfileCalendarPackage(admin, packageName);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Gets a set of package names that are whitelisted to access cross profile calendar APIs.
+     *
+     * <p>Called by a profile owner of a managed profile.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @return the set of names of packages that were previously whitelisted via
+     * {@link #addCrossProfileCalendarPackage(ComponentName, String)}, or an
+     * empty set if none have been whitelisted.
+     * @throws SecurityException if {@code admin} is not a profile owner.
+     *
+     * @see #addCrossProfileCalendarPackage(ComponentName, String)
+     * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+     */
+    public @NonNull Set<String> getCrossProfileCalendarPackages(@NonNull ComponentName admin) {
+        throwIfParentInstance("getCrossProfileCalendarPackages");
+        if (mService != null) {
+            try {
+                return new ArraySet<>(mService.getCrossProfileCalendarPackages(admin));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return Collections.emptySet();
+    }
+
+    /**
+     * Returns if a package is whitelisted to access cross profile calendar APIs.
+     *
+     * <p>To query for a specific user, use
+     * {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for
+     * that user, and get a {@link DevicePolicyManager} from this context.
+     *
+     * @param packageName the name of the package
+     * @return {@code true} if the package is whitelisted to access cross profile calendar APIs.
+     * {@code false} otherwise.
+     *
+     * @see #addCrossProfileCalendarPackage(ComponentName, String)
+     * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+     * @see #getCrossProfileCalendarPackages(ComponentName)
+     * @hide
+     */
+    public @NonNull boolean isPackageAllowedToAccessCalendar(@NonNull  String packageName) {
+        throwIfParentInstance("isPackageAllowedToAccessCalendar");
+        if (mService != null) {
+            try {
+                return mService.isPackageAllowedToAccessCalendarForUser(packageName,
+                        mContext.getUserId());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 918c127..297676d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -20,6 +20,7 @@
 import android.app.admin.NetworkEvent;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
+import android.app.admin.StartInstallingUpdateCallback;
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.admin.PasswordMetrics;
@@ -419,4 +420,11 @@
     String getGlobalPrivateDnsHost(in ComponentName admin);
 
     void grantDeviceIdsAccessToProfileOwner(in ComponentName who, int userId);
+
+    void installUpdateFromFile(in ComponentName admin, in ParcelFileDescriptor updateFileDescriptor, in StartInstallingUpdateCallback listener);
+
+    void addCrossProfileCalendarPackage(in ComponentName admin, String packageName);
+    boolean removeCrossProfileCalendarPackage(in ComponentName admin, String packageName);
+    List<String> getCrossProfileCalendarPackages(in ComponentName admin);
+    boolean isPackageAllowedToAccessCalendarForUser(String packageName, int userHandle);
 }
diff --git a/core/java/android/app/admin/StartInstallingUpdateCallback.aidl b/core/java/android/app/admin/StartInstallingUpdateCallback.aidl
new file mode 100644
index 0000000..df04707
--- /dev/null
+++ b/core/java/android/app/admin/StartInstallingUpdateCallback.aidl
@@ -0,0 +1,27 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.app.admin;
+
+/**
+* Callback used between {@link DevicePolicyManager} and {@link DevicePolicyManagerService} to
+* indicate that starting installing an update is finished.
+* {@hide}
+*/
+oneway interface StartInstallingUpdateCallback {
+    void onStartInstallingUpdateError(int errorCode, String errorMessage);
+}
\ No newline at end of file
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 5514851..3f34803 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -193,10 +193,6 @@
     /** @hide */
     public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000D;
     /** @hide */
-    public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000E;
-    /** @hide */
-    public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP = 0x000F;
-    /** @hide */
     public static final int REASON_SUB_PREDICTED_RESTORED       = 0x0001;
 
 
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index 7988008..2174255 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -116,6 +116,9 @@
      */
     @Nullable
     public byte[] getManufacturerSpecificData(int manufacturerId) {
+        if (mManufacturerSpecificData == null) {
+            return null;
+        }
         return mManufacturerSpecificData.get(manufacturerId);
     }
 
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 949cdd6..437039d 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -42,7 +42,6 @@
 import android.graphics.Point;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.DeadObjectException;
@@ -3244,18 +3243,8 @@
         Objects.requireNonNull(uri);
         Objects.requireNonNull(size);
 
-        // Older apps might be relying on mutable results, so only consider
-        // giving them hardware bitmaps once they target Q or higher. If modern
-        // apps need mutable thumbnails, they can always roll their own logic.
-        final int allocator;
-        if (getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
-            allocator = ImageDecoder.ALLOCATOR_DEFAULT;
-        } else {
-            allocator = ImageDecoder.ALLOCATOR_SOFTWARE;
-        }
-
         try (ContentProviderClient client = acquireContentProviderClient(uri)) {
-            return loadThumbnail(client, uri, size, signal, allocator);
+            return loadThumbnail(client, uri, size, signal, ImageDecoder.ALLOCATOR_SOFTWARE);
         }
     }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 10c6bc6..6fd5061 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1767,6 +1767,9 @@
      * that should be managed by the launched UI.
      * </p>
      * <p>
+     * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app.
+     * </p>
+     * <p>
      * Output: Nothing.
      * </p>
      *
@@ -3586,6 +3589,27 @@
     public static final String
             ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
 
+
+    /**
+     * Activity Action: Perform text translation.
+     * <p>
+     * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to translate.
+     * <p>
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
+
+    /**
+     * Activity Action: Define the meaning of the selected word(s).
+     * <p>
+     * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to define.
+     * <p>
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DEFINE = "android.intent.action.DEFINE";
+
     /**
      * Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or
      * exisiting sensor being disconnected.
@@ -4092,6 +4116,18 @@
      */
     public static final String ACTION_DOCK_ACTIVE = "android.intent.action.DOCK_ACTIVE";
 
+    /**
+     * Broadcast Action: Indicates that a new device customization has been
+     * downloaded and applied (packages installed, runtime resource overlays
+     * enabled, xml files copied, ...), and that it is time for components that
+     * need to for example clear their caches to do so now.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_DEVICE_CUSTOMIZATION_READY =
+            "android.intent.action.DEVICE_CUSTOMIZATION_READY";
+
 
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 4e110da..7c3b5e4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -32,7 +32,6 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
-import android.text.TextUtils;
 import android.util.Printer;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
@@ -464,16 +463,6 @@
     public static final int PRIVATE_FLAG_CANT_SAVE_STATE = 1<<1;
 
     /**
-     * Value for {@link #privateFlags}: Set to true if the application has been
-     * installed using the forward lock option.
-     *
-     * NOTE: DO NOT CHANGE THIS VALUE!  It is saved in packages.xml.
-     *
-     * {@hide}
-     */
-    public static final int PRIVATE_FLAG_FORWARD_LOCK = 1<<2;
-
-    /**
      * Value for {@link #privateFlags}: set to {@code true} if the application
      * is permitted to hold privileged permissions.
      *
@@ -651,7 +640,6 @@
             PRIVATE_FLAG_CANT_SAVE_STATE,
             PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
             PRIVATE_FLAG_DIRECT_BOOT_AWARE,
-            PRIVATE_FLAG_FORWARD_LOCK,
             PRIVATE_FLAG_HAS_DOMAIN_URLS,
             PRIVATE_FLAG_HIDDEN,
             PRIVATE_FLAG_INSTANT,
@@ -1843,17 +1831,6 @@
         return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
     }
 
-    /** @hide */
-    public boolean isExternalAsec() {
-        return TextUtils.isEmpty(volumeUuid) && isExternal();
-    }
-
-    /** @hide */
-    @UnsupportedAppUsage
-    public boolean isForwardLocked() {
-        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
-    }
-
     /**
      * True if the application is installed as an instant app.
      * @hide
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d0eff2e..dbea821 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -676,4 +676,6 @@
     String getSystemTextClassifierPackageName();
 
     boolean isPackageStateProtected(String packageName, int userId);
+
+    void sendDeviceCustomizationReadyBroadcast();
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 8f90199..07672d9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1374,12 +1374,6 @@
         }
 
         /** {@hide} */
-        public void setInstallFlagsInternal() {
-            installFlags |= PackageManager.INSTALL_INTERNAL;
-            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
-        }
-
-        /** {@hide} */
         @SystemApi
         public void setAllowDowngrade(boolean allowDowngrade) {
             if (allowDowngrade) {
@@ -1390,12 +1384,6 @@
         }
 
         /** {@hide} */
-        public void setInstallFlagsExternal() {
-            installFlags |= PackageManager.INSTALL_EXTERNAL;
-            installFlags &= ~PackageManager.INSTALL_INTERNAL;
-        }
-
-        /** {@hide} */
         public void setInstallFlagsForcePermissionPrompt() {
             installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 361beba..6421dc5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -700,10 +700,8 @@
 
     /** @hide */
     @IntDef(flag = true, prefix = { "INSTALL_" }, value = {
-            INSTALL_FORWARD_LOCK,
             INSTALL_REPLACE_EXISTING,
             INSTALL_ALLOW_TEST,
-            INSTALL_EXTERNAL,
             INSTALL_INTERNAL,
             INSTALL_FROM_ADB,
             INSTALL_ALL_USERS,
@@ -721,17 +719,6 @@
     public @interface InstallFlags {}
 
     /**
-     * Flag parameter for {@link #installPackage} to indicate that this package
-     * should be installed as forward locked, i.e. only the app itself should
-     * have access to its code and non-resource assets.
-     *
-     * @deprecated new installs into ASEC containers are no longer supported.
-     * @hide
-     */
-    @Deprecated
-    public static final int INSTALL_FORWARD_LOCK = 0x00000001;
-
-    /**
      * Flag parameter for {@link #installPackage} to indicate that you want to
      * replace an already installed package, if one exists.
      *
@@ -750,17 +737,6 @@
 
     /**
      * Flag parameter for {@link #installPackage} to indicate that this package
-     * must be installed to an ASEC on a {@link VolumeInfo#TYPE_PUBLIC}.
-     *
-     * @deprecated new installs into ASEC containers are no longer supported;
-     *             use adoptable storage instead.
-     * @hide
-     */
-    @Deprecated
-    public static final int INSTALL_EXTERNAL = 0x00000008;
-
-    /**
-     * Flag parameter for {@link #installPackage} to indicate that this package
      * must be installed to internal storage.
      *
      * @hide
@@ -1521,14 +1497,6 @@
 
     /**
      * Error code that is passed to the {@link IPackageMoveObserver} if the
-     * specified package cannot be moved since its forward locked.
-     *
-     * @hide
-     */
-    public static final int MOVE_FAILED_FORWARD_LOCKED = -4;
-
-    /**
-     * Error code that is passed to the {@link IPackageMoveObserver} if the
      * specified package cannot be moved to the specified location.
      *
      * @hide
@@ -6441,4 +6409,18 @@
             "isPackageStateProtected not implemented in subclass");
     }
 
+    /**
+     * Notify to the rest of the system that a new device configuration has
+     * been prepared and that it is time to refresh caches.
+     *
+     * @see android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY
+     *
+     * @hide
+     */
+    @SystemApi
+    public void sendDeviceCustomizationReadyBroadcast() {
+        throw new UnsupportedOperationException(
+            "sendDeviceCustomizationReadyBroadcast not implemented in subclass");
+    }
+
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2fcf1dd..d00c9a0 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -829,9 +829,6 @@
 
     public static final int PARSE_MUST_BE_APK = 1 << 0;
     public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
-    /** @deprecated forward lock no longer functional. remove. */
-    @Deprecated
-    public static final int PARSE_FORWARD_LOCK = 1 << 2;
     public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
     public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
     public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
@@ -845,7 +842,6 @@
             PARSE_ENFORCE_CODE,
             PARSE_EXTERNAL_STORAGE,
             PARSE_FORCE_SDK,
-            PARSE_FORWARD_LOCK,
             PARSE_IGNORE_PROCESSES,
             PARSE_IS_SYSTEM_DIR,
             PARSE_MUST_BE_APK,
@@ -2006,11 +2002,6 @@
                 PARSE_DEFAULT_TARGET_SANDBOX);
         pkg.applicationInfo.targetSandboxVersion = targetSandboxVersion;
 
-        /* Set the global "forward lock" flag */
-        if ((flags & PARSE_FORWARD_LOCK) != 0) {
-            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
-        }
-
         /* Set the global "on SD card" flag */
         if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
@@ -2532,55 +2523,33 @@
 
                 final ArraySet<String> newPermissions = new ArraySet<>();
                 newPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
-                newPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
                 newPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
-                newPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
                 newPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
-                newPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
                 newPermissions.add(android.Manifest.permission.ACCESS_MEDIA_LOCATION);
                 newPermissions.add(android.Manifest.permission.WRITE_OBB);
 
-                final ArraySet<String> dangerousPermissions = new ArraySet<>();
-                dangerousPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE);
-                dangerousPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
+                final ArraySet<String> removedPermissions = new ArraySet<>();
+                removedPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE);
+                removedPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
 
                 for (int i = pkg.permissions.size() - 1; i >= 0; i--) {
                     final Permission p = pkg.permissions.get(i);
                     if (newPermissions.contains(p.info.name)) {
                         pkg.permissions.remove(i);
-                    } else if (dangerousPermissions.contains(p.info.name)) {
-                        p.info.protectionLevel &= ~PermissionInfo.PROTECTION_MASK_BASE;
-                        p.info.protectionLevel |= PermissionInfo.PROTECTION_DANGEROUS;
+                    } else if (removedPermissions.contains(p.info.name)) {
+                        p.info.flags &= ~PermissionInfo.FLAG_REMOVED;
                     }
                 }
             }
         } else {
             if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) {
                 pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
-                pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
             }
             if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) {
                 pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
-                pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
             }
             if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) {
                 pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
-                pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
-            }
-
-            if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_LEGACY, false)) {
-                if (pkg.requestedPermissions
-                        .contains(android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
-                    pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
-                    pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
-                    pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
-                }
-                if (pkg.requestedPermissions
-                        .contains(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
-                    pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_AUDIO);
-                    pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_VIDEO);
-                    pkg.requestedPermissions.add(android.Manifest.permission.WRITE_MEDIA_IMAGES);
-                }
             }
         }
 
@@ -5369,6 +5338,11 @@
             s.info.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE;
         }
         if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_useAppZygote,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_USE_APP_ZYGOTE;
+        }
+        if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestService_singleUser,
                 false)) {
             s.info.flags |= ServiceInfo.FLAG_SINGLE_USER;
@@ -6801,7 +6775,7 @@
 
         /** @hide */
         public boolean isForwardLocked() {
-            return applicationInfo.isForwardLocked();
+            return false;
         }
 
         /** @hide */
@@ -6843,9 +6817,7 @@
         public boolean canHaveOatDir() {
             // The following app types CANNOT have oat directory
             // - non-updated system apps
-            // - forward-locked apps or apps installed in ASEC containers
-            return (!isSystem() || isUpdatedSystemApp())
-                    && !isForwardLocked() && !applicationInfo.isExternalAsec();
+            return !isSystem() || isUpdatedSystemApp();
         }
 
         public boolean isMatch(int flags) {
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 91f884c..ad2c72274 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -56,6 +56,23 @@
     public static final int FLAG_EXTERNAL_SERVICE = 0x0004;
 
     /**
+     * Bit in {@link #flags}: If set, the service (which must be isolated)
+     * will be spawned from an Application Zygote, instead of the regular Zygote.
+     * The Application Zygote will pre-initialize the application's class loader,
+     * and call a static callback into the application to allow it to perform
+     * application-specific preloads (such as loading a shared library). Therefore,
+     * spawning from the Application Zygote will typically reduce the service
+     * launch time and reduce its memory usage. The downside of using this flag
+     * is that you will have an additional process (the app zygote itself) that
+     * is taking up memory. Whether actual memory usage is improved therefore
+     * strongly depends on the number of isolated services that an application
+     * starts, and how much memory those services save by preloading. Therefore,
+     * it is recommended to measure memory usage under typical workloads to
+     * determine whether it makes sense to use this flag.
+     */
+    public static final int FLAG_USE_APP_ZYGOTE = 0x0008;
+
+    /**
      * Bit in {@link #flags} indicating if the service is visible to ephemeral applications.
      * @hide
      */
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 4371c77..740cdae 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -58,7 +58,7 @@
 public final class AssetManager implements AutoCloseable {
     private static final String TAG = "AssetManager";
     private static final boolean DEBUG_REFS = false;
-    private static final boolean FEATURE_FLAG_IDMAP2 = false;
+    private static final boolean FEATURE_FLAG_IDMAP2 = true;
 
     private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
 
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 9db1f92..9d61f02 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -24,7 +24,7 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Surface;
-import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
 
 /**
  * Display manager local system service interface.
@@ -126,7 +126,7 @@
      * Called by the window manager to perform traversals while holding a
      * surface flinger transaction.
      */
-    public abstract void performTraversal(SurfaceControl.Transaction t);
+    public abstract void performTraversal(Transaction t);
 
     /**
      * Tells the display manager about properties of the display that depend on the windows on it.
@@ -383,6 +383,6 @@
      * update the position of its surfaces as part of the same transaction.
      */
     public interface DisplayTransactionListener {
-        void onDisplayTransaction();
+        void onDisplayTransaction(Transaction t);
     }
 }
diff --git a/core/java/android/hardware/location/ContextHubIntentEvent.java b/core/java/android/hardware/location/ContextHubIntentEvent.java
index 539c494..d1190ab 100644
--- a/core/java/android/hardware/location/ContextHubIntentEvent.java
+++ b/core/java/android/hardware/location/ContextHubIntentEvent.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.content.Intent;
 
@@ -30,6 +31,7 @@
  *
  * @hide
  */
+@SystemApi
 public class ContextHubIntentEvent {
     @ContextHubManager.Event private final int mEventType;
 
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 88fb3de..7639302 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -56,38 +56,28 @@
 
     /**
      * An extra of type {@link ContextHubInfo} describing the source of the event.
-     *
-     * @hide
      */
     public static final String EXTRA_CONTEXT_HUB_INFO =
             "android.hardware.location.extra.CONTEXT_HUB_INFO";
 
     /**
      * An extra of type {@link ContextHubManager.Event} describing the event type.
-     *
-     * @hide
      */
     public static final String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
 
     /**
      * An extra of type long describing the ID of the nanoapp an event is for.
-     *
-     * @hide
      */
     public static final String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID";
 
     /**
      * An extra of type int describing the nanoapp-specific abort code.
-     *
-     * @hide
      */
     public static final String EXTRA_NANOAPP_ABORT_CODE =
             "android.hardware.location.extra.NANOAPP_ABORT_CODE";
 
     /**
      * An extra of type {@link NanoAppMessage} describing contents of a message from a nanoapp.
-     *
-     * @hide
      */
     public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
 
@@ -109,56 +99,41 @@
 
     /**
      * An event describing that a nanoapp has been loaded. Contains the EXTRA_NANOAPP_ID extra.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_LOADED = 0;
 
     /**
      * An event describing that a nanoapp has been unloaded. Contains the EXTRA_NANOAPP_ID extra.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_UNLOADED = 1;
 
     /**
      * An event describing that a nanoapp has been enabled. Contains the EXTRA_NANOAPP_ID extra.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_ENABLED = 2;
 
     /**
      * An event describing that a nanoapp has been disabled. Contains the EXTRA_NANOAPP_ID extra.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_DISABLED = 3;
 
     /**
      * An event describing that a nanoapp has aborted. Contains the EXTRA_NANOAPP_ID and
      * EXTRA_NANOAPP_ABORT_CODE extras.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_ABORTED = 4;
 
     /**
      * An event containing a message sent from a nanoapp. Contains the EXTRA_NANOAPP_ID and
      * EXTRA_NANOAPP_MESSAGE extras.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_MESSAGE = 5;
 
     /**
      * An event describing that the Context Hub has reset.
-     *
-     * @hide
      */
     public static final int EVENT_HUB_RESET = 6;
 
-
     private final Looper mMainLooper;
     private final IContextHubService mService;
     private Callback mCallback;
@@ -797,14 +772,14 @@
      * Creates a ContextHubClient that will receive notifications based on Intent events.
      *
      * This method should be used instead of {@link #createClient(ContextHubInfo,
-     * ContextHubClientCallback)} and the equivalent API if the caller wants to preserve the
-     * messaging endpoint of a ContextHubClient, even after a process exits. If the PendingIntent
-     * with the provided nanoapp has already been registered at the service previously, then the
-     * same ContextHubClient will be regenerated without creating a new client connection at the
-     * service. Note that the PendingIntent, nanoapp, and Context Hub must all match in identifying
-     * a previously registered ContextHubClient. If a client is regenerated, it can be treated as
-     * the same endpoint entity from a nanoapp's perspective, and can be continued to be
-     * used to send messages even if the original process has exited.
+     * ContextHubClientCallback)} or {@link #createClient(ContextHubInfo, ContextHubClientCallback,
+     * Executor)} if the caller wants to preserve the messaging endpoint of a ContextHubClient, even
+     * after a process exits. If the PendingIntent with the provided nanoapp has already been
+     * registered at the service, then the same ContextHubClient will be regenerated without
+     * creating a new client connection at the service. Note that the PendingIntent, nanoapp, and
+     * Context Hub must all match in identifying a previously registered ContextHubClient.
+     * If a client is regenerated, the host endpoint identifier attached to messages sent to the
+     * nanoapp remains consistent, even if the original process has exited.
      *
      * If registered successfully, intents will be delivered regarding events or messages from the
      * specified nanoapp from the attached Context Hub. The intent will have an extra
@@ -815,10 +790,11 @@
      * each event type, along with event-specific extra fields. The client can also use
      * {@link ContextHubIntentEvent.fromIntent(Intent)} to parse the Intent generated by the event.
      *
-     * Intent events will be delivered until it is unregistered through
-     * {@link ContextHubClient.close()}. Note that the registration of this
-     * ContextHubClient at the Context Hub Service will continued to be maintained until
-     * {@link ContextHubClient.close()} is called.
+     * Intent events will be delivered until {@link ContextHubClient.close()} is called. Note that
+     * the registration of this ContextHubClient at the Context Hub Service will be maintained until
+     * {@link ContextHubClient.close()} is called. If {@link PendingIntent.cancel()} is called
+     * on the provided PendingIntent, then the client will be automatically unregistered by the
+     * service.
      *
      * @param hubInfo       the hub to attach this client to
      * @param pendingIntent the PendingIntent to register to the client
@@ -828,8 +804,6 @@
      * @throws IllegalArgumentException if hubInfo does not represent a valid hub
      * @throws IllegalStateException    if there were too many registered clients at the service
      * @throws NullPointerException     if pendingIntent or hubInfo is null
-     *
-     * @hide
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     @NonNull public ContextHubClient createClient(
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index 8599f47..3552655 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -65,10 +65,13 @@
     // An interval, in seconds between the NattKeepalive packets
     private int mNattKeepaliveInterval;
 
-    // XFRM mark and mask
+    // XFRM mark and mask; defaults to 0 (no mark/mask)
     private int mMarkValue;
     private int mMarkMask;
 
+    // XFRM interface id
+    private int mXfrmInterfaceId;
+
     /** Set the mode for this IPsec transform */
     public void setMode(int mode) {
         mMode = mode;
@@ -125,14 +128,30 @@
         mNattKeepaliveInterval = interval;
     }
 
+    /**
+     * Sets the mark value
+     *
+     * <p>Internal (System server) use only. Marks passed in by users will be overwritten or
+     * ignored.
+     */
     public void setMarkValue(int mark) {
         mMarkValue = mark;
     }
 
+    /**
+     * Sets the mark mask
+     *
+     * <p>Internal (System server) use only. Marks passed in by users will be overwritten or
+     * ignored.
+     */
     public void setMarkMask(int mask) {
         mMarkMask = mask;
     }
 
+    public void setXfrmInterfaceId(int xfrmInterfaceId) {
+        mXfrmInterfaceId = xfrmInterfaceId;
+    }
+
     // Transport or Tunnel
     public int getMode() {
         return mMode;
@@ -190,6 +209,10 @@
         return mMarkMask;
     }
 
+    public int getXfrmInterfaceId() {
+        return mXfrmInterfaceId;
+    }
+
     // Parcelable Methods
 
     @Override
@@ -213,6 +236,7 @@
         out.writeInt(mNattKeepaliveInterval);
         out.writeInt(mMarkValue);
         out.writeInt(mMarkMask);
+        out.writeInt(mXfrmInterfaceId);
     }
 
     @VisibleForTesting
@@ -235,6 +259,7 @@
         mNattKeepaliveInterval = c.mNattKeepaliveInterval;
         mMarkValue = c.mMarkValue;
         mMarkMask = c.mMarkMask;
+        mXfrmInterfaceId = c.mXfrmInterfaceId;
     }
 
     private IpSecConfig(Parcel in) {
@@ -255,6 +280,7 @@
         mNattKeepaliveInterval = in.readInt();
         mMarkValue = in.readInt();
         mMarkMask = in.readInt();
+        mXfrmInterfaceId = in.readInt();
     }
 
     @Override
@@ -289,6 +315,8 @@
                 .append(mMarkValue)
                 .append(", mMarkMask=")
                 .append(mMarkMask)
+                .append(", mXfrmInterfaceId=")
+                .append(mXfrmInterfaceId)
                 .append("}");
 
         return strBuilder.toString();
@@ -320,10 +348,10 @@
                 && lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
                 && lhs.mSpiResourceId == rhs.mSpiResourceId
                 && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption)
-                && IpSecAlgorithm.equals(
-                        lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
+                && IpSecAlgorithm.equals(lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
                 && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication)
                 && lhs.mMarkValue == rhs.mMarkValue
-                && lhs.mMarkMask == rhs.mMarkMask);
+                && lhs.mMarkMask == rhs.mMarkMask
+                && lhs.mXfrmInterfaceId == rhs.mXfrmInterfaceId);
     }
 }
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index 1999e78..cb82fbe 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -44,6 +44,8 @@
     public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10;
     public static final int NETWORK_REVALIDATION_PORTAL_FOUND     = 11;
 
+    public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12;
+
     @IntDef(value = {
             NETWORK_CONNECTED,
             NETWORK_VALIDATED,
@@ -56,6 +58,7 @@
             NETWORK_REVALIDATION_SUCCESS,
             NETWORK_FIRST_VALIDATION_PORTAL_FOUND,
             NETWORK_REVALIDATION_PORTAL_FOUND,
+            NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index c7184c0..1468fe5 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -400,6 +400,9 @@
      * }
      * </pre>
      *
+     * <p>The work source will be propagated for future outgoing binder transactions
+     * executed on this thread.
+     *
      * @param workSource The original UID responsible for the binder call.
      * @return token to restore original work source.
      * @hide
@@ -423,6 +426,9 @@
     /**
      * Clears the work source on this thread.
      *
+     * <p>The work source will be propagated for future outgoing binder transactions
+     * executed on this thread.
+     *
      * @return token to restore original work source.
      * @hide
      **/
@@ -442,6 +448,9 @@
      *   Binder.restoreCallingWorkSource(token);
      * }
      * </pre>
+     *
+     * <p>The work source will be propagated for future outgoing binder transactions
+     * executed on this thread.
      * @hide
      **/
     @CriticalNative
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 720c167..97d72f0 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -360,6 +360,16 @@
     }
 
     /**
+     * Returns the number of binder proxies held in this process.
+     * @return number of binder proxies in this process
+     */
+    public static int getProxyCount() {
+        synchronized (sProxyMap) {
+            return sProxyMap.size();
+        }
+    }
+
+    /**
      * Dump proxy debug information.
      *
      * @hide
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
index 7858c59..767c15c 100644
--- a/core/java/android/os/ConfigUpdate.java
+++ b/core/java/android/os/ConfigUpdate.java
@@ -83,6 +83,14 @@
             = "android.intent.action.UPDATE_SMART_SELECTION";
 
     /**
+     * Update conversation actions model file.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_CONVERSATION_ACTIONS
+            = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
+
+    /**
      * Update network watchlist config file.
      * @hide
      */
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8c5c415..900b62d 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -54,7 +54,7 @@
     private static final String TAG = "GraphicsEnvironment";
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
     private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
-    private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
+    private static final String ANGLE_PACKAGE_NAME = "com.google.android.angle";
     private static final String ANGLE_RULES_FILE = "a4a_rules.json";
     private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
 
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 1c1db68..894015f 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1008,7 +1008,8 @@
      * progress, does nothing. Unlike {@link #nap(long)}, this does not put device to sleep when
      * dream ends.
      * </p><p>
-     * Requires the {@link android.Manifest.permission#WRITE_DREAM_STATE} permission.
+     * Requires the {@link android.Manifest.permission#READ_DREAM_STATE} and
+     * {@link android.Manifest.permission#WRITE_DREAM_STATE} permissions.
      * </p>
      *
      * @param time The time when the request to nap was issued, in the
@@ -1019,7 +1020,9 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.READ_DREAM_STATE,
+            android.Manifest.permission.WRITE_DREAM_STATE })
     public void dream(long time) {
         Sandman.startDreamByUserRequest(mContext);
     }
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 866bd9a..acb9eac 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -43,6 +43,7 @@
     int mTag;
     long mElapsedTimeNs;
     long mWallClockTimeNs;
+    WorkSource mWorkSource = null;
 
     public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) {
         this.mTag = tag;
@@ -71,6 +72,17 @@
             };
 
     /**
+     * Set work source if any.
+     */
+    public void setWorkSource(WorkSource ws) {
+        if (ws.getWorkChains() == null || ws.getWorkChains().size() == 0) {
+            Slog.w(TAG, "Empty worksource!");
+            return;
+        }
+        mWorkSource = ws;
+    }
+
+    /**
      * Write a int value.
      */
     public void writeInt(int val) {
@@ -119,11 +131,6 @@
         mValues.add(val ? 1 : 0);
     }
 
-    /**
-     * Writes the stored fields to a byte array. Will first write a new-line character to denote
-     * END_LIST before writing contents to byte array.
-     */
-
     public void writeToParcel(Parcel out, int flags) {
         if (DEBUG) {
             Slog.d(TAG,
@@ -133,6 +140,34 @@
         out.writeInt(mTag);
         out.writeLong(mElapsedTimeNs);
         out.writeLong(mWallClockTimeNs);
+        if (mWorkSource != null) {
+            ArrayList<android.os.WorkSource.WorkChain> workChains = mWorkSource.getWorkChains();
+            // number of chains
+            out.writeInt(workChains.size());
+            for (int i = 0; i < workChains.size(); i++) {
+                android.os.WorkSource.WorkChain wc = workChains.get(i);
+                if (wc.getSize() == 0) {
+                    Slog.w(TAG, "Empty work chain.");
+                    out.writeInt(0);
+                    continue;
+                }
+                if (wc.getUids().length != wc.getTags().length
+                        || wc.getUids().length != wc.getSize()) {
+                    Slog.w(TAG, "Malformated work chain.");
+                    out.writeInt(0);
+                    continue;
+                }
+                // number of nodes
+                out.writeInt(wc.getSize());
+                for (int j = 0; j < wc.getSize(); j++) {
+                    out.writeInt(wc.getUids()[j]);
+                    out.writeString(wc.getTags()[j] == null ? "" : wc.getTags()[j]);
+                }
+            }
+        } else {
+            // no chains
+            out.writeInt(0);
+        }
         out.writeInt(mTypes.size());
         for (int i = 0; i < mTypes.size(); i++) {
             out.writeInt(mTypes.get(i));
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 423ce77..b42f1c4 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -137,8 +137,6 @@
     public static final String PROP_FORCE_VIDEO = "persist.fw.force_video";
     /** {@hide} */
     public static final String PROP_FORCE_IMAGES = "persist.fw.force_images";
-    /** {@hide} */
-    public static final String PROP_FORCE_LEGACY = "persist.fw.force_legacy";
 
     /** {@hide} */
     public static final String UUID_PRIVATE_INTERNAL = null;
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 16d454d..e032c18 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1232,7 +1232,7 @@
     public static Bitmap getDocumentThumbnail(ContentProviderClient client, Uri documentUri,
             Point size, CancellationSignal signal) throws IOException {
         return ContentResolver.loadThumbnail(client, documentUri, Point.convert(size), signal,
-                ImageDecoder.ALLOCATOR_DEFAULT);
+                ImageDecoder.ALLOCATOR_SOFTWARE);
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 291891e..e0e4fe2 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -572,6 +572,34 @@
         public void setSecondaryDirectory(@Nullable String secondaryDirectory) {
             this.secondaryDirectory = secondaryDirectory;
         }
+
+        /**
+         * Optionally set the Uri from where the file has been downloaded. This is used
+         * for files being added to {@link Downloads} table.
+         *
+         * @see DownloadColumns#DOWNLOAD_URI
+         */
+        public void setDownloadUri(@Nullable Uri downloadUri) {
+            if (downloadUri == null) {
+                this.insertValues.remove(DownloadColumns.DOWNLOAD_URI);
+            } else {
+                this.insertValues.put(DownloadColumns.DOWNLOAD_URI, downloadUri.toString());
+            }
+        }
+
+        /**
+         * Optionally set the Uri indicating HTTP referer of the file. This is used for
+         * files being added to {@link Downloads} table.
+         *
+         * @see DownloadColumns#REFERER_URI
+         */
+        public void setRefererUri(@Nullable Uri refererUri) {
+            if (refererUri == null) {
+                this.insertValues.remove(DownloadColumns.REFERER_URI);
+            } else {
+                this.insertValues.put(DownloadColumns.REFERER_URI, refererUri.toString());
+            }
+        }
     }
 
     /**
@@ -763,7 +791,7 @@
          * Type: BOOLEAN
          *
          * @see MediaStore#createPending(Context, PendingParams)
-         * @see MediaStore#QUERY_ARG_INCLUDE_PENDING
+         * @see MediaStore#PARAM_INCLUDE_PENDING
          */
         public static final String IS_PENDING = "is_pending";
 
@@ -927,6 +955,12 @@
              * Constant for the {@link #MEDIA_TYPE} column indicating that file is a playlist file.
              */
             public static final int MEDIA_TYPE_PLAYLIST = 4;
+
+            /**
+             * Column indicating if the file is part of Downloads collection.
+             * @hide
+             */
+            public static final String IS_DOWNLOAD = "is_download";
         }
     }
 
@@ -940,6 +974,80 @@
         public static final Point MICRO_SIZE = new Point(96, 96);
     }
 
+    /** Column fields for downloaded files used in {@link Downloads} table */
+    public interface DownloadColumns extends MediaColumns {
+        /**
+         * Uri indicating where the file has been downloaded from.
+         * <p>
+         * Type: TEXT
+         */
+        String DOWNLOAD_URI = "download_uri";
+
+        /**
+         * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}.
+         * <p>
+         * Type: TEXT
+         */
+        String REFERER_URI = "referer_uri";
+    }
+
+    /**
+     * Container for downloaded files.
+     *
+     * <p>
+     * Querying for downloads from this table will return files contributed via
+     * {@link PendingSession} and also ones which were downloaded using
+     * {@link android.app.DownloadManager} APIs.
+     */
+    public static final class Downloads implements DownloadColumns {
+        private Downloads() {}
+
+        /**
+         * The content:// style URI for the internal storage.
+         */
+        public static final Uri INTERNAL_CONTENT_URI =
+                getContentUri("internal");
+
+        /**
+         * The content:// style URI for the "primary" external storage
+         * volume.
+         */
+        public static final Uri EXTERNAL_CONTENT_URI =
+                getContentUri("external");
+
+        /**
+         * Get the content:// style URI for the downloads table on the
+         * given volume.
+         *
+         * @param volumeName the name of the volume to get the URI for
+         * @return the URI to the image media table on the given volume
+         */
+        public static Uri getContentUri(String volumeName) {
+            return AUTHORITY_URI.buildUpon().appendPath(volumeName)
+                    .appendPath("downloads").build();
+        }
+
+        /** @hide */
+        public static Uri getContentUriForPath(@NonNull String path) {
+            return getContentUri(getVolumeNameForPath(path));
+        }
+    }
+
+    private static String getVolumeNameForPath(@NonNull String path) {
+        final StorageManager sm = AppGlobals.getInitialApplication()
+                .getSystemService(StorageManager.class);
+        final StorageVolume sv = sm.getStorageVolume(new File(path));
+        if (sv != null) {
+            if (sv.isPrimary()) {
+                return VOLUME_EXTERNAL;
+            } else {
+                return sv.getUuid();
+            }
+        } else {
+            return VOLUME_INTERNAL;
+        }
+    }
+
     /**
      * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
      * to be accessed elsewhere.
@@ -1671,18 +1779,7 @@
              *             access this path.
              */
             public static @Nullable Uri getContentUriForPath(@NonNull String path) {
-                final StorageManager sm = AppGlobals.getInitialApplication()
-                        .getSystemService(StorageManager.class);
-                final StorageVolume sv = sm.getStorageVolume(new File(path));
-                if (sv != null) {
-                    if (sv.isPrimary()) {
-                        return EXTERNAL_CONTENT_URI;
-                    } else {
-                        return getContentUri(sv.getUuid());
-                    }
-                } else {
-                    return INTERNAL_CONTENT_URI;
-                }
+                return getContentUri(getVolumeNameForPath(path));
             }
 
             /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 929466c..d772903 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1710,6 +1710,15 @@
     /** @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";
+
+    /** @hide - Private call() method to query the 'secure' table */
+    public static final String CALL_METHOD_LIST_SECURE = "LIST_secure";
+
+    /** @hide - Private call() method to query the 'global' table */
+    public static final String CALL_METHOD_LIST_GLOBAL = "LIST_global";
+
     /**
      * Activity Extra: Limit available options in launched activity based on the given authority.
      * <p>
@@ -7958,6 +7967,14 @@
                 "managed_profile_contact_remote_search";
 
         /**
+         * Whether parent profile can access remote calendar data in managed profile.
+         *
+         * @hide
+         */
+        public static final String CROSS_PROFILE_CALENDAR_ENABLED =
+                "cross_profile_calendar_enabled";
+
+        /**
          * Whether or not the automatic storage manager is enabled and should run on the device.
          *
          * @hide
@@ -9330,6 +9347,13 @@
             "location_background_throttle_package_whitelist";
 
         /**
+         * Whether to disable location status callbacks in preparation for deprecation.
+         * @hide
+         */
+        public static final String LOCATION_DISABLE_STATUS_CALLBACKS =
+                "location_disable_status_callbacks";
+
+        /**
          * Maximum staleness allowed for last location when returned to clients with only foreground
          * location permissions.
          * @hide
@@ -10764,6 +10788,41 @@
         public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
 
         /**
+         * The threshold value for the number of consecutive dns timeout events received to be a
+         * signal of data stall. Set the value to 0 or less than 0 to disable. Note that the value
+         * should be larger than 0 if the DNS data stall detection is enabled.
+         *
+         * @hide
+         */
+        public static final String DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD =
+                "data_stall_consecutive_dns_timeout_threshold";
+
+        /**
+         * The minimal time interval in milliseconds for data stall reevaluation.
+         *
+         * @hide
+         */
+        public static final String DATA_STALL_MIN_EVALUATE_INTERVAL =
+                "data_stall_min_evaluate_interval";
+
+        /**
+         * DNS timeouts older than this timeout (in milliseconds) are not considered for detecting
+         * a data stall.
+         *
+         * @hide
+         */
+        public static final String DATA_STALL_VALID_DNS_TIME_THRESHOLD =
+                "data_stall_valid_dns_time_threshold";
+
+        /**
+         * Which data stall detection signal to use. Possible values are a union of the powers of 2
+         * of DATA_STALL_EVALUATION_TYPE_*.
+         *
+         * @hide
+         */
+        public static final String DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type";
+
+        /**
          * Whether network service discovery is enabled.
          *
          * @hide
@@ -10984,6 +11043,16 @@
                 = "activity_starts_logging_enabled";
 
         /**
+         * Feature flag to enable or disable the background activity starts.
+         * When disabled, apps aren't allowed to start activities unless they're in the foreground.
+         * Type: int (0 for false, 1 for true)
+         * Default: 1
+         * @hide
+         */
+        public static final String BACKGROUND_ACTIVITY_STARTS_ENABLED =
+                "background_activity_starts_enabled";
+
+        /**
          * @hide
          * @see com.android.server.appbinding.AppBindingConstants
          */
@@ -11549,7 +11618,7 @@
         /**
          * Whether or not show hidden launcher icon apps feature is enabled.
          * Type: int (0 for false, 1 for true)
-         * Default: 0
+         * Default: 1
          * @hide
          */
         public static final String SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED =
@@ -12129,6 +12198,20 @@
                 "smart_selection_metadata_url";
 
         /**
+         * URL for conversation actions model updates
+         * @hide
+         */
+        public static final String CONVERSATION_ACTIONS_UPDATE_CONTENT_URL =
+                "conversation_actions_content_url";
+
+        /**
+         * URL for conversation actions model update metadata
+         * @hide
+         */
+        public static final String CONVERSATION_ACTIONS_UPDATE_METADATA_URL =
+                "conversation_actions_metadata_url";
+
+        /**
          * SELinux enforcement status. If 0, permissive; if 1, enforcing.
          * @hide
          */
@@ -12678,6 +12761,17 @@
         public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets";
 
         /**
+         * Used to emulate Smart Suggestion for Augmented Autofill during development
+         *
+         * <p>Valid values: {@code 0x1} for IME and/or {@code 0x2} for popup window.
+         *
+         * @hide
+         */
+        @TestApi
+        public static final String AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS =
+                "autofill_smart_suggestion_emulation_flags";
+
+        /**
          * Exemptions to the hidden API blacklist.
          *
          * @hide
@@ -13677,6 +13771,15 @@
          */
         public static final String LAST_ACTIVE_USER_ID = "last_active_persistent_user_id";
 
+
+        /**
+         * Whether we've enabled native flags health check on this device. Takes effect on
+         * reboot. The value "1" enables native flags health check; otherwise it's disabled.
+         * @hide
+         */
+        public static final String NATIVE_FLAGS_HEALTH_CHECK_ENABLED =
+                "native_flags_health_check_enabled";
+
     }
 
     /**
diff --git a/core/java/android/service/intelligence/FillCallback.java b/core/java/android/service/intelligence/FillCallback.java
new file mode 100644
index 0000000..af2da79
--- /dev/null
+++ b/core/java/android/service/intelligence/FillCallback.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Callback used to indicate at {@link FillRequest} has been fulfilled.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillCallback {
+
+    FillCallback() {}
+
+    /**
+     * Sets the response associated with the request.
+     *
+     * @param response response associated with the request, or {@code null} if the service
+     * could not provide autofill for the request.
+     */
+    public void onSuccess(@Nullable FillResponse response) {
+        final FillWindow fillWindow = response.getFillWindow();
+        if (fillWindow != null) {
+            fillWindow.show();
+        }
+        // TODO(b/111330312): properly implement on server-side by updating the Session state
+        // accordingly (and adding CTS tests)
+    }
+}
diff --git a/core/java/android/service/intelligence/FillController.java b/core/java/android/service/intelligence/FillController.java
new file mode 100644
index 0000000..c5e1242
--- /dev/null
+++ b/core/java/android/service/intelligence/FillController.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import static android.service.intelligence.IntelligenceService.DEBUG;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.util.Log;
+import android.util.Pair;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Object used to interact with the autofill system.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillController {
+    private static final String TAG = "FillController";
+
+    private final AutofillProxy mProxy;
+
+    FillController(@NonNull AutofillProxy proxy) {
+        mProxy = proxy;
+    }
+
+    /**
+     * Fills the activity with the provided values.
+     *
+     * <p>As a side effect, the {@link FillWindow} associated with the {@link FillResponse} will be
+     * automatically {@link FillWindow#destroy() destroyed}.
+     */
+    public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> values) {
+        Preconditions.checkNotNull(values);
+
+        if (DEBUG) {
+            Log.d(TAG, "autofill() with " + values.size() + " values");
+        }
+
+        try {
+            mProxy.autofill(values);
+            final FillWindow fillWindow = mProxy.getFillWindow();
+            if (fillWindow != null) {
+                fillWindow.destroy();
+            }
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+}
diff --git a/core/java/android/service/intelligence/FillRequest.java b/core/java/android/service/intelligence/FillRequest.java
new file mode 100644
index 0000000..95e9224
--- /dev/null
+++ b/core/java/android/service/intelligence/FillRequest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.view.autofill.AutofillId;
+
+/**
+ * Represents a request to augment-fill an activity.
+ * @hide
+ */
+@SystemApi
+public final class FillRequest {
+
+    final AutofillProxy mProxy;
+
+    /** @hide */
+    FillRequest(@NonNull AutofillProxy proxy) {
+        mProxy = proxy;
+    }
+
+    /**
+     * Gets the session associated with this request.
+     */
+    @NonNull
+    public InteractionSessionId getSessionId() {
+        return mProxy.sessionId;
+    }
+
+    /**
+     * Gets the id of the field that triggered the request.
+     */
+    @NonNull
+    public AutofillId getFocusedId() {
+        return mProxy.focusedId;
+    }
+
+    /**
+     * Gets the Smart Suggestions object used to embed the autofill UI.
+     *
+     * @return object used to embed the autofill UI, or {@code null} if not supported.
+     */
+    @Nullable
+    public PresentationParams getPresentationParams() {
+        return mProxy.getSmartSuggestionParams();
+    }
+
+    @Override
+    public String toString() {
+        return "FillRequest[id=" + mProxy.focusedId + "]";
+    }
+}
diff --git a/core/java/android/service/intelligence/FillResponse.java b/core/java/android/service/intelligence/FillResponse.java
new file mode 100644
index 0000000..860c027
--- /dev/null
+++ b/core/java/android/service/intelligence/FillResponse.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+
+import java.util.List;
+
+/**
+ * Response to a {@link FillRequest}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillResponse implements Parcelable {
+
+    private final FillWindow mFillWindow;
+
+    private FillResponse(@NonNull Builder builder) {
+        mFillWindow = builder.mFillWindow;
+    }
+
+    /** @hide */
+    @Nullable
+    FillWindow getFillWindow() {
+        return mFillWindow;
+    }
+
+    /**
+     * Builder for {@link FillResponse} objects.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static class Builder {
+
+        private FillWindow mFillWindow;
+
+        /**
+         * Sets the {@link FillWindow} used to display the Autofill UI.
+         *
+         * <p>Must be called when the service is handling the request so the Android System can
+         * properly synchronize the UI.
+         *
+         * @return this builder
+         */
+        public Builder setFillWindow(@NonNull FillWindow fillWindow) {
+            // TODO(b/111330312): implement / check not null / unit test
+            // TODO(b/111330312): throw exception if FillWindow not updated yet
+            mFillWindow = fillWindow;
+            return this;
+        }
+
+        /**
+         * Tells the Android System that the given {@code ids} should not trigger further
+         * {@link FillRequest requests} when focused.
+         *
+         * @param ids ids of the fields that should be ignored
+         *
+         * @return this builder
+         */
+        public Builder setIgnoredIds(@NonNull List<AutofillId> ids) {
+            // TODO(b/111330312): implement / check not null / unit test
+            return this;
+        }
+
+        /**
+         * Builds a new {@link FillResponse} instance.
+         *
+         * @throws IllegalStateException if any of the following conditions occur:
+         * <ol>
+         *   <li>{@link #build()} was already called.
+         *   <li>No call was made to {@link #setFillWindow(FillWindow)} or
+         *   {@ling #setIgnoredIds(List<AutofillId>)}.
+         * </ol>
+         *
+         * @return A built response.
+         */
+        public FillResponse build() {
+            // TODO(b/111330312): check conditions / add unit test
+            return new FillResponse(this);
+        }
+
+        // TODO(b/111330312): add methods to disable app / activity, either here or on manager
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        // TODO(b/111330312): implement
+    }
+
+    public static final Parcelable.Creator<FillResponse> CREATOR =
+            new Parcelable.Creator<FillResponse>() {
+
+                @Override
+                public FillResponse createFromParcel(Parcel parcel) {
+                    // TODO(b/111330312): implement
+                    return null;
+                }
+
+                @Override
+                public FillResponse[] newArray(int size) {
+                    return new FillResponse[size];
+                }
+    };
+}
diff --git a/core/java/android/service/intelligence/FillWindow.java b/core/java/android/service/intelligence/FillWindow.java
new file mode 100644
index 0000000..4ea07bf
--- /dev/null
+++ b/core/java/android/service/intelligence/FillWindow.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import static android.service.intelligence.IntelligenceService.DEBUG;
+
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Dialog;
+import android.graphics.Rect;
+import android.service.intelligence.PresentationParams.Area;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Handle to a window used to display the augmented autofill UI.
+ *
+ * <p>The steps to create an augmented autofill UI are:
+ *
+ * <ol>
+ *   <li>Gets the {@link PresentationParams} from the {@link FillRequest}.
+ *   <li>Gets the {@link Area} to display the UI (for example, through
+ *   {@link PresentationParams#getSuggestionArea()}.
+ *   <li>Creates a {@link View} that must fit in the {@link Area#getBounds() area boundaries}.
+ *   <li>Set the proper listeners to the view (for example, a click listener that
+ *   triggers {@link FillController#autofill(java.util.List)}
+ *   <li>Call {@link #update(Area, View, long)} with these arguments.
+ *   <li>Create a {@link FillResponse} with the {@link FillWindow}.
+ *   <li>Pass such {@link FillResponse} to {@link FillCallback#onSuccess(FillResponse)}.
+ * </ol>
+ *
+ * @hide
+ */
+@SystemApi
+public final class FillWindow {
+    private static final String TAG = "FillWindow";
+
+    /** Indicates the data being shown is a physical address */
+    public static final long FLAG_METADATA_ADDRESS = 0x1;
+
+    // TODO(b/111330312): add moar flags
+
+    /** @hide */
+    @LongDef(prefix = { "FLAG" }, value = {
+            FLAG_METADATA_ADDRESS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Flags{}
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private Dialog mDialog;
+
+    @GuardedBy("mLock")
+    private boolean mDestroyed;
+
+    /**
+     * Updates the content of the window.
+     *
+     * @param rootView new root view
+     * @param area coordinates to render the view.
+     * @param flags optional flags such as metadata of what will be rendered in the window. The
+     * Smart Suggestion host might decide whether or not to render the UI based on them.
+     *
+     * @return boolean whether the window was updated or not.
+     *
+     * @throws IllegalArgumentException if the area is not compatible with this window
+     */
+    public boolean update(@NonNull Area area, @NonNull View rootView, @Flags long flags) {
+        if (DEBUG) {
+            Log.d(TAG, "Updating " + area + " + with " + rootView);
+        }
+        // TODO(b/111330312): add test case for null
+        Preconditions.checkNotNull(area);
+        Preconditions.checkNotNull(rootView);
+        // TODO(b/111330312): must check the area is a valid object returned by
+        // SmartSuggestionParams, throw IAE if not
+
+        // TODO(b/111330312): must some how pass metadata to the SmartSuggestiongs provider
+
+
+        // TODO(b/111330312): use a SurfaceControl approach; for now, we're manually creating
+        // the window underneath the existing view.
+
+        final PresentationParams smartSuggestion = area.proxy.getSmartSuggestionParams();
+        if (smartSuggestion == null) {
+            Log.w(TAG, "No SmartSuggestionParams");
+            return false;
+        }
+
+        final Rect rect = area.getBounds();
+        if (rect == null) {
+            Log.wtf(TAG, "No Rect on SmartSuggestionParams");
+            return false;
+        }
+
+        synchronized (mLock) {
+            checkNotDestroyedLocked();
+
+            // TODO(b/111330312): once we have the SurfaceControl approach, we should update the
+            // window instead of destroying. In fact, it might be better to allocate a full window
+            // initially, which is transparent (and let touches get through) everywhere but in the
+            // rect boundaries.
+            destroy();
+
+            // TODO(b/111330312): make sure all touch events are handled, window is always closed,
+            // etc.
+
+            mDialog = new Dialog(rootView.getContext());
+            final Window window = mDialog.getWindow();
+            window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+
+            final int height = rect.bottom - rect.top;
+            final int width = rect.right - rect.left;
+            final WindowManager.LayoutParams windowParams = window.getAttributes();
+            windowParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+            windowParams.y = rect.top - height;
+            windowParams.height = height;
+            windowParams.x = rect.left;
+            windowParams.width = width;
+
+            window.setAttributes(windowParams);
+            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+
+            mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+            final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
+            mDialog.setContentView(rootView, diagParams);
+
+            if (DEBUG) {
+                Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
+            }
+
+            area.proxy.setFillWindow(this);
+            return true;
+        }
+    }
+
+    /** @hide */
+    void show() {
+        // TODO(b/111330312): check if updated first / throw exception
+        if (DEBUG) Log.d(TAG, "show()");
+
+        synchronized (mLock) {
+            checkNotDestroyedLocked();
+            if (mDialog == null) {
+                throw new IllegalStateException("update() not called yet, or already destroyed()");
+            }
+
+            mDialog.show();
+        }
+    }
+
+    /**
+     * Destroys the window.
+     *
+     * <p>Once destroyed, this window cannot be used anymore
+     */
+    public void destroy() {
+        if (DEBUG) Log.d(TAG, "destroy(): mDestroyed = " + mDestroyed);
+
+        synchronized (this) {
+            if (mDestroyed) return;
+
+            if (mDialog != null) {
+                mDialog.dismiss();
+                mDialog = null;
+            }
+        }
+    }
+
+    private void checkNotDestroyedLocked() {
+        if (mDestroyed) {
+            throw new IllegalStateException("already destroyed()");
+        }
+    }
+
+    /** @hide */
+    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        synchronized (this) {
+            pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+            if (mDialog != null) {
+                pw.print(prefix); pw.print("dialog: ");
+                pw.println(mDialog.isShowing() ? "shown" : "hidden");
+                pw.print(prefix); pw.print("window: ");
+                pw.println(mDialog.getWindow().getAttributes());
+            }
+        }
+    }
+}
diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl
index 709c3b7..e2260d7 100644
--- a/core/java/android/service/intelligence/IIntelligenceService.aidl
+++ b/core/java/android/service/intelligence/IIntelligenceService.aidl
@@ -16,10 +16,12 @@
 
 package android.service.intelligence;
 
+import android.os.IBinder;
 import android.service.intelligence.InteractionSessionId;
 import android.service.intelligence.InteractionContext;
 import android.service.intelligence.SnapshotData;
 
+import android.view.autofill.AutofillId;
 import android.view.intelligence.ContentCaptureEvent;
 
 import java.util.List;
@@ -40,4 +42,9 @@
 
     void onActivitySnapshot(in InteractionSessionId sessionId,
                             in SnapshotData snapshotData);
+
+    void onAutofillRequest(in InteractionSessionId sessionId, in IBinder autofillManagerClient,
+                           int autofilSessionId, in AutofillId focusedId);
+
+    void onDestroyAutofillWindowsRequest(in InteractionSessionId sessionId);
 }
diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/IntelligenceService.java
index 27569b6..040e25e 100644
--- a/core/java/android/service/intelligence/IntelligenceService.java
+++ b/core/java/android/service/intelligence/IntelligenceService.java
@@ -22,13 +22,26 @@
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
+import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.service.intelligence.PresentationParams.SystemPopupPresentationParams;
+import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.view.autofill.IAugmentedAutofillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -44,6 +57,9 @@
 
     private static final String TAG = "IntelligenceService";
 
+    // TODO(b/111330312): STOPSHIP use dynamic value, or change to false
+    static final boolean DEBUG = true;
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      * To be supported, the service must also require the
@@ -55,6 +71,8 @@
 
     private Handler mHandler;
 
+    private ArrayMap<InteractionSessionId, AutofillProxy> mAutofillProxies;
+
     private final IIntelligenceService mInterface = new IIntelligenceService.Stub() {
 
         @Override
@@ -87,6 +105,20 @@
                     obtainMessage(IntelligenceService::onActivitySnapshot,
                             IntelligenceService.this, sessionId, snapshotData));
         }
+
+        @Override
+        public void onAutofillRequest(InteractionSessionId sessionId, IBinder client,
+                int autofilSessionId, AutofillId focusedId) {
+            mHandler.sendMessage(obtainMessage(IntelligenceService::handleOnAutofillRequest,
+                    IntelligenceService.this, sessionId, client, autofilSessionId, focusedId));
+        }
+
+        @Override
+        public void onDestroyAutofillWindowsRequest(InteractionSessionId sessionId) {
+            mHandler.sendMessage(
+                    obtainMessage(IntelligenceService::handleOnDestroyAutofillWindowsRequest,
+                            IntelligenceService.this, sessionId));
+        }
     };
 
     @CallSuper
@@ -122,10 +154,93 @@
      * @param sessionId the session's Id
      * @param events the events
      */
-     // TODO(b/111276913): rename to onContentCaptureEvents
+    // TODO(b/111276913): rename to onContentCaptureEvents or something like that; also, pass a
+    // Request object so it can be extended
     public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId,
             @NonNull List<ContentCaptureEvent> events);
 
+    private void handleOnAutofillRequest(@NonNull InteractionSessionId sessionId,
+            @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId) {
+        if (mAutofillProxies == null) {
+            mAutofillProxies = new ArrayMap<>();
+        }
+        AutofillProxy proxy = mAutofillProxies.get(sessionId);
+        if (proxy == null) {
+            proxy = new AutofillProxy(sessionId, client, autofillSessionId, focusedId);
+            mAutofillProxies.put(sessionId,  proxy);
+        } else {
+            // TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging
+            if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
+        }
+        // TODO(b/111330312): set cancellation signal
+        final CancellationSignal cancellationSignal = null;
+        onFillRequest(sessionId, new FillRequest(proxy), cancellationSignal,
+                new FillController(proxy), new FillCallback());
+    }
+
+    /**
+     * Asks the service to handle an "augmented" autofill request.
+     *
+     * <p>This method is called when the "stantard" autofill service cannot handle a request, which
+     * typically occurs when:
+     * <ul>
+     *   <li>Service does not recognize what should be autofilled.
+     *   <li>Service does not have data to fill the request.
+     *   <li>Service blacklisted that app (or activity) for autofill.
+     *   <li>App disabled itself for autofill.
+     * </ul>
+     *
+     * <p>Differently from the standard autofill workflow, on augmented autofill the service is
+     * responsible to generate the autofill UI and request the Android system to autofill the
+     * activity when the user taps an action in that UI (through the
+     * {@link FillController#autofill(List)} method).
+     *
+     * <p>The service <b>MUST</b> call {@link
+     * FillCallback#onSuccess(android.service.intelligence.FillResponse)} as soon as possible,
+     * passing {@code null} when it cannot fulfill the request.
+     *
+     * @param sessionId the session's id
+     * @param request the request to handle.
+     * @param cancellationSignal signal for observing cancellation requests. The system will use
+     *     this to notify you that the fill result is no longer needed and you should stop
+     *     handling this fill request in order to save resources.
+     * @param controller object used to interact with the autofill system.
+     * @param callback object used to notify the result of the request. Service <b>must</b> call
+     * {@link FillCallback#onSuccess(android.service.intelligence.FillResponse)}.
+     */
+    public void onFillRequest(@NonNull InteractionSessionId sessionId, @NonNull FillRequest request,
+            @NonNull CancellationSignal cancellationSignal, @NonNull FillController controller,
+            @NonNull FillCallback callback) {
+    }
+
+    private void handleOnDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) {
+        AutofillProxy proxy = null;
+        if (mAutofillProxies != null) {
+            proxy = mAutofillProxies.get(sessionId);
+        }
+        if (proxy == null) {
+            // TODO(b/111330312): this might be fine, in which case we should logv it
+            Log.w(TAG, "No proxy for session " + sessionId);
+            return;
+        }
+        proxy.destroy();
+        mAutofillProxies.remove(sessionId);
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mAutofillProxies != null) {
+            final int size = mAutofillProxies.size();
+            pw.print("Number proxies: "); pw.println(size);
+            for (int i = 0; i < size; i++) {
+                final InteractionSessionId sessionId = mAutofillProxies.keyAt(i);
+                final AutofillProxy proxy = mAutofillProxies.valueAt(i);
+                pw.print(i); pw.print(") SessionId="); pw.print(sessionId); pw.println(":");
+                proxy.dump("  ", pw);
+            }
+        }
+    }
+
     /**
      * Notifies the service of {@link IntelligenceSnapshotData snapshot data} associated with a
      * session.
@@ -142,4 +257,99 @@
      * @param sessionId the id of the session to destroy
      */
     public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {}
+
+    /** @hide */
+    static final class AutofillProxy {
+        private final Object mLock = new Object();
+        private final IAugmentedAutofillManagerClient mClient;
+        private final int mAutofillSessionId;
+        public final InteractionSessionId sessionId;
+        public final AutofillId focusedId;
+
+        @GuardedBy("mLock")
+        private SystemPopupPresentationParams mSmartSuggestion;
+
+        @GuardedBy("mLock")
+        private FillWindow mFillWindow;
+
+        private AutofillProxy(@NonNull InteractionSessionId sessionId, @NonNull IBinder client,
+                int autofillSessionId, @NonNull AutofillId focusedId) {
+            this.sessionId = sessionId;
+            mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
+            mAutofillSessionId = autofillSessionId;
+            this.focusedId = focusedId;
+            // TODO(b/111330312): linkToDeath
+        }
+
+        @NonNull
+        public SystemPopupPresentationParams getSmartSuggestionParams() {
+            synchronized (mLock) {
+                if (mSmartSuggestion != null) {
+                    return mSmartSuggestion;
+                }
+                Rect rect;
+                try {
+                    rect = mClient.getViewCoordinates(focusedId);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Could not get coordinates for " + focusedId);
+                    return null;
+                }
+                if (rect == null) {
+                    if (DEBUG) Log.d(TAG, "getViewCoordinates(" + focusedId + ") returned null");
+                    return null;
+                }
+                mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
+                return mSmartSuggestion;
+            }
+        }
+
+        public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> pairs)
+                throws RemoteException {
+            final int size = pairs.size();
+            final List<AutofillId> ids = new ArrayList<>(size);
+            final List<AutofillValue> values = new ArrayList<>(size);
+            for (int i = 0; i < size; i++) {
+                final Pair<AutofillId, AutofillValue> pair = pairs.get(i);
+                ids.add(pair.first);
+                values.add(pair.second);
+            }
+            mClient.autofill(mAutofillSessionId, ids, values);
+        }
+
+        public void setFillWindow(@NonNull FillWindow fillWindow) {
+            synchronized (mLock) {
+                mFillWindow = fillWindow;
+            }
+        }
+
+        public FillWindow getFillWindow() {
+            synchronized (mLock) {
+                return mFillWindow;
+            }
+        }
+
+        public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+            pw.print(prefix); pw.print("afSessionId: "); pw.println(mAutofillSessionId);
+            pw.print(prefix); pw.print("focusedId: "); pw.println(focusedId);
+            pw.print(prefix); pw.print("client: "); pw.println(mClient);
+            final String prefix2 = prefix + "  ";
+            if (mFillWindow != null) {
+                pw.print(prefix); pw.println("window:");
+                mFillWindow.dump(prefix2, pw);
+            }
+            if (mSmartSuggestion != null) {
+                pw.print(prefix); pw.println("smartSuggestion:");
+                mSmartSuggestion.dump(prefix2, pw);
+            }
+        }
+
+        private void destroy() {
+            synchronized (mLock) {
+                if (mFillWindow != null) {
+                    if (DEBUG) Log.d(TAG, "destroying window");
+                    mFillWindow.destroy();
+                }
+            }
+        }
+    }
 }
diff --git a/core/java/android/service/intelligence/PresentationParams.java b/core/java/android/service/intelligence/PresentationParams.java
new file mode 100644
index 0000000..c59069b
--- /dev/null
+++ b/core/java/android/service/intelligence/PresentationParams.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.graphics.Rect;
+import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.util.DebugUtils;
+import android.view.View;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Abstraction of a "Smart Suggestion" component responsible to embed the autofill UI provided by
+ * the intelligence service.
+ *
+ * <p>The Smart Suggestion can embed the autofill UI in 3 distinct places:
+ *
+ * <ul>
+ *   <li>A small area associated with suggestions (like a small strip in the top of the IME),
+ *   returned by {@link #getSuggestionArea()}
+ *   <li>The full area (like the full IME window), returned by {@link #getFullArea()}
+ *   <li>A subset of the aforementioned areas, returned by {@link Area#getSubArea(Rect)}
+ * </ul>
+ *
+ * <p>The Smart Suggestion is represented by a {@link Area} object that contains the
+ * dimensions the smart suggestion window, so the service can use it to calculate the size of the
+ * view that will be passed to {@link FillWindow#update(Area, View, long)}.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class PresentationParams {
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted in the top of its container.
+     */
+    public static final int FLAG_HINT_GRAVITY_TOP = 0x1;
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted in the bottom of its container.
+     */
+    public static final int FLAG_HINT_GRAVITY_BOTTOM = 0x2;
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted in the left of its container.
+     */
+    public static final int FLAG_HINT_GRAVITY_LEFT = 0x4;
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted in the right of its container.
+     */
+    public static final int FLAG_HINT_GRAVITY_RIGHT = 0x8;
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted by the IME.
+     */
+    public static final int FLAG_HOST_IME = 0x10;
+
+    /**
+     * Flag indicating the Smart Suggestion is hosted by the Android System as a floating popup
+     * window.
+     */
+    public static final int FLAG_HOST_SYSTEM = 0x20;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_HINT_GRAVITY_TOP,
+            FLAG_HINT_GRAVITY_BOTTOM,
+            FLAG_HINT_GRAVITY_LEFT,
+            FLAG_HINT_GRAVITY_RIGHT,
+            FLAG_HOST_IME,
+            FLAG_HOST_SYSTEM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Flags {}
+
+
+    // /** @hide */
+    PresentationParams() {}
+
+    /**
+     * Gets the area of the suggestion strip for the given {@code metadata}
+     *
+     * @return strip dimensions, or {@code null} if the Smart Suggestion provider does not support
+     * suggestions strip.
+     */
+    @Nullable
+    public Area getSuggestionArea() {
+        return null;
+    }
+
+    /**
+     * Gets the full area for the of the Smart Suggestion provider.
+     *
+     * @return full dimensions, or {@code null} if the Smart Suggestion provider does not support
+     * embeding the UI on its full area.
+     */
+    @Nullable
+    public Area getFullArea() {
+        return null;
+    }
+
+    /**
+     * Gets flags associated with the Smart Suggestion.
+     *
+     * @return any combination of {@link #FLAG_HINT_GRAVITY_TOP},
+     * {@link #FLAG_HINT_GRAVITY_BOTTOM}, {@link #FLAG_HINT_GRAVITY_LEFT},
+     * {@link #FLAG_HINT_GRAVITY_RIGHT}, {@link #FLAG_HOST_IME}, or
+     * {@link #FLAG_HOST_SYSTEM},
+     */
+    public @Flags int getFlags() {
+        return 0;
+    }
+
+    /** @hide */
+    void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        final int flags = getFlags();
+        if (flags > 0) {
+            pw.print(prefix); pw.print("flags: "); pw.println(flagsToString(flags));
+        }
+    }
+
+    private static String flagsToString(int flags) {
+        return DebugUtils.flagsToString(PresentationParams.class, "FLAG_", flags);
+    }
+
+    /**
+     * Area associated with a {@link PresentationParams Smart Suggestions} provider.
+     *
+     * @hide
+     * */
+    @SystemApi
+    public abstract static class Area {
+
+        /** @hide */
+        public final AutofillProxy proxy;
+
+        private final Rect mBounds;
+
+        private Area(@NonNull AutofillProxy proxy, @NonNull Rect bounds) {
+            this.proxy = proxy;
+            mBounds = bounds;
+        }
+
+        /**
+         * Gets the area boundaries.
+         */
+        @NonNull
+        public Rect getBounds() {
+            return mBounds;
+        }
+
+        /**
+         * Gets a subarea limited by given boundaries.
+         *
+         * @param bounds boundaries relative to this Area.
+         *
+         * @throws {@link IllegalArgumentException} if the {@code bounds} is not fully-contained
+         * inside this full Area.
+         *
+         * @return new subarea, or {@code null} if the Smart Suggestion host does not support such
+         * subaarea.
+         */
+        @Nullable
+        public Area getSubArea(@NonNull Rect bounds) {
+            // TODO(b/111330312): implement / check boundaries / throw IAE / add unit test
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            return mBounds.toString();
+        }
+    }
+
+    /**
+     * System-provided poup window anchored to a view.
+     *
+     * <p>Used just for debugging purposes.
+     *
+     * @hide
+     */
+    public static final class SystemPopupPresentationParams extends PresentationParams {
+        private final Area mSuggestionArea;
+
+        public SystemPopupPresentationParams(@NonNull AutofillProxy proxy, @NonNull Rect rect) {
+            mSuggestionArea = new Area(proxy, rect) {};
+        }
+
+        @Override
+        public Area getSuggestionArea() {
+            return mSuggestionArea;
+        }
+
+        @Override
+        public int getFlags() {
+            return FLAG_HOST_SYSTEM | FLAG_HINT_GRAVITY_BOTTOM;
+        }
+
+        @Override
+        void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+            super.dump(prefix, pw);
+            pw.print(prefix); pw.print("area: "); pw.println(mSuggestionArea);
+        }
+    }
+}
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index d8bd002..1ddc099e 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -16,6 +16,7 @@
 
 package android.service.notification;
 
+import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.content.pm.ParceledListSlice;
@@ -47,4 +48,8 @@
     void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel);
     void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
     void onNotificationsSeen(in List<String> keys);
+    void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
+    void onNotificationDirectReply(String key);
+    void onSuggestedReplySent(String key, in CharSequence reply, int source);
+    void onActionClicked(String key, in Notification.Action action, int source);
 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index c1a3c2b..c850a4e 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -16,9 +16,14 @@
 
 package android.service.notification;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
@@ -33,6 +38,7 @@
 
 import com.android.internal.os.SomeArgs;
 
+import java.lang.annotation.Retention;
 import java.util.List;
 
 /**
@@ -63,6 +69,13 @@
 public abstract class NotificationAssistantService extends NotificationListenerService {
     private static final String TAG = "NotificationAssistants";
 
+    /** @hide */
+    @Retention(SOURCE)
+    @IntDef({SOURCE_FROM_APP, SOURCE_FROM_ASSISTANT})
+    public @interface Source {}
+    public static final int SOURCE_FROM_APP = 0;
+    public static final int SOURCE_FROM_ASSISTANT = 1;
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
@@ -160,6 +173,38 @@
     }
 
     /**
+     * Implement this to know when a notification is expanded / collapsed.
+     * @param key the notification key
+     * @param isUserAction whether the expanded change is caused by user action.
+     * @param isExpanded whether the notification is expanded.
+     */
+    public void onNotificationExpansionChanged(
+            String key, boolean isUserAction, boolean isExpanded) {}
+
+    /**
+     * Implement this to know when a direct reply is sent from a notification.
+     * @param key the notification key
+     */
+    public void onNotificationDirectReply(String key) {}
+
+    /**
+     * Implement this to know when a suggested reply is sent.
+     * @param key the notification key
+     * @param reply the reply that is just sent
+     * @param source the source that provided the reply, e.g. SOURCE_FROM_APP
+     */
+    public void onSuggestedReplySent(String key, CharSequence reply, @Source int source) {}
+
+    /**
+     * Implement this to know when an action is clicked.
+     * @param key the notification key
+     * @param action the action that is just clicked
+     * @param source the source that provided the action, e.g. SOURCE_FROM_APP
+     */
+    public void onActionClicked(String key, @Nullable Notification.Action action, int source) {
+    }
+
+    /**
      * Updates a notification.  N.B. this won’t cause
      * an existing notification to alert, but might allow a future update to
      * this notification to alert.
@@ -255,12 +300,53 @@
             mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATIONS_SEEN,
                     args).sendToTarget();
         }
+
+        @Override
+        public void onNotificationExpansionChanged(String key, boolean isUserAction,
+                boolean isExpanded) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = key;
+            args.argi1 = isUserAction ? 1 : 0;
+            args.argi2 = isExpanded ? 1 : 0;
+            mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_EXPANSION_CHANGED, args)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void onNotificationDirectReply(String key) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = key;
+            mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT, args)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = key;
+            args.arg2 = reply;
+            args.argi2 = source;
+            mHandler.obtainMessage(MyHandler.MSG_ON_SUGGESTED_REPLY_SENT, args).sendToTarget();
+        }
+
+        @Override
+        public void onActionClicked(String key, Notification.Action action, int source) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = key;
+            args.arg2 = action;
+            args.argi2 = source;
+            mHandler.obtainMessage(MyHandler.MSG_ON_ACTION_CLICKED, args).sendToTarget();
+        }
     }
 
     private final class MyHandler extends Handler {
         public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
         public static final int MSG_ON_NOTIFICATION_SNOOZED = 2;
         public static final int MSG_ON_NOTIFICATIONS_SEEN = 3;
+        public static final int MSG_ON_NOTIFICATION_EXPANSION_CHANGED = 4;
+        public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5;
+        public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6;
+        public static final int MSG_ON_ACTION_CLICKED = 7;
 
         public MyHandler(Looper looper) {
             super(looper, null, false);
@@ -305,6 +391,40 @@
                     onNotificationsSeen(keys);
                     break;
                 }
+                case MSG_ON_NOTIFICATION_EXPANSION_CHANGED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    String key = (String) args.arg1;
+                    boolean isUserAction = args.argi1 == 1;
+                    boolean isExpanded = args.argi2 == 1;
+                    args.recycle();
+                    onNotificationExpansionChanged(key, isUserAction, isExpanded);
+                    break;
+                }
+                case MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    String key = (String) args.arg1;
+                    args.recycle();
+                    onNotificationDirectReply(key);
+                    break;
+                }
+                case MSG_ON_SUGGESTED_REPLY_SENT: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    String key = (String) args.arg1;
+                    CharSequence reply = (CharSequence) args.arg2;
+                    int source = args.argi2;
+                    args.recycle();
+                    onSuggestedReplySent(key, reply, source);
+                    break;
+                }
+                case MSG_ON_ACTION_CLICKED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    String key = (String) args.arg1;
+                    Notification.Action action = (Notification.Action) args.arg2;
+                    int source = args.argi2;
+                    args.recycle();
+                    onActionClicked(key, action, source);
+                    break;
+                }
             }
         }
     }
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 64eae0c..1fe97b7 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1366,6 +1366,27 @@
         }
 
         @Override
+        public void onNotificationExpansionChanged(
+                String key, boolean isUserAction, boolean isExpanded) {
+            // no-op in the listener
+        }
+
+        @Override
+        public void onNotificationDirectReply(String key) {
+            // no-op in the listener
+        }
+
+        @Override
+        public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+            // no-op in the listener
+        }
+
+        @Override
+        public void onActionClicked(String key, Notification.Action action, int source) {
+            // no-op in the listener
+        }
+
+        @Override
         public void onNotificationChannelModification(String pkgName, UserHandle user,
                 NotificationChannel channel,
                 @ChannelOrGroupModificationTypes int modificationType) {
diff --git a/core/java/android/service/sms/FinancialSmsService.java b/core/java/android/service/sms/FinancialSmsService.java
new file mode 100644
index 0000000..5fb7249
--- /dev/null
+++ b/core/java/android/service/sms/FinancialSmsService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.sms;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.database.CursorWindow;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+/**
+ * A service to support sms messages read for financial apps.
+ *
+ * {@hide}
+ */
+@SystemApi
+public abstract class FinancialSmsService extends Service {
+
+    private static final String TAG = "FinancialSmsService";
+
+    /**
+     * The {@link Intent} action that must be declared as handled by a service
+     * in its manifest for the system to recognize it as a quota providing
+     * service.
+     */
+    public static final String ACTION_FINANCIAL_SERVICE_INTENT =
+            "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+
+    /** {@hide} **/
+    public static final String EXTRA_SMS_MSGS = "sms_messages";
+
+    private FinancialSmsServiceWrapper mWrapper;
+
+    private void getSmsMessages(RemoteCallback callback, Bundle params) {
+        final Bundle data = new Bundle();
+        CursorWindow smsMessages = onGetSmsMessages(params);
+        if (smsMessages != null) {
+            data.putParcelable(EXTRA_SMS_MSGS, smsMessages);
+        }
+        callback.sendResult(data);
+    }
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+    /** @hide */
+    public FinancialSmsService() {
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mWrapper = new FinancialSmsServiceWrapper();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mWrapper;
+    }
+
+    /**
+     * Get sms messages for financial apps.
+     *
+     * @param params parameters passed in by the calling app.
+     * @return the {@code CursorWindow} with all sms messages for the app to read.
+     *
+     * {@hide}
+     */
+    @Nullable
+    @SystemApi
+    public abstract CursorWindow onGetSmsMessages(@NonNull Bundle params);
+
+    private final class FinancialSmsServiceWrapper extends IFinancialSmsService.Stub {
+        @Override
+        public void getSmsMessages(RemoteCallback callback, Bundle params) throws RemoteException {
+            mHandler.sendMessage(obtainMessage(
+                    FinancialSmsService::getSmsMessages,
+                    FinancialSmsService.this,
+                    callback, params));
+        }
+    }
+
+}
diff --git a/media/java/android/media/update/SessionToken2Provider.java b/core/java/android/service/sms/IFinancialSmsService.aidl
similarity index 63%
rename from media/java/android/media/update/SessionToken2Provider.java
rename to core/java/android/service/sms/IFinancialSmsService.aidl
index 95d6ce0..caabe58 100644
--- a/media/java/android/media/update/SessionToken2Provider.java
+++ b/core/java/android/service/sms/IFinancialSmsService.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,21 +14,17 @@
  * limitations under the License.
  */
 
-package android.media.update;
+package android.service.sms;
 
 import android.os.Bundle;
+import android.os.RemoteCallback;
 
 /**
+ * Service used by financial apps to read sms messages.
+ *
  * @hide
  */
-public interface SessionToken2Provider {
-    String getPackageName_impl();
-    String getId_imp();
-    int getType_impl();
-    int getUid_impl();
-    Bundle toBundle_impl();
-
-    int hashCode_impl();
-    boolean equals_impl(Object obj);
-    String toString_impl();
-}
+oneway interface IFinancialSmsService
+{
+    void getSmsMessages(in RemoteCallback callback, in Bundle params);
+}
\ No newline at end of file
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 6f51bec..45d53f3 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -56,6 +56,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
+import android.view.InsetsState;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -129,7 +130,7 @@
         Bundle extras;
         boolean sync;
     }
-    
+
     /**
      * The actual implementation of a wallpaper.  A wallpaper service may
      * have multiple instances running (for example as a real wallpaper
@@ -144,7 +145,7 @@
         HandlerCaller mCaller;
         IWallpaperConnection mConnection;
         IBinder mWindowToken;
-        
+
         boolean mInitializing = true;
         boolean mVisible;
         boolean mReportedVisible;
@@ -184,6 +185,7 @@
         final DisplayCutout.ParcelableWrapper mDisplayCutout =
                 new DisplayCutout.ParcelableWrapper();
         DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
+        final InsetsState mInsetsState = new InsetsState();
         final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
 
         final WindowManager.LayoutParams mLayout
@@ -208,7 +210,6 @@
         private final Supplier<Long> mClockFunction;
         private final Handler mHandler;
 
-        DisplayManager mDisplayManager;
         Display mDisplay;
         private int mDisplayState;
 
@@ -419,7 +420,7 @@
         public int getDesiredMinimumHeight() {
             return mIWallpaperEngine.mReqHeight;
         }
-        
+
         /**
          * Return whether the wallpaper is currently visible to the user,
          * this is the last value supplied to
@@ -804,9 +805,11 @@
                         mLayout.windowAnimations =
                                 com.android.internal.R.style.Animation_Wallpaper;
                         mInputChannel = new InputChannel();
+
                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
                                 mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
-                                mOutsets, mDisplayCutout, mInputChannel) < 0) {
+                                mOutsets, mDisplayCutout, mInputChannel,
+                                mInsetsState) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
@@ -832,7 +835,8 @@
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
-                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface);
+                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface,
+                            mInsetsState);
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
@@ -1015,14 +1019,6 @@
                 return;
             }
 
-            mDisplayManager = getSystemService(DisplayManager.class);
-            mDisplay = mDisplayManager.getDisplay(wrapper.mDisplayId);
-            if (mDisplay == null) {
-                // TODO(b/115486823) Ignore this engine.
-                Log.e(TAG, "Attaching to a non-existent display: " + wrapper.mDisplayId);
-                return;
-            }
-
             mIWallpaperEngine = wrapper;
             mCaller = wrapper.mCaller;
             mConnection = wrapper.mConnection;
@@ -1034,13 +1030,16 @@
             mWindow.setSession(mSession);
 
             mLayout.packageName = getPackageName();
-            mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
+            mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
+                    mCaller.getHandler());
+            mDisplay = mIWallpaperEngine.mDisplay;
             mDisplayState = mDisplay.getState();
 
             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
             onCreate(mSurfaceHolder);
-            
+
             mInitializing = false;
+
             mReportedVisible = false;
             updateSurface(false, false, false);
         }
@@ -1202,8 +1201,8 @@
             
             mDestroyed = true;
 
-            if (mDisplayManager != null) {
-                mDisplayManager.unregisterDisplayListener(mDisplayListener);
+            if (mIWallpaperEngine.mDisplayManager != null) {
+                mIWallpaperEngine.mDisplayManager.unregisterDisplayListener(mDisplayListener);
             }
 
             if (mVisible) {
@@ -1272,7 +1271,9 @@
         int mReqWidth;
         int mReqHeight;
         final Rect mDisplayPadding = new Rect();
-        int mDisplayId;
+        final int mDisplayId;
+        final DisplayManager mDisplayManager;
+        final Display mDisplay;
 
         Engine mEngine;
 
@@ -1289,7 +1290,15 @@
             mReqHeight = reqHeight;
             mDisplayPadding.set(padding);
             mDisplayId = displayId;
-            
+
+            // Create a display context before onCreateEngine.
+            mDisplayManager = getSystemService(DisplayManager.class);
+            mDisplay = mDisplayManager.getDisplay(mDisplayId);
+
+            if (mDisplay == null) {
+                // Ignore this engine.
+                throw new IllegalArgumentException("Cannot find display with id" + mDisplayId);
+            }
             Message msg = mCaller.obtainMessage(DO_ATTACH);
             mCaller.sendMessage(msg);
         }
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index ec63cd9..7815864 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -1009,7 +1009,7 @@
                 Log.e(TAG, "null synthesis text");
                 return false;
             }
-            if (mText.length() >= TextToSpeech.getMaxSpeechInputLength()) {
+            if (mText.length() > TextToSpeech.getMaxSpeechInputLength()) {
                 Log.w(TAG, "Text too long: " + mText.length() + " chars");
                 return false;
             }
@@ -1609,7 +1609,7 @@
             synchronized (mCallerToCallback) {
                 mCallerToCallback.remove(caller);
             }
-            //mSynthHandler.stopForApp(caller);
+            mSynthHandler.stopForApp(caller);
         }
 
         @Override
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 2cf0262..8cb18b2 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -485,7 +485,7 @@
      * @deprecated Use {@link Builder} instead.
      */
     @Deprecated
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521430)
     public StaticLayout(CharSequence source, int bufstart, int bufend,
                         TextPaint paint, int outerwidth,
                         Alignment align, TextDirectionHeuristic textDir,
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index b970c25..85b6b88 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -46,6 +46,7 @@
 import android.text.style.ForegroundColorSpan;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LineBackgroundSpan;
+import android.text.style.LineHeightSpan;
 import android.text.style.LocaleSpan;
 import android.text.style.ParagraphStyle;
 import android.text.style.QuoteSpan;
@@ -733,7 +734,9 @@
     /** @hide */
     public static final int LINE_BACKGROUND_SPAN = 27;
     /** @hide */
-    public static final int LAST_SPAN = LINE_BACKGROUND_SPAN;
+    public static final int LINE_HEIGHT_SPAN = 28;
+    /** @hide */
+    public static final int LAST_SPAN = LINE_HEIGHT_SPAN;
 
     /**
      * Flatten a CharSequence and whatever styles can be copied across processes
@@ -928,6 +931,10 @@
                     readSpan(p, sp, new LineBackgroundSpan.Standard(p));
                     break;
 
+                case LINE_HEIGHT_SPAN:
+                    readSpan(p, sp, new LineHeightSpan.Standard(p));
+                    break;
+                    
                 default:
                     throw new RuntimeException("bogus span encoding " + kind);
                 }
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 562ae7a..bab4bc3 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -18,8 +18,8 @@
 
 import android.util.TimeFormatException;
 
+import libcore.timezone.ZoneInfoDB;
 import libcore.util.ZoneInfo;
-import libcore.util.ZoneInfoDB;
 
 import java.io.IOException;
 import java.util.Locale;
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 2742ae0..a5d5af2 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -20,7 +20,10 @@
 import android.annotation.NonNull;
 import android.annotation.Px;
 import android.graphics.Paint;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
 import android.text.TextPaint;
+import android.text.TextUtils;
 
 import com.android.internal.util.Preconditions;
 
@@ -71,7 +74,7 @@
      * covers only part of the paragraph.
      * </p>
      */
-    class Standard implements LineHeightSpan {
+    class Standard implements LineHeightSpan, ParcelableSpan {
 
         private final @Px int mHeight;
         /**
@@ -82,6 +85,48 @@
             mHeight = height;
         }
 
+        /**
+         * Constructor called from {@link TextUtils} to restore the span from a parcel
+         */
+        public Standard(Parcel src) {
+            mHeight = src.readInt();
+        }
+
+        /**
+         * Returns the line height specified by this span.
+         */
+        @Px
+        public int getHeight() {
+            return mHeight;
+        }
+
+        @Override
+        public int getSpanTypeId() {
+            return getSpanTypeIdInternal();
+        }
+
+        /** @hide */
+        @Override
+        public int getSpanTypeIdInternal() {
+            return TextUtils.LINE_HEIGHT_SPAN;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            writeToParcelInternal(dest, flags);
+        }
+
+        /** @hide */
+        @Override
+        public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mHeight);
+        }
+
         @Override
         public void chooseHeight(@NonNull CharSequence text, int start, int end,
                 int spanstartv, int lineHeight,
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index f4dad62..eef7ea2 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -29,6 +29,7 @@
 import android.text.method.LinkMovementMethod;
 import android.text.method.MovementMethod;
 import android.text.style.URLSpan;
+import android.util.Log;
 import android.util.Patterns;
 import android.webkit.WebView;
 import android.widget.TextView;
@@ -72,6 +73,9 @@
  */
 
 public class Linkify {
+
+    private static final String LOG_TAG = "Linkify";
+
     /**
      *  Bit field indicating that web URLs should be matched in methods that
      *  take an options mask
@@ -310,6 +314,11 @@
      */
     private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
             @Nullable Context context, @Nullable UrlSpanFactory urlSpanFactory) {
+        if (text != null && containsUnsupportedCharacters(text.toString())) {
+            android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+            return false;
+        }
+
         if (mask == 0) {
             return false;
         }
@@ -356,6 +365,29 @@
     }
 
     /**
+     * Returns true if the specified text contains at least one unsupported character for applying
+     * links. Also logs the error.
+     *
+     * @param text the text to apply links to
+     * @hide
+     */
+    public static boolean containsUnsupportedCharacters(String text) {
+        if (text.contains("\u202C")) {
+            Log.e(LOG_TAG, "Unsupported character for applying links: u202C");
+            return true;
+        }
+        if (text.contains("\u202D")) {
+            Log.e(LOG_TAG, "Unsupported character for applying links: u202D");
+            return true;
+        }
+        if (text.contains("\u202E")) {
+            Log.e(LOG_TAG, "Unsupported character for applying links: u202E");
+            return true;
+        }
+        return false;
+    }
+
+    /**
      *  Scans the text of the provided TextView and turns all occurrences of
      *  the link types indicated in the mask into clickable links.  If matches
      *  are found the movement method for the TextView is set to
@@ -560,6 +592,11 @@
             @Nullable String defaultScheme, @Nullable String[] schemes,
             @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter,
             @Nullable UrlSpanFactory urlSpanFactory) {
+        if (spannable != null && containsUnsupportedCharacters(spannable.toString())) {
+            android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+            return false;
+        }
+
         final String[] schemesCopy;
         if (defaultScheme == null) defaultScheme = "";
         if (schemes == null || schemes.length < 1) {
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index c822832..de182da 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -32,6 +32,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.util.Property;
 import android.view.View;
@@ -109,7 +110,7 @@
                 }
             };
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private static final Property<View, PointF> BOTTOM_RIGHT_ONLY_PROPERTY =
             new Property<View, PointF>(PointF.class, "bottomRight") {
                 @Override
@@ -144,7 +145,7 @@
                 }
             };
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private static final Property<View, PointF> POSITION_PROPERTY =
             new Property<View, PointF>(PointF.class, "position") {
                 @Override
diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java
index 7e499f2..b1fc17a 100644
--- a/core/java/android/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.os.Build;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -38,9 +39,9 @@
     private int mLayoutId = -1;
     private ViewGroup mSceneRoot;
     private View mLayout; // alternative to layoutId
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     Runnable mEnterAction;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     Runnable mExitAction;
 
     /**
@@ -200,7 +201,7 @@
      *
      * @param sceneRoot The view on which the current scene is being set
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     static void setCurrentScene(@NonNull View sceneRoot, @Nullable Scene scene) {
         sceneRoot.setTagInternal(com.android.internal.R.id.current_scene, scene);
     }
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 21c4252..57d55bf 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -24,7 +24,7 @@
 import libcore.timezone.CountryTimeZones;
 import libcore.timezone.CountryTimeZones.TimeZoneMapping;
 import libcore.timezone.TimeZoneFinder;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
 
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 4b8b7f3..af41b69 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -24,6 +24,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.DisplayCutout;
+import android.view.InsetsState;
 
 import com.android.internal.os.IResultReceiver;
 import android.util.MergedConfiguration;
@@ -53,6 +54,12 @@
             in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
             boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
             in DisplayCutout.ParcelableWrapper displayCutout);
+
+    /**
+     * Called when the window insets configuration has changed.
+     */
+    void insetsChanged(in InsetsState insetsState);
+
     void moved(int newX, int newY);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 308a000..dace388 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -96,8 +96,9 @@
      */
     void overridePendingAppTransitionMultiThumbFuture(
             IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
-            boolean scaleUp);
-    void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter);
+            boolean scaleUp, int displayId);
+    void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter,
+            int displayId);
     void executeAppTransition();
 
     /**
@@ -294,9 +295,11 @@
     void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled);
 
     /**
-     * Device has a software navigation bar (separate from the status bar).
+     * Device has a software navigation bar (separate from the status bar) on specific display.
+     *
+     * @param displayId the id of display to check if there is a software navigation bar.
      */
-    boolean hasNavigationBar();
+    boolean hasNavigationBar(int displayId);
 
     /**
      * Get the position of the nav bar
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index bedfa9f..9762586 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -28,6 +28,7 @@
 import android.view.IWindowId;
 import android.view.MotionEvent;
 import android.view.WindowManager;
+import android.view.InsetsState;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
@@ -40,10 +41,11 @@
     int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outFrame,
             out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets,
-            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel);
+            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
+            out InsetsState insetsState);
     int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outContentInsets,
-            out Rect outStableInsets);
+            out Rect outStableInsets, out InsetsState insetsState);
     void remove(IWindow window);
 
     /**
@@ -86,6 +88,7 @@
      * config for window, if it is now becoming visible and the merged configuration has changed
      * since it was last displayed.
      * @param outSurface Object in which is placed the new display surface.
+     * @param insetsState The current insets state in the system.
      *
      * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
      * {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
@@ -96,7 +99,8 @@
             out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
             out Rect outOutsets, out Rect outBackdropFrame,
             out DisplayCutout.ParcelableWrapper displayCutout,
-            out MergedConfiguration outMergedConfiguration, out Surface outSurface);
+            out MergedConfiguration outMergedConfiguration, out Surface outSurface,
+            out InsetsState insetsState);
 
     /*
      * Notify the window manager that an application is relaunching and
diff --git a/core/java/android/view/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java
index dc1e505..5f6bc23 100644
--- a/core/java/android/view/InputApplicationHandle.java
+++ b/core/java/android/view/InputApplicationHandle.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.os.IBinder;
+
 /**
  * Functions as a handle for an application that can receive input.
  * Enables the native input dispatcher to refer indirectly to the window manager's
@@ -28,19 +30,18 @@
     @SuppressWarnings("unused")
     private long ptr;
 
-    // The window manager's application window token.
-    public final Object appWindowToken;
-
     // Application name.
     public String name;
 
     // Dispatching timeout.
     public long dispatchingTimeoutNanos;
 
+    public IBinder token;
+
     private native void nativeDispose();
 
-    public InputApplicationHandle(Object appWindowToken) {
-        this.appWindowToken = appWindowToken;
+    public InputApplicationHandle(IBinder token) {
+        this.token = token;
     }
 
     @Override
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 621ee89..92e0009 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.graphics.Region;
+import android.os.IBinder;
 import android.view.IWindow;
 import android.view.InputChannel;
 
@@ -37,8 +38,8 @@
     // The client window.
     public final IWindow clientWindow;
 
-    // The input channel associated with the window.
-    public InputChannel inputChannel;
+    // The token assosciated with the window.
+    public IBinder token;
 
     // The window name.
     public String name;
@@ -56,6 +57,8 @@
     public int frameRight;
     public int frameBottom;
 
+    public int surfaceInset;
+
     // Global scaling factor applied to touch events when they are dispatched
     // to the window
     public float scaleFactor;
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
new file mode 100644
index 0000000..7841d04
--- /dev/null
+++ b/core/java/android/view/InsetsController.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Rect;
+
+import java.io.PrintWriter;
+
+/**
+ * Implements {@link WindowInsetsController} on the client.
+ */
+class InsetsController {
+
+    private final InsetsState mState = new InsetsState();
+    private final Rect mFrame = new Rect();
+
+    void onFrameChanged(Rect frame) {
+        mFrame.set(frame);
+    }
+
+    public InsetsState getState() {
+        return mState;
+    }
+
+    public void setState(InsetsState state) {
+        mState.set(state);
+    }
+
+    /**
+     * @see InsetsState#calculateInsets
+     */
+    WindowInsets calculateInsets(boolean isScreenRound,
+            boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+        return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout);
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix); pw.println("InsetsController:");
+        mState.dump(prefix + "  ", pw);
+    }
+}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
new file mode 100644
index 0000000..0cb8ad7
--- /dev/null
+++ b/core/java/android/view/InsetsSource.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InsetsState.InternalInsetType;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents the state of a single window generating insets for clients.
+ * @hide
+ */
+public class InsetsSource implements Parcelable {
+
+    private final @InternalInsetType int mType;
+
+    /** Frame of the source in screen coordinate space */
+    private final Rect mFrame;
+    private boolean mVisible;
+
+    private final Rect mTmpFrame = new Rect();
+
+    public InsetsSource(@InternalInsetType int type) {
+        mType = type;
+        mFrame = new Rect();
+    }
+
+    public InsetsSource(InsetsSource other) {
+        mType = other.mType;
+        mFrame = new Rect(other.mFrame);
+        mVisible = other.mVisible;
+    }
+
+    public void setFrame(Rect frame) {
+        mFrame.set(frame);
+    }
+
+    public void setVisible(boolean visible) {
+        mVisible = visible;
+    }
+
+    public @InternalInsetType int getType() {
+        return mType;
+    }
+
+    public Rect getFrame() {
+        return mFrame;
+    }
+
+    /**
+     * Calculates the insets this source will cause to a client window.
+     *
+     * @param relativeFrame The frame to calculate the insets relative to.
+     * @param ignoreVisibility If true, always reports back insets even if source isn't visible.
+     * @return The resulting insets.
+     */
+    public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
+        if (!ignoreVisibility && !mVisible) {
+            return Insets.NONE;
+        }
+        if (!mTmpFrame.setIntersect(mFrame, relativeFrame)) {
+            return Insets.NONE;
+        }
+
+        // Intersecting at top/bottom
+        if (mTmpFrame.width() == relativeFrame.width()) {
+            if (mTmpFrame.top == relativeFrame.top) {
+                return Insets.of(0, mTmpFrame.height(), 0, 0);
+            } else {
+                return Insets.of(0, 0, 0, mTmpFrame.height());
+            }
+        }
+        // Intersecting at left/right
+        else if (mTmpFrame.height() == relativeFrame.height()) {
+            if (mTmpFrame.left == relativeFrame.left) {
+                return Insets.of(mTmpFrame.width(), 0, 0, 0);
+            } else {
+                return Insets.of(0, 0, mTmpFrame.width(), 0);
+            }
+        } else {
+            return Insets.NONE;
+        }
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix);
+        pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
+        pw.print(" frame="); pw.print(mFrame.toShortString());
+        pw.print(" visible="); pw.print(mVisible);
+        pw.println();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        InsetsSource that = (InsetsSource) o;
+
+        if (mType != that.mType) return false;
+        if (mVisible != that.mVisible) return false;
+        return mFrame.equals(that.mFrame);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mType;
+        result = 31 * result + mFrame.hashCode();
+        result = 31 * result + (mVisible ? 1 : 0);
+        return result;
+    }
+
+    public InsetsSource(Parcel in) {
+        mType = in.readInt();
+        mFrame = in.readParcelable(null /* loader */);
+        mVisible = in.readBoolean();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeParcelable(mFrame, 0 /* flags*/);
+        dest.writeBoolean(mVisible);
+    }
+
+    public static final Creator<InsetsSource> CREATOR = new Creator<InsetsSource>() {
+
+        public InsetsSource createFromParcel(Parcel in) {
+            return new InsetsSource(in);
+        }
+
+        public InsetsSource[] newArray(int size) {
+            return new InsetsSource[size];
+        }
+    };
+}
diff --git a/core/java/android/view/InsetsState.aidl b/core/java/android/view/InsetsState.aidl
new file mode 100644
index 0000000..d02ddd1
--- /dev/null
+++ b/core/java/android/view/InsetsState.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+parcelable InsetsState;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
new file mode 100644
index 0000000..9895adc
--- /dev/null
+++ b/core/java/android/view/InsetsState.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Holder for state of system windows that cause window insets for all other windows in the system.
+ * @hide
+ */
+public class InsetsState implements Parcelable {
+
+    /**
+     * Internal representation of inset source types. This is different from the public API in
+     * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
+     * at the same time.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "TYPE", value = {
+            TYPE_TOP_BAR,
+            TYPE_SIDE_BAR_1,
+            TYPE_SIDE_BAR_2,
+            TYPE_SIDE_BAR_3,
+            TYPE_IME
+    })
+    public @interface InternalInsetType {}
+
+    static final int FIRST_TYPE = 0;
+
+    /** Top bar. Can be status bar or caption in freeform windowing mode. */
+    public static final int TYPE_TOP_BAR = FIRST_TYPE;
+
+    /**
+     * Up to 3 side bars that appear on left/right/bottom. On phones there is only one side bar
+     * (the navigation bar, see {@link #TYPE_NAVIGATION_BAR}), but other form factors might have
+     * multiple, like Android Auto.
+     */
+    public static final int TYPE_SIDE_BAR_1 = 1;
+    public static final int TYPE_SIDE_BAR_2 = 2;
+    public static final int TYPE_SIDE_BAR_3 = 3;
+
+    /** Input method window. */
+    public static final int TYPE_IME = 4;
+    static final int LAST_TYPE = TYPE_IME;
+
+    // Derived types
+
+    /** First side bar is navigation bar. */
+    public static final int TYPE_NAVIGATION_BAR = TYPE_SIDE_BAR_1;
+
+    /** A shelf is the same as the navigation bar. */
+    public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR;
+
+    private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>();
+
+    public InsetsState() {
+    }
+
+    /**
+     * Calculates {@link WindowInsets} based on the current source configuration.
+     *
+     * @param frame The frame to calculate the insets relative to.
+     * @return The calculated insets.
+     */
+    public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
+            boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+        Insets systemInsets = Insets.NONE;
+        Insets maxInsets = Insets.NONE;
+        final Rect relativeFrame = new Rect(frame);
+        final Rect relativeFrameMax = new Rect(frame);
+        for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+            InsetsSource source = mSources.get(type);
+            if (source == null) {
+                continue;
+            }
+            systemInsets = processSource(source, systemInsets, relativeFrame,
+                    false /* ignoreVisibility */);
+
+            // IME won't be reported in max insets as the size depends on the EditorInfo of the IME
+            // target.
+            if (source.getType() != TYPE_IME) {
+                maxInsets = processSource(source, maxInsets, relativeFrameMax,
+                        true /* ignoreVisibility */);
+            }
+        }
+        return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
+                alwaysConsumeNavBar, cutout);
+    }
+
+    private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
+            boolean ignoreVisibility) {
+        Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
+        insets = Insets.add(currentInsets, insets);
+        relativeFrame.inset(insets);
+        return insets;
+    }
+
+    public InsetsSource getSource(@InternalInsetType int type) {
+        return mSources.computeIfAbsent(type, InsetsSource::new);
+    }
+
+    /**
+     * Modifies the state of this class to exclude a certain type to make it ready for dispatching
+     * to the client.
+     *
+     * @param type The {@link InternalInsetType} of the source to remove
+     */
+    public void removeSource(int type) {
+        mSources.remove(type);
+    }
+
+    public void set(InsetsState other) {
+        set(other, false /* copySources */);
+    }
+
+    public void set(InsetsState other, boolean copySources) {
+        mSources.clear();
+        if (copySources) {
+            for (int i = 0; i < other.mSources.size(); i++) {
+                InsetsSource source = other.mSources.valueAt(i);
+                mSources.put(source.getType(), new InsetsSource(source));
+            }
+        } else {
+            mSources.putAll(other.mSources);
+        }
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "InsetsState");
+        for (int i = mSources.size() - 1; i >= 0; i--) {
+            mSources.valueAt(i).dump(prefix + "  ", pw);
+        }
+    }
+
+    static String typeToString(int type) {
+        switch (type) {
+            case TYPE_TOP_BAR:
+                return "TYPE_TOP_BAR";
+            case TYPE_SIDE_BAR_1:
+                return "TYPE_SIDE_BAR_1";
+            case TYPE_SIDE_BAR_2:
+                return "TYPE_SIDE_BAR_2";
+            case TYPE_SIDE_BAR_3:
+                return "TYPE_SIDE_BAR_3";
+            case TYPE_IME:
+                return "TYPE_IME";
+            default:
+                return "TYPE_UNKNOWN";
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) { return true; }
+        if (o == null || getClass() != o.getClass()) { return false; }
+
+        InsetsState state = (InsetsState) o;
+
+        if (mSources.size() != state.mSources.size()) {
+            return false;
+        }
+        for (int i = mSources.size() - 1; i >= 0; i--) {
+            InsetsSource source = mSources.valueAt(i);
+            InsetsSource otherSource = state.mSources.get(source.getType());
+            if (otherSource == null) {
+                return false;
+            }
+            if (!otherSource.equals(source)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return mSources.hashCode();
+    }
+
+    public InsetsState(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSources.size());
+        for (int i = 0; i < mSources.size(); i++) {
+            dest.writeParcelable(mSources.valueAt(i), 0 /* flags */);
+        }
+    }
+
+    public static final Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
+
+        public InsetsState createFromParcel(Parcel in) {
+            return new InsetsState(in);
+        }
+
+        public InsetsState[] newArray(int size) {
+            return new InsetsState[size];
+        }
+    };
+
+    public void readFromParcel(Parcel in) {
+        mSources.clear();
+        final int size = in.readInt();
+        for (int i = 0; i < size; i++) {
+            final InsetsSource source = in.readParcelable(null /* loader */);
+            mSources.put(source.getType(), source);
+        }
+    }
+}
+
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a7a5024..46f396a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -375,9 +375,13 @@
          * Construct a new {@link SurfaceControl} with the set parameters.
          */
         public SurfaceControl build() {
-            if (mWidth <= 0 || mHeight <= 0) {
+            if (mWidth < 0 || mHeight < 0) {
                 throw new IllegalArgumentException(
-                        "width and height must be set");
+                        "width and height must be positive or unset");
+            }
+            if ((mWidth > 0 || mHeight > 0) && (isColorLayerSet() || isContainerLayerSet())) {
+                throw new IllegalArgumentException(
+                        "Only buffer layers can set a valid buffer size.");
             }
             return new SurfaceControl(mSession, mName, mWidth, mHeight, mFormat,
                     mFlags, mParent, mWindowType, mOwnerUid);
@@ -399,8 +403,8 @@
          * @param width The buffer width in pixels.
          * @param height The buffer height in pixels.
          */
-        public Builder setSize(int width, int height) {
-            if (width <= 0 || height <= 0) {
+        public Builder setBufferSize(int width, int height) {
+            if (width < 0 || height < 0) {
                 throw new IllegalArgumentException(
                         "width and height must be positive");
             }
@@ -533,6 +537,10 @@
             return this;
         }
 
+        private boolean isColorLayerSet() {
+            return  (mFlags & FX_SURFACE_DIM) == FX_SURFACE_DIM;
+        }
+
         /**
          * Indicates whether a 'ContainerLayer' is to be constructed.
          *
@@ -550,6 +558,10 @@
             return this;
         }
 
+        private boolean isContainerLayerSet() {
+            return  (mFlags & FX_SURFACE_CONTAINER) == FX_SURFACE_CONTAINER;
+        }
+
         /**
          * Set 'Surface creation flags' such as {@link HIDDEN}, {@link SECURE}.
          *
@@ -869,10 +881,10 @@
         }
     }
 
-    public void setSize(int w, int h) {
+    public void setBufferSize(int w, int h) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
-            sGlobalTransaction.setSize(this, w, h);
+            sGlobalTransaction.setBufferSize(this, w, h);
         }
     }
 
@@ -1427,7 +1439,7 @@
         }
 
         @UnsupportedAppUsage
-        public Transaction setSize(SurfaceControl sc, int w, int h) {
+        public Transaction setBufferSize(SurfaceControl sc, int w, int h) {
             sc.checkNotReleased();
             mResizedSurfaces.put(sc, new Point(w, h));
             nativeSetSize(mNativeObject, sc.mNativeObject, w, h);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 2b68ec0..3c4ce8f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -557,7 +557,7 @@
                             name,
                             (mSurfaceFlags & SurfaceControl.OPAQUE) != 0,
                             new SurfaceControl.Builder(mSurfaceSession)
-                                    .setSize(mSurfaceWidth, mSurfaceHeight)
+                                    .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                                     .setFormat(mFormat)
                                     .setFlags(mSurfaceFlags));
                 } else if (mSurfaceControl == null) {
@@ -595,10 +595,14 @@
                             mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
                                     0.0f, 0.0f,
                                     mScreenRect.height() / (float) mSurfaceHeight);
+                            // Set a window crop when creating the surface or changing its size to
+                            // crop the buffer to the surface size since the buffer producer may
+                            // use SCALING_MODE_SCALE and submit a larger size than the surface
+                            // size.
+                            mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
                         }
                         if (sizeChanged && !creating) {
-                            mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
-                            mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
+                            mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight);
                         }
                     } finally {
                         SurfaceControl.closeTransaction();
@@ -1133,6 +1137,8 @@
 
             mBackgroundControl = b.setName("Background for -" + name)
                     .setFormat(OPAQUE)
+                    // Unset the buffer size of the background color layer.
+                    .setBufferSize(0, 0)
                     .setColorLayer(true)
                     .build();
             mOpaque = opaque;
@@ -1158,9 +1164,9 @@
         }
 
         @Override
-        public void setSize(int w, int h) {
-            super.setSize(w, h);
-            mBackgroundControl.setSize(w, h);
+        public void setBufferSize(int w, int h) {
+            super.setBufferSize(w, h);
+            // The background surface is a color layer so we do not set a size.
         }
 
         @Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5f1336f..2767505 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3373,9 +3373,8 @@
      *
      * |-------|-------|-------|-------|
      *                              1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
-     *                             1     PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT
-     *                            1      PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED
-     *                           1       PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE
+     *                             1     PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
+     *                            1      PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
      * |-------|-------|-------|-------|
      */
 
@@ -3396,28 +3395,14 @@
      * Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods
      * should be called.
      *
-     * The idea is to call notifyNodeAdded() after the view is layout and visible, then call
-     * notifyNodeRemoved() when it's gone (without known when it was removed from the parent).
-     *
-     * TODO(b/111276913): the current algortighm could probably be optimized and some of them
-     * removed
+     * The idea is to call notifyAppeared() after the view is layout and visible, then call
+     * notifyDisappeared() when it's gone (without known when it was removed from the parent).
      */
-    private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT = 0x10;
-    private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED = 0x20;
-    private static final int PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE = 0x40;
+    private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10;
+    private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20;
 
     /* End of masks for mPrivateFlags4 */
 
-    private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED = 1;
-    private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED = 0;
-
-    @IntDef(flag = true, prefix = { "CONTENT_CAPTURE_NOTIFICATION_TYPE_" }, value = {
-            CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED,
-            CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface ContentCaptureNotificationType {}
-
     /** @hide */
     protected static final int VIEW_STRUCTURE_FOR_ASSIST = 0;
     /** @hide */
@@ -3993,7 +3978,7 @@
      *
      * @see #getParent()
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected ViewParent mParent;
 
     /**
@@ -4199,7 +4184,7 @@
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "layout")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected int mLeft;
     /**
      * The distance in pixels from the left edge of this view's parent
@@ -4207,7 +4192,7 @@
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "layout")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected int mRight;
     /**
      * The distance in pixels from the top edge of this view's parent
@@ -4215,7 +4200,7 @@
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "layout")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected int mTop;
     /**
      * The distance in pixels from the top edge of this view's parent
@@ -4223,7 +4208,7 @@
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "layout")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected int mBottom;
 
     /**
@@ -4714,7 +4699,7 @@
      * of this view to at least this amount.
      */
     @ViewDebug.ExportedProperty(category = "measurement")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mMinHeight;
 
     /**
@@ -4722,7 +4707,7 @@
      * of this view to at least this amount.
      */
     @ViewDebug.ExportedProperty(category = "measurement")
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mMinWidth;
 
     /**
@@ -8152,6 +8137,9 @@
      * the user, and the activity rendering the view is enabled for Content Capture) is laid out and
      * is visible.
      *
+     * <p>The populated structure is then passed to the service through
+     * {@link IntelligenceManager#notifyViewAppeared(ViewStructure)}.
+     *
      * <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
      * <ul>
      *   <li>{@link ViewStructure#setChildCount(int)}
@@ -8165,16 +8153,9 @@
      *   <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}
      *   <li>{@link ViewStructure#setDataIsSensitive(boolean)}
      * </ul>
-     *
-     * @return whether the IntelligenceService should be notified that the view was added (through
-     * the {@link IntelligenceManager#notifyViewAppeared(ViewStructure)} method) to the view
-     * hierarchy. Most views should return {@code true} here, but views that contains virtual
-     * hierarchy might opt to return {@code false} and notify the manager independently, as the
-     * virtual views are rendered.
      */
-    public boolean onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
+    public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
         onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
-        return true;
     }
 
     /** @hide */
@@ -8937,65 +8918,61 @@
      * Helper used to notify the {@link IntelligenceManager} when the view is removed or
      * added, based on whether it's laid out and visible, and without knowing if the parent removed
      * it from the view hierarchy.
+     *
+     * <p>This method is called from many places (visibility changed, view laid out, view attached
+     * or detached to/from window, etc...) and hence must contain the logic to call the manager, as
+     * described below:
+     *
+     * <ol>
+     *   <li>It should only be called when content capture is enabled for the view.
+     *   <li>It must call viewAppeared() before viewDisappeared()
+     *   <li>viewAppearead() can only be called when the view is visible and laidout
+     *   <li>It should not call the same event twice.
+     * </ol>
      */
-    // TODO(b/111276913): make sure the current algorithm covers all cases. For example, it should
-    // probably be called every time notifyEnterOrExitForAutoFillIfNeeded() is called as well.
-    private void notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
-            @ContentCaptureNotificationType int type) {
-        if (type != CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
-                && type != CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
-            // Sanity check so it does not screw up the flags
-            Log.wtf(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded(): "
-                    + "invalid type " + type + " for " + this);
-            return;
-        }
-
-        if (!isImportantForContentCapture()) return;
+    private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
 
         final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
         if (im == null || !im.isContentCaptureEnabled()) return;
 
-        // Make sure event is notified just once, and reset the
-        // PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE flag
-        boolean ignoreNotification = false;
-        if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
-            if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
-                    == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
-                ignoreNotification = true;
-            } else {
-                mPrivateFlags4 |= PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
-            }
-        } else {
-            if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
-                    == CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
-                ignoreNotification = true;
-            } else {
-                mPrivateFlags4 &= ~PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
-            }
-        }
-        if (ignoreNotification) {
-            if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
-                // TODO(b/111276913): remove this log statement if the algorithm is not improved
-                // (right now it's called too many times when the activity is stopped and/or views
-                // disappear
-                Log.v(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded("
-                        + type + "): ignoring repeated notification on " + this);
-            }
-            return;
-        }
+        // NOTE: isImportantForContentCapture() is more expensive than im.isContentCaptureEnabled()
+        if (!isImportantForContentCapture()) return;
 
-        if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
+        if (appeared) {
+            if (!isLaidOut() || !isVisibleToUser()
+                    || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) {
+                if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+                    Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid="
+                            + isLaidOut() + ", visible=" + isVisibleToUser()
+                            + ": alreadyNotifiedAppeared="
+                            + ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0));
+                }
+                return;
+            }
+            // All good: notify the manager...
             final ViewStructure structure = im.newViewStructure(this);
-            boolean notifyMgr = onProvideContentCaptureStructure(structure, /* flags= */ 0);
-            if (notifyMgr) {
-                im.notifyViewAppeared(structure);
-            }
-            mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED;
+            onProvideContentCaptureStructure(structure, /* flags= */ 0);
+            im.notifyViewAppeared(structure);
+            // ...and set the flags
+            mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
+            mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
         } else {
-            if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED) == 0) {
-                return; // skip initial notification
+            if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
+                    || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
+                if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+                    Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this
+                            + ": notifiedAppeared="
+                            + ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0)
+                            + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4
+                                    & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0));
+                }
+                return;
             }
+            // All good: notify the manager...
             im.notifyViewDisappeared(getAutofillId());
+            // ...and set the flags
+            mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
+            mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
         }
     }
 
@@ -12902,6 +12879,7 @@
     public void dispatchStartTemporaryDetach() {
         mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH;
         notifyEnterOrExitForAutoFillIfNeeded(false);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
         onStartTemporaryDetach();
     }
 
@@ -12928,6 +12906,7 @@
             notifyFocusChangeToInputMethodManager(true /* hasFocus */);
         }
         notifyEnterOrExitForAutoFillIfNeeded(true);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
     }
 
     /**
@@ -13509,9 +13488,8 @@
                         : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
             }
         }
-        notifyNodeAddedOrRemovedForContentCaptureIfNeeded(isVisible
-                ? CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
-                : CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
+
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
     }
 
     /**
@@ -19082,6 +19060,7 @@
         needGlobalAttributesUpdate(false);
 
         notifyEnterOrExitForAutoFillIfNeeded(true);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
     }
 
     @UnsupportedAppUsage
@@ -19131,8 +19110,7 @@
         }
 
         notifyEnterOrExitForAutoFillIfNeeded(false);
-        notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
-                CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
     }
 
     /**
@@ -21438,12 +21416,7 @@
             notifyEnterOrExitForAutoFillIfNeeded(true);
         }
 
-        if ((mViewFlags & VISIBILITY_MASK) == VISIBLE
-                && (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT) == 0) {
-            notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
-                    CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED);
-            mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT;
-        }
+        notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
     }
 
     private boolean hasParentWantsFocus() {
@@ -21552,10 +21525,18 @@
     }
 
     /**
-     * Same as setFrame, but public and hidden. For use in {@link android.transition.ChangeBounds}.
-     * @hide
+     * Assign a size and position to this view.
+     *
+     * This method is meant to be used in animations only as it applies this position and size
+     * for the view only temporary and it can be changed back at any time by the layout.
+     *
+     * @param left Left position, relative to parent
+     * @param top Top position, relative to parent
+     * @param right Right position, relative to parent
+     * @param bottom Bottom position, relative to parent
+     *
+     * @see #setLeft(int), #setRight(int), #setTop(int), #setBottom(int)
      */
-    @UnsupportedAppUsage
     public void setLeftTopRightBottom(int left, int top, int right, int bottom) {
         setFrame(left, top, right, bottom);
     }
@@ -23157,26 +23138,24 @@
      * Modifies the input matrix such that it maps view-local coordinates to
      * on-screen coordinates.
      *
-     * @param m input matrix to modify
-     * @hide
+     * @param matrix input matrix to modify
      */
-    @UnsupportedAppUsage
-    public void transformMatrixToGlobal(Matrix m) {
+    public void transformMatrixToGlobal(Matrix matrix) {
         final ViewParent parent = mParent;
         if (parent instanceof View) {
             final View vp = (View) parent;
-            vp.transformMatrixToGlobal(m);
-            m.preTranslate(-vp.mScrollX, -vp.mScrollY);
+            vp.transformMatrixToGlobal(matrix);
+            matrix.preTranslate(-vp.mScrollX, -vp.mScrollY);
         } else if (parent instanceof ViewRootImpl) {
             final ViewRootImpl vr = (ViewRootImpl) parent;
-            vr.transformMatrixToGlobal(m);
-            m.preTranslate(0, -vr.mCurScrollY);
+            vr.transformMatrixToGlobal(matrix);
+            matrix.preTranslate(0, -vr.mCurScrollY);
         }
 
-        m.preTranslate(mLeft, mTop);
+        matrix.preTranslate(mLeft, mTop);
 
         if (!hasIdentityMatrix()) {
-            m.preConcat(getMatrix());
+            matrix.preConcat(getMatrix());
         }
     }
 
@@ -23184,26 +23163,24 @@
      * Modifies the input matrix such that it maps on-screen coordinates to
      * view-local coordinates.
      *
-     * @param m input matrix to modify
-     * @hide
+     * @param matrix input matrix to modify
      */
-    @UnsupportedAppUsage
-    public void transformMatrixToLocal(Matrix m) {
+    public void transformMatrixToLocal(Matrix matrix) {
         final ViewParent parent = mParent;
         if (parent instanceof View) {
             final View vp = (View) parent;
-            vp.transformMatrixToLocal(m);
-            m.postTranslate(vp.mScrollX, vp.mScrollY);
+            vp.transformMatrixToLocal(matrix);
+            matrix.postTranslate(vp.mScrollX, vp.mScrollY);
         } else if (parent instanceof ViewRootImpl) {
             final ViewRootImpl vr = (ViewRootImpl) parent;
-            vr.transformMatrixToLocal(m);
-            m.postTranslate(0, vr.mCurScrollY);
+            vr.transformMatrixToLocal(matrix);
+            matrix.postTranslate(0, vr.mCurScrollY);
         }
 
-        m.postTranslate(-mLeft, -mTop);
+        matrix.postTranslate(-mLeft, -mTop);
 
         if (!hasIdentityMatrix()) {
-            m.postConcat(getInverseMatrix());
+            matrix.postConcat(getInverseMatrix());
         }
     }
 
@@ -24772,7 +24749,7 @@
         final SurfaceSession session = new SurfaceSession(root.mSurface);
         final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
                 .setName("drag surface")
-                .setSize(shadowSize.x, shadowSize.y)
+                .setBufferSize(shadowSize.x, shadowSize.y)
                 .setFormat(PixelFormat.TRANSLUCENT)
                 .build();
         final Surface surface = new Surface();
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 767cd33..d03d97e 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -393,7 +393,7 @@
                 case HAS_PERMANENT_MENU_KEY_AUTODETECT: {
                     IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
                     try {
-                        sHasPermanentMenuKey = !wm.hasNavigationBar();
+                        sHasPermanentMenuKey = !wm.hasNavigationBar(context.getDisplayId());
                         sHasPermanentMenuKeySet = true;
                     } catch (RemoteException ex) {
                         sHasPermanentMenuKey = false;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 484c6f3..d9d52c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -161,6 +161,19 @@
     private static final boolean MT_RENDERER_AVAILABLE = true;
 
     /**
+     * If set to true, the view system will switch from using rectangles retrieved from window to
+     * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
+     * directly from the full configuration, enabling richer information about the insets state, as
+     * well as new APIs to control it frame-by-frame, and synchronize animations with it.
+     * <p>
+     * Only switch this to true once the new insets system is productionized and the old APIs are
+     * fully migrated over.
+     */
+    private static final String USE_NEW_INSETS_PROPERTY = "persist.wm.new_insets";
+    private static final boolean USE_NEW_INSETS =
+            SystemProperties.getBoolean(USE_NEW_INSETS_PROPERTY, false);
+
+    /**
      * Set this system property to true to force the view hierarchy to render
      * at 60 Hz. This can be used to measure the potential framerate.
      */
@@ -432,6 +445,8 @@
     boolean mAdded;
     boolean mAddedTouchMode;
 
+    final Rect mTmpFrame = new Rect();
+
     // These are accessed by multiple threads.
     final Rect mWinFrame; // frame given by window manager.
 
@@ -444,6 +459,7 @@
     final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
             new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
     boolean mPendingAlwaysConsumeNavBar;
+    private InsetsState mPendingInsets;
     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
             = new ViewTreeObserver.InternalInsetsInfo();
 
@@ -531,6 +547,8 @@
             InputEventConsistencyVerifier.isInstrumentationEnabled() ?
                     new InputEventConsistencyVerifier(this, 0) : null;
 
+    private final InsetsController mInsetsController = new InsetsController();
+
     static final class SystemUiVisibilityInfo {
         int seq;
         int globalVisibility;
@@ -797,9 +815,11 @@
                     mAttachInfo.mRecomputeGlobalAttributes = true;
                     collectViewAttributes();
                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
-                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
+                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
-                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
+                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
+                            mInsetsController.getState());
+                    setFrame(mTmpFrame);
                 } catch (RemoteException e) {
                     mAdded = false;
                     mView = null;
@@ -826,6 +846,7 @@
                 mAttachInfo.mAlwaysConsumeNavBar =
                         (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
                 mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
+                mPendingInsets = mInsetsController.getState();
                 if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
                 if (res < WindowManagerGlobal.ADD_OKAY) {
                     mAttachInfo.mRootView = null;
@@ -1473,31 +1494,22 @@
 
         mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                 .setName("Bounds for - " + getTitle().toString())
-                .setSize(mWidth, mHeight)
                 .build();
 
-        setBoundsSurfaceSizeAndCrop();
+        setBoundsSurfaceCrop();
         mTransaction.setLayer(mBoundsSurfaceControl, zOrderLayer)
                     .show(mBoundsSurfaceControl)
                     .apply();
         mBoundsSurface.copyFrom(mBoundsSurfaceControl);
     }
 
-    private void setBoundsSurfaceSizeAndCrop() {
+    private void setBoundsSurfaceCrop() {
         // mWinFrame is already adjusted for surface insets. So offset it and use it as
         // the cropping bounds.
         mTempBoundsRect.set(mWinFrame);
         mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left,
                 mWindowAttributes.surfaceInsets.top);
         mTransaction.setWindowCrop(mBoundsSurfaceControl, mTempBoundsRect);
-
-        // Expand the bounds by the surface insets to get the size of surface.
-        mTempBoundsRect.inset(-mWindowAttributes.surfaceInsets.left,
-                -mWindowAttributes.surfaceInsets.top,
-                -mWindowAttributes.surfaceInsets.right,
-                -mWindowAttributes.surfaceInsets.bottom);
-        mTransaction.setSize(mBoundsSurfaceControl, mTempBoundsRect.width(),
-                mTempBoundsRect.height());
     }
 
     /**
@@ -1506,7 +1518,7 @@
      */
     private void updateBoundsSurface() {
         if (mBoundsSurfaceControl != null && mSurface.isValid()) {
-            setBoundsSurfaceSizeAndCrop();
+            setBoundsSurfaceCrop();
             mTransaction.deferTransactionUntilSurface(mBoundsSurfaceControl,
                     mSurface, mSurface.getNextFrameNumber())
                     .apply();
@@ -1780,7 +1792,8 @@
             Rect stableInsets = mDispatchStableInsets;
             DisplayCutout displayCutout = mDispatchDisplayCutout;
             // For dispatch we preserve old logic, but for direct requests from Views we allow to
-            // immediately use pending insets.
+            // immediately use pending insets. This is such that getRootWindowInsets returns the
+            // result from the layout hint before we ran a traversal shortly after adding a window.
             if (!forceConstruct
                     && (!mPendingContentInsets.equals(contentInsets) ||
                         !mPendingStableInsets.equals(stableInsets) ||
@@ -1797,10 +1810,16 @@
             }
             contentInsets = ensureInsetsNonNegative(contentInsets, "content");
             stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
-            mLastWindowInsets = new WindowInsets(contentInsets,
-                    null /* windowDecorInsets */, stableInsets,
-                    mContext.getResources().getConfiguration().isScreenRound(),
-                    mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+            if (USE_NEW_INSETS) {
+                mLastWindowInsets = mInsetsController.calculateInsets(
+                        mContext.getResources().getConfiguration().isScreenRound(),
+                        mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+            } else {
+                mLastWindowInsets = new WindowInsets(contentInsets,
+                        null /* windowDecorInsets */, stableInsets,
+                        mContext.getResources().getConfiguration().isScreenRound(),
+                        mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+            }
         }
         return mLastWindowInsets;
     }
@@ -2000,6 +2019,9 @@
                 if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
                     insetsChanged = true;
                 }
+                if (!mPendingInsets.equals(mInsetsController.getState())) {
+                    insetsChanged = true;
+                }
                 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                     windowSizeMayChange = true;
@@ -2193,6 +2215,8 @@
                         mAttachInfo.mStableInsets);
                 final boolean cutoutChanged = !mPendingDisplayCutout.equals(
                         mAttachInfo.mDisplayCutout);
+                final boolean insetsStateChanged = !mPendingInsets.equals(
+                        mInsetsController.getState());
                 final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
                 final boolean surfaceSizeChanged = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
@@ -2230,6 +2254,10 @@
                     mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
                     contentInsetsChanged = true;
                 }
+                if (insetsStateChanged) {
+                    mInsetsController.setState(mPendingInsets);
+                    contentInsetsChanged = true;
+                }
                 if (contentInsetsChanged || mLastSystemUiVisibility !=
                         mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
                         || mLastOverscanRequested != mAttachInfo.mOverscanRequested
@@ -2675,7 +2703,6 @@
     }
 
     private void maybeHandleWindowMove(Rect frame) {
-
         // TODO: Well, we are checking whether the frame has changed similarly
         // to how this is done for the insets. This is however incorrect since
         // the insets and the frame are translated. For example, the old frame
@@ -4180,6 +4207,7 @@
     private final static int MSG_UPDATE_POINTER_ICON = 27;
     private final static int MSG_POINTER_CAPTURE_CHANGED = 28;
     private final static int MSG_DRAW_FINISHED = 29;
+    private final static int MSG_INSETS_CHANGED = 30;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -4235,6 +4263,8 @@
                     return "MSG_POINTER_CAPTURE_CHANGED";
                 case MSG_DRAW_FINISHED:
                     return "MSG_DRAW_FINISHED";
+                case MSG_INSETS_CHANGED:
+                    return "MSG_INSETS_CHANGED";
             }
             return super.getMessageName(message);
         }
@@ -4315,7 +4345,7 @@
                                 || !mPendingVisibleInsets.equals(args.arg3)
                                 || !mPendingOutsets.equals(args.arg7);
 
-                        mWinFrame.set((Rect) args.arg1);
+                        setFrame((Rect) args.arg1);
                         mPendingOverscanInsets.set((Rect) args.arg5);
                         mPendingContentInsets.set((Rect) args.arg2);
                         mPendingStableInsets.set((Rect) args.arg6);
@@ -4338,16 +4368,25 @@
                         requestLayout();
                     }
                     break;
+                case MSG_INSETS_CHANGED:
+                    mPendingInsets = (InsetsState) msg.obj;
+
+                    // TODO: Full traversal not needed here
+                    if (USE_NEW_INSETS) {
+                        requestLayout();
+                    }
+                    break;
                 case MSG_WINDOW_MOVED:
                     if (mAdded) {
                         final int w = mWinFrame.width();
                         final int h = mWinFrame.height();
                         final int l = msg.arg1;
                         final int t = msg.arg2;
-                        mWinFrame.left = l;
-                        mWinFrame.right = l + w;
-                        mWinFrame.top = t;
-                        mWinFrame.bottom = t + h;
+                        mTmpFrame.left = l;
+                        mTmpFrame.right = l + w;
+                        mTmpFrame.top = t;
+                        mTmpFrame.bottom = t + h;
+                        setFrame(mTmpFrame);
 
                         mPendingBackDropFrame.set(mWinFrame);
                         maybeHandleWindowMove(mWinFrame);
@@ -6733,9 +6772,9 @@
                 (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
-                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
+                mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                 mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
-                mPendingMergedConfiguration, mSurface);
+                mPendingMergedConfiguration, mSurface, mPendingInsets);
 
         mPendingAlwaysConsumeNavBar =
                 (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -6745,15 +6784,22 @@
         }
 
         if (mTranslator != null) {
-            mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
+            mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame);
             mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
         }
+        setFrame(mTmpFrame);
+
         return relayoutResult;
     }
 
+    private void setFrame(Rect frame) {
+        mWinFrame.set(frame);
+        mInsetsController.onFrameChanged(frame);
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -6856,6 +6902,8 @@
 
         mChoreographer.dump(prefix, writer);
 
+        mInsetsController.dump(prefix, writer);
+
         writer.print(prefix); writer.println("View Hierarchy:");
         dumpViewHierarchy(innerPrefix, writer, mView);
     }
@@ -7064,6 +7112,10 @@
         mHandler.sendMessage(msg);
     }
 
+    private void dispatchInsetsChanged(InsetsState insetsState) {
+        mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget();
+    }
+
     public void dispatchMoved(int newX, int newY) {
         if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
         if (mTranslator != null) {
@@ -8127,6 +8179,14 @@
         }
 
         @Override
+        public void insetsChanged(InsetsState insetsState) {
+            final ViewRootImpl viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchInsetsChanged(insetsState);
+            }
+        }
+
+        @Override
         public void moved(int newX, int newY) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index d4c7069..9227249 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -202,6 +202,14 @@
     public static final String EXTRA_RESTORE_SESSION_TOKEN =
             "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
 
+    /**
+     * Internal extra used to pass a binder to the {@link IAugmentedAutofillManagerClient}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_AUGMENTED_AUTOFILL_CLIENT =
+            "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
+
     private static final String SESSION_ID_TAG = "android:sessionId";
     private static final String STATE_TAG = "android:state";
     private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
@@ -370,6 +378,9 @@
     private Cleaner mServiceClientCleaner;
 
     @GuardedBy("mLock")
+    private IAugmentedAutofillManagerClient mAugmentedAutofillServiceClient;
+
+    @GuardedBy("mLock")
     private AutofillCallback mCallback;
 
     private final Context mContext;
@@ -1664,6 +1675,8 @@
                 final IAutoFillManager service = mService;
                 final IAutoFillManagerClient serviceClient = mServiceClient;
                 mServiceClientCleaner = Cleaner.create(this, () -> {
+                    // TODO(b/111330312): call service to also remove reference to
+                    // mAugmentedAutofillServiceClient
                     try {
                         service.removeClient(serviceClient, userId);
                     } catch (RemoteException e) {
@@ -1808,6 +1821,7 @@
             if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
                 // Reset connection to system
                 mServiceClient = null;
+                mAugmentedAutofillServiceClient = null;
                 if (mServiceClientCleaner != null) {
                     mServiceClientCleaner.clean();
                     mServiceClientCleaner = null;
@@ -2054,6 +2068,29 @@
         }
     }
 
+    /**
+     * Gets a {@link AugmentedAutofillManagerClient} for this {@link AutofillManagerClient}.
+     *
+     * <p>These are 2 distinct objects because we need to restrict what the Augmented Autofill
+     * service can do (which is defined by {@code IAugmentedAutofillManagerClient.aidl}).
+     */
+    private void getAugmentedAutofillClient(@NonNull IResultReceiver result) {
+        synchronized (mLock) {
+            if (mAugmentedAutofillServiceClient == null) {
+                mAugmentedAutofillServiceClient = new AugmentedAutofillManagerClient(this);
+            }
+            final Bundle resultData = new Bundle();
+            resultData.putBinder(EXTRA_AUGMENTED_AUTOFILL_CLIENT,
+                    mAugmentedAutofillServiceClient.asBinder());
+
+            try {
+                result.send(0, resultData);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Could not send AugmentedAutofillClient back: " + e);
+            }
+        }
+    }
+
     /** @hide */
     public void requestHideFillUi() {
         requestHideFillUi(mIdShownFillUi, true);
@@ -2801,7 +2838,7 @@
     private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
         private final WeakReference<AutofillManager> mAfm;
 
-        AutofillManagerClient(AutofillManager autofillManager) {
+        private AutofillManagerClient(AutofillManager autofillManager) {
             mAfm = new WeakReference<>(autofillManager);
         }
 
@@ -2904,6 +2941,50 @@
                 afm.post(() -> afm.setSessionFinished(newState));
             }
         }
+
+        @Override
+        public void getAugmentedAutofillClient(IResultReceiver result) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.getAugmentedAutofillClient(result));
+            }
+        }
+    }
+
+    private static final class AugmentedAutofillManagerClient
+            extends IAugmentedAutofillManagerClient.Stub {
+        private final WeakReference<AutofillManager> mAfm;
+
+        private AugmentedAutofillManagerClient(AutofillManager autofillManager) {
+            mAfm = new WeakReference<>(autofillManager);
+        }
+
+        @Override
+        public Rect getViewCoordinates(@NonNull AutofillId id) {
+            // TODO(b/111330312): use handler / callback?
+            final AutofillManager afm = mAfm.get();
+            if (afm == null) return null;
+
+            final View view = afm.getClient().autofillClientFindViewByAutofillIdTraversal(id);
+            // TODO(b/111330312): optimize (for example, use temp rect from attach info) and
+            // fix (for example, take system status bar height into account) logic below
+            final int[] location = new int[2];
+            view.getLocationOnScreen(location);
+            final Rect rect = new Rect(location[0], location[1], location[0] + view.getWidth(),
+                    location[1] + view.getHeight());
+            if (sVerbose) {
+                Log.v(TAG, "Coordinates for " + id + ": " + rect);
+            }
+            return rect;
+        }
+
+        @Override
+        public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.autofill(sessionId, ids, values));
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
new file mode 100644
index 0000000..67cd0bf
--- /dev/null
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import java.util.List;
+
+import android.graphics.Rect;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Object running in the application process and responsible to provide the functionalities
+ * required by an Augmented Autofill service.
+ *
+ * @hide
+ */
+interface IAugmentedAutofillManagerClient {
+   Rect getViewCoordinates(in AutofillId id);
+   void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 0ff7a0b..63394b4 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -27,6 +27,8 @@
 import android.view.autofill.IAutofillWindowPresenter;
 import android.view.KeyEvent;
 
+import com.android.internal.os.IResultReceiver;
+
 /**
  * Object running in the application process and responsible for autofilling it.
  *
@@ -93,8 +95,18 @@
 
    /**
      * Marks the state of the session as finished.
+     *
      * @param newState STATE_FINISHED (because the autofill service returned a null
      * FillResponse) or STATE_UNKNOWN (because the session was removed).
      */
    void setSessionFinished(int newState);
+
+   /**
+    * Gets a reference to the binder object that can be used by the Augmented Autofill service.
+    *
+    * @param receiver, whose AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT extra will contain
+    * the reference.
+    */
+   void getAugmentedAutofillClient(in IResultReceiver result);
+
 }
diff --git a/core/java/android/view/intelligence/IIntelligenceManager.aidl b/core/java/android/view/intelligence/IIntelligenceManager.aidl
index 2f128de..7518ff5 100644
--- a/core/java/android/view/intelligence/IIntelligenceManager.aidl
+++ b/core/java/android/view/intelligence/IIntelligenceManager.aidl
@@ -38,7 +38,8 @@
     /**
       * Finishes a session.
       */
-    void finishSession(int userId, in InteractionSessionId sessionId);
+    void finishSession(int userId, in InteractionSessionId sessionId,
+                       in List<ContentCaptureEvent> events);
 
     /**
       * Sends a batch of events
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
index dfa52d9..2f3b4ef 100644
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -39,7 +39,6 @@
 import android.view.autofill.AutofillId;
 import android.view.intelligence.ContentCaptureEvent.EventType;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.Preconditions;
 
@@ -47,10 +46,18 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * TODO(b/111276913): add javadocs / implement
  */
+/*
+ * NOTE: all methods in this class should return right away, or do the real work in a handler
+ * thread.
+ *
+ * Hence, the only field that must be thread-safe is mEnabled, which is called at the beginning
+ * of every method.
+ */
 @SystemService(Context.INTELLIGENCE_MANAGER_SERVICE)
 public final class IntelligenceManager {
 
@@ -97,48 +104,48 @@
     private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
 
     /**
-     * Maximum number of events that are delayed for an app.
-     *
-     * <p>If the session is not started after the limit is reached, it's discarded.
+     * Maximum number of events that are buffered before sent to the app.
      */
-    private static final int MAX_DELAYED_SIZE = 20;
+    // TODO(b/111276913): use settings
+    private static final int MAX_BUFFER_SIZE = 100;
 
+    @NonNull
+    private final AtomicBoolean mDisabled = new AtomicBoolean();
+
+    @NonNull
     private final Context mContext;
 
     @Nullable
     private final IIntelligenceManager mService;
 
-    private final Object mLock = new Object();
-
     @Nullable
-    @GuardedBy("mLock")
     private InteractionSessionId mId;
 
-    @GuardedBy("mLock")
     private int mState = STATE_UNKNOWN;
 
-    @GuardedBy("mLock")
+    @Nullable
     private IBinder mApplicationToken;
 
-    // TODO(b/111276913): replace by an interface name implemented by Activity, similar to
-    // AutofillClient
-    @GuardedBy("mLock")
+    @Nullable
     private ComponentName mComponentName;
 
-    // TODO(b/111276913): create using maximum batch size as capacity
     /**
      * List of events held to be sent as a batch.
      */
-    @GuardedBy("mLock")
-    private final ArrayList<ContentCaptureEvent> mEvents = new ArrayList<>();
+    @Nullable
+    private ArrayList<ContentCaptureEvent> mEvents;
 
+    // TODO(b/111276913): use UI Thread directly (as calls are one-way) or a shared thread / handler
+    // held at the Application level
     private final Handler mHandler;
 
     /** @hide */
     public IntelligenceManager(@NonNull Context context, @Nullable IIntelligenceManager service) {
         mContext = Preconditions.checkNotNull(context, "context cannot be null");
+        if (VERBOSE) {
+            Log.v(TAG, "Constructor for " + context.getPackageName());
+        }
         mService = service;
-
         // TODO(b/111276913): use an existing bg thread instead...
         final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
         bgThread.start();
@@ -149,102 +156,100 @@
     public void onActivityCreated(@NonNull IBinder token, @NonNull ComponentName componentName) {
         if (!isContentCaptureEnabled()) return;
 
-        synchronized (mLock) {
-            if (mState != STATE_UNKNOWN) {
-                // TODO(b/111276913): revisit this scenario
-                Log.w(TAG, "ignoring onActivityStarted(" + token + ") while on state "
-                        + getStateAsString(mState));
-                return;
-            }
-            mState = STATE_WAITING_FOR_SERVER;
-            mId = new InteractionSessionId();
-            mApplicationToken = token;
-            mComponentName = componentName;
+        mHandler.sendMessage(obtainMessage(IntelligenceManager::handleStartSession, this,
+                token, componentName));
+    }
 
-            if (VERBOSE) {
-                Log.v(TAG, "onActivityCreated(): token=" + token + ", act="
-                        + getActivityDebugNameLocked() + ", id=" + mId);
-            }
-            final int flags = 0; // TODO(b/111276913): get proper flags
+    private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
+        if (mState != STATE_UNKNOWN) {
+            // TODO(b/111276913): revisit this scenario
+            Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
+                    + getStateAsString(mState));
+            return;
+        }
+        mState = STATE_WAITING_FOR_SERVER;
+        mId = new InteractionSessionId();
+        mApplicationToken = token;
+        mComponentName = componentName;
 
-            try {
-                mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
-                        mId, flags, new IResultReceiver.Stub() {
-                            @Override
-                            public void send(int resultCode, Bundle resultData)
-                                    throws RemoteException {
-                                synchronized (mLock) {
-                                    mState = resultCode;
-                                    if (VERBOSE) {
-                                        Log.v(TAG, "onActivityStarted() result: code=" + resultCode
-                                                + ", id=" + mId
-                                                + ", state=" + getStateAsString(mState));
-                                    }
-                                }
-                            }
-                        });
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
+        if (VERBOSE) {
+            Log.v(TAG, "handleStartSession(): token=" + token + ", act="
+                    + getActivityDebugName() + ", id=" + mId);
+        }
+        final int flags = 0; // TODO(b/111276913): get proper flags
+
+        try {
+            mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
+                    mId, flags, new IResultReceiver.Stub() {
+                        @Override
+                        public void send(int resultCode, Bundle resultData) {
+                            handleSessionStarted(resultCode);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error starting session for " + componentName.flattenToShortString() + ": "
+                    + e);
         }
     }
 
-    //TODO(b/111276913): should buffer event (and call service on handler thread), instead of
-    // calling right away
-    private void sendEvent(@NonNull ContentCaptureEvent event) {
-        mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this, event));
+    private  void handleSessionStarted(int resultCode) {
+        mState = resultCode;
+        mDisabled.set(mState == STATE_DISABLED);
+        if (VERBOSE) {
+            Log.v(TAG, "onActivityStarted() result: code=" + resultCode + ", id=" + mId
+                    + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get());
+        }
     }
 
-    private void handleSendEvent(@NonNull ContentCaptureEvent event) {
-
-        //TODO(b/111276913): make a copy and don't use lock
-        synchronized (mLock) {
-            mEvents.add(event);
-            final int numberEvents = mEvents.size();
-            if (mState != STATE_ACTIVE) {
-                if (numberEvents >= MAX_DELAYED_SIZE) {
-                    // Typically happens on system apps that are started before the system service
-                    // is ready (like com.android.settings/.FallbackHome)
-                    //TODO(b/111276913): try to ignore session while system is not ready / boot
-                    // not complete instead. Similarly, the manager service should return right away
-                    // when the user does not have a service set
-                    if (VERBOSE) {
-                        Log.v(TAG, "Closing session for " + getActivityDebugNameLocked()
-                                + " after " + numberEvents + " delayed events and state "
-                                + getStateAsString(mState));
-                    }
-                    // TODO(b/111276913): blacklist activity / use special flag to indicate that
-                    // when it's launched again
-                    resetStateLocked();
-                    return;
-                }
-
-                if (VERBOSE) {
-                    Log.v(TAG, "Delaying " + numberEvents + " events for "
-                            + getActivityDebugNameLocked() + " while on state "
-                            + getStateAsString(mState));
-                }
-                return;
+    private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+        if (mEvents == null) {
+            if (VERBOSE) {
+                Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
             }
+            mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
+        }
+        mEvents.add(event);
+        final int numberEvents = mEvents.size();
+        if (numberEvents < MAX_BUFFER_SIZE && !forceFlush) {
+            // Buffering events, return right away...
+            return;
+        }
 
-            if (mId == null) {
-                // Sanity check - should not happen
-                Log.wtf(TAG, "null session id for " + mComponentName);
-                return;
+        if (mState != STATE_ACTIVE) {
+            // Callback from startSession hasn't been called yet - typically happens on system
+            // apps that are started before the system service
+            // TODO(b/111276913): try to ignore session while system is not ready / boot
+            // not complete instead. Similarly, the manager service should return right away
+            // when the user does not have a service set
+            if (VERBOSE) {
+                Log.v(TAG, "Closing session for " + getActivityDebugName()
+                        + " after " + numberEvents + " delayed events and state "
+                        + getStateAsString(mState));
             }
+            handleResetState();
+            // TODO(b/111276913): blacklist activity / use special flag to indicate that
+            // when it's launched again
+            return;
+        }
 
-            //TODO(b/111276913): right now we're sending sending right away (unless not ready), but
-            // we should hold the events and flush later.
-            try {
-                if (DEBUG) {
-                    Log.d(TAG, "Sending " + numberEvents + " event(s) for "
-                            + getActivityDebugNameLocked());
-                }
-                mService.sendEvents(mContext.getUserId(), mId, mEvents);
-                mEvents.clear();
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+        if (mId == null) {
+            // Sanity check - should not happen
+            Log.wtf(TAG, "null session id for " + getActivityDebugName());
+            return;
+        }
+
+        try {
+            if (DEBUG) {
+                Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
             }
+            mService.sendEvents(mContext.getUserId(), mId, mEvents);
+            // TODO(b/111276913): decide whether we should clear or set it to null, as each has
+            // its own advantages: clearing will save extra allocations while the session is
+            // active, while setting to null would save memory if there's no more event coming.
+            mEvents.clear();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
+                    + ": " + e);
         }
     }
 
@@ -256,41 +261,54 @@
     public void onActivityLifecycleEvent(@EventType int type) {
         if (!isContentCaptureEnabled()) return;
         if (VERBOSE) {
-            Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugNameLocked()
+            Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugName()
                     + ": " + ContentCaptureEvent.getTypeAsString(type));
         }
-        sendEvent(new ContentCaptureEvent(type));
+        mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this,
+                new ContentCaptureEvent(type), /* forceFlush= */ true));
     }
 
     /** @hide */
     public void onActivityDestroyed() {
         if (!isContentCaptureEnabled()) return;
 
-        synchronized (mLock) {
-            //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
-            // id) and send it to the cache of batched commands
+        //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
+        // id) and send it to the cache of batched commands
+        if (VERBOSE) {
+            Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsString(mState)
+                    + ", mId=" + mId);
+        }
 
-            if (VERBOSE) {
-                Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsString(mState)
-                        + ", mId=" + mId);
+        mHandler.sendMessage(obtainMessage(IntelligenceManager::handleFinishSession, this));
+    }
+
+    private void handleFinishSession() {
+        //TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent
+        // to system_server, so it's ok to call both in sequence here. But once we split
+        // them so the events are sent directly to the service, we need to make sure they're
+        // sent in order.
+        try {
+            if (DEBUG) {
+                Log.d(TAG, "Finishing session " + mId + " with "
+                        + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+                        + getActivityDebugName());
             }
 
-            try {
-                mService.finishSession(mContext.getUserId(), mId);
-                resetStateLocked();
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
+            mService.finishSession(mContext.getUserId(), mId, mEvents);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error finishing session " + mId + " for " + getActivityDebugName()
+                    + ": " + e);
+        } finally {
+            handleResetState();
         }
     }
 
-    @GuardedBy("mLock")
-    private void resetStateLocked() {
+    private void handleResetState() {
         mState = STATE_UNKNOWN;
         mId = null;
         mApplicationToken = null;
         mComponentName = null;
-        mEvents.clear();
+        mEvents = null;
     }
 
     /**
@@ -309,8 +327,11 @@
         if (!(node instanceof ViewNode.ViewStructureImpl)) {
             throw new IllegalArgumentException("Invalid node class: " + node.getClass());
         }
-        sendEvent(new ContentCaptureEvent(TYPE_VIEW_APPEARED)
-                .setViewNode(((ViewNode.ViewStructureImpl) node).mNode));
+
+        mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this,
+                new ContentCaptureEvent(TYPE_VIEW_APPEARED)
+                        .setViewNode(((ViewNode.ViewStructureImpl) node).mNode),
+                        /* forceFlush= */ false));
     }
 
     /**
@@ -325,7 +346,9 @@
         Preconditions.checkNotNull(id);
         if (!isContentCaptureEnabled()) return;
 
-        sendEvent(new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id));
+        mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this,
+                new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id),
+                        /* forceFlush= */ false));
     }
 
     /**
@@ -339,10 +362,12 @@
     public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
             int flags) {
         Preconditions.checkNotNull(id);
+
         if (!isContentCaptureEnabled()) return;
 
-        sendEvent(new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
-                .setText(text));
+        mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this,
+                new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
+                        .setText(text), /* forceFlush= */ false));
     }
 
     /**
@@ -384,14 +409,11 @@
      * Checks whether content capture is enabled for this activity.
      */
     public boolean isContentCaptureEnabled() {
-        //TODO(b/111276913): properly implement by checking if it was explicitly disabled by
-        // service, or if service is not set
-        // (and probably renamign to isEnabledLocked()
-        return mService != null && mState != STATE_DISABLED;
+        return mService != null && !mDisabled.get();
     }
 
     /**
-     * Called by apps to explicitly enabled  or disable content capture.
+     * Called by apps to explicitly enable or disable content capture.
      *
      * <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call
      * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
@@ -504,25 +526,36 @@
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.println("IntelligenceManager");
         final String prefix2 = prefix + "  ";
-        synchronized (mLock) {
-            pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
+        pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
+        pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
+        if (mService != null) {
             pw.print(prefix2); pw.print("mService: "); pw.println(mService);
-            pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
-            pw.print(prefix2); pw.print("enabled: "); pw.println(isContentCaptureEnabled());
+        }
+        pw.print(prefix2); pw.print("mDisabled: "); pw.println(mDisabled.get());
+        pw.print(prefix2); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
+        if (mId != null) {
             pw.print(prefix2); pw.print("id: "); pw.println(mId);
-            pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
-            pw.print(getStateAsString(mState)); pw.println(")");
+        }
+        pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
+        pw.print(getStateAsString(mState)); pw.println(")");
+        if (mApplicationToken != null) {
             pw.print(prefix2); pw.print("app token: "); pw.println(mApplicationToken);
+        }
+        if (mComponentName != null) {
             pw.print(prefix2); pw.print("component name: ");
-            pw.println(mComponentName == null ? "null" : mComponentName.flattenToShortString());
+            pw.println(mComponentName.flattenToShortString());
+        }
+        if (mEvents != null) {
             final int numberEvents = mEvents.size();
-            pw.print(prefix2); pw.print("batched events: "); pw.println(numberEvents);
-            if (numberEvents > 0) {
+            pw.print(prefix2); pw.print("batched events: "); pw.print(numberEvents);
+            pw.print('/'); pw.println(MAX_BUFFER_SIZE);
+            if (VERBOSE && numberEvents > 0) {
+                final String prefix3 = prefix2 + "  ";
                 for (int i = 0; i < numberEvents; i++) {
                     final ContentCaptureEvent event = mEvents.get(i);
-                    pw.println(i); pw.print(": "); event.dump(pw); pw.println();
+                    pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
+                    pw.println();
                 }
-
             }
         }
     }
@@ -530,8 +563,7 @@
     /**
      * Gets a string that can be used to identify the activity on logging statements.
      */
-    @GuardedBy("mLock")
-    private String getActivityDebugNameLocked() {
+    private String getActivityDebugName() {
         return mComponentName == null ? mContext.getPackageName()
                 : mComponentName.flattenToShortString();
     }
diff --git a/core/java/android/view/intelligence/ViewNode.java b/core/java/android/view/intelligence/ViewNode.java
index cc78e6b..ea57461 100644
--- a/core/java/android/view/intelligence/ViewNode.java
+++ b/core/java/android/view/intelligence/ViewNode.java
@@ -238,6 +238,8 @@
 
         @Override
         public void setText(CharSequence text, int selectionStart, int selectionEnd) {
+            // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
+            mNode.mText = text;
             // TODO(b/111276913): implement or move to superclass
         }
 
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
new file mode 100644
index 0000000..8df83c0
--- /dev/null
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.app.Person;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.android.textclassifier.ActionsSuggestionsModel;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Helper class for action suggestions.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class ActionsSuggestionsHelper {
+    private static final int USER_LOCAL = 0;
+    private static final int FIRST_NON_LOCAL_USER = 1;
+
+    private ActionsSuggestionsHelper() {}
+
+    /**
+     * Converts the messages to a list of native messages object that the model can understand.
+     * <p>
+     * User id encoding - local user is represented as 0, Other users are numbered according to
+     * how far before they spoke last time in the conversation. For example, considering this
+     * conversation:
+     * <ul>
+     * <li> User A: xxx
+     * <li> Local user: yyy
+     * <li> User B: zzz
+     * </ul>
+     * User A will be encoded as 2, user B will be encoded as 1 and local user will be encoded as 0.
+     */
+    @NonNull
+    public static ActionsSuggestionsModel.ConversationMessage[] toNativeMessages(
+            @NonNull List<ConversationActions.Message> messages) {
+        List<ConversationActions.Message> messagesWithText =
+                messages.stream()
+                        .filter(message -> !TextUtils.isEmpty(message.getText()))
+                        .collect(Collectors.toCollection(ArrayList::new));
+        if (messagesWithText.isEmpty()) {
+            return new ActionsSuggestionsModel.ConversationMessage[0];
+        }
+        int size = messagesWithText.size();
+        // If the last message (the most important one) does not have the Person object, we will
+        // just use the last message and consider this message is sent from a remote user.
+        ConversationActions.Message lastMessage = messages.get(size - 1);
+        boolean useLastMessageOnly = lastMessage.getAuthor() == null;
+        if (useLastMessageOnly) {
+            return new ActionsSuggestionsModel.ConversationMessage[]{
+                    new ActionsSuggestionsModel.ConversationMessage(
+                            FIRST_NON_LOCAL_USER,
+                            lastMessage.getText().toString())};
+        }
+
+        // Encode the messages in the reverse order, stop whenever the Person object is missing.
+        Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
+        PersonEncoder personEncoder = new PersonEncoder();
+        for (int i = size - 1; i >= 0; i--) {
+            ConversationActions.Message message = messagesWithText.get(i);
+            if (message.getAuthor() == null) {
+                break;
+            }
+            nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
+                    personEncoder.encode(message.getAuthor()),
+                    message.getText().toString()));
+        }
+        return nativeMessages.toArray(
+                new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
+    }
+
+    private static final class PersonEncoder {
+        private final Map<Person, Integer> mMapping = new ArrayMap<>();
+        private int mNextUserId = FIRST_NON_LOCAL_USER;
+
+        private int encode(Person person) {
+            if (ConversationActions.Message.PERSON_USER_LOCAL.equals(person)) {
+                return USER_LOCAL;
+            }
+            Integer result = mMapping.get(person);
+            if (result == null) {
+                mMapping.put(person, mNextUserId);
+                result = mNextUserId;
+                mNextUserId++;
+            }
+            return result;
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 5fcf227..1a7b911 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -345,6 +345,16 @@
 
     /** Represents a message in the conversation. */
     public static final class Message implements Parcelable {
+        /**
+         * Represents the local user.
+         *
+         * @see Builder#setAuthor(Person)
+         */
+        public static final Person PERSON_USER_LOCAL =
+                new Person.Builder()
+                        .setKey("text-classifier-conversation-actions-local-user")
+                        .build();
+
         @Nullable
         private final Person mAuthor;
         @Nullable
@@ -446,7 +456,11 @@
             @Nullable
             private Bundle mExtras;
 
-            /** Sets the person who composed this message. */
+            /**
+             * Sets the person who composed this message.
+             * <p>
+             * Use {@link #PERSON_USER_LOCAL} to represent the local user.
+             */
             @NonNull
             public Builder setAuthor(@Nullable Person author) {
                 mAuthor = author;
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index f6c3d77..e0910c0 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -105,7 +105,7 @@
     /**
      * @hide
      */
-    static final TextClassification EMPTY = new TextClassification.Builder().build();
+    public static final TextClassification EMPTY = new TextClassification.Builder().build();
 
     private static final String LOG_TAG = "TextClassification";
     // TODO(toki): investigate a way to derive this based on device properties.
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 524f709..a2536cb 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -90,6 +90,11 @@
     String TYPE_DATE_TIME = "datetime";
     /** Flight number in IATA format. */
     String TYPE_FLIGHT_NUMBER = "flight";
+    /**
+     * Word that users may be interested to look up for meaning.
+     * @hide
+     */
+    String TYPE_DICTIONARY = "dictionary";
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -103,6 +108,7 @@
             TYPE_DATE,
             TYPE_DATE_TIME,
             TYPE_FLIGHT_NUMBER,
+            TYPE_DICTIONARY
     })
     @interface EntityType {}
 
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 798a820..8e14dfd 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -40,9 +40,9 @@
 import android.provider.Browser;
 import android.provider.CalendarContract;
 import android.provider.ContactsContract;
-import android.text.TextUtils;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
@@ -269,17 +269,17 @@
             final ZonedDateTime refTime = ZonedDateTime.now();
             final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
                     ? request.getEntityConfig().resolveEntityListModifications(
-                            getEntitiesForHints(request.getEntityConfig().getHints()))
+                    getEntitiesForHints(request.getEntityConfig().getHints()))
                     : mSettings.getEntityListDefault();
             final AnnotatorModel annotatorImpl =
                     getAnnotatorImpl(request.getDefaultLocales());
             final AnnotatorModel.AnnotatedSpan[] annotations =
                     annotatorImpl.annotate(
-                        textString,
-                        new AnnotatorModel.AnnotationOptions(
-                                refTime.toInstant().toEpochMilli(),
-                                        refTime.getZone().getId(),
-                                concatenateLocales(request.getDefaultLocales())));
+                            textString,
+                            new AnnotatorModel.AnnotationOptions(
+                                    refTime.toInstant().toEpochMilli(),
+                                    refTime.getZone().getId(),
+                                    concatenateLocales(request.getDefaultLocales())));
             for (AnnotatorModel.AnnotatedSpan span : annotations) {
                 final AnnotatorModel.ClassificationResult[] results =
                         span.getClassification();
@@ -373,20 +373,13 @@
                 // Actions model is optional, fallback if it is not available.
                 return mFallback.suggestConversationActions(request);
             }
-            List<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayList<>();
-            for (ConversationActions.Message message : request.getConversation()) {
-                if (TextUtils.isEmpty(message.getText())) {
-                    continue;
-                }
-                // TODO: We need to map the Person object to user id.
-                int userId = 1;
-                nativeMessages.add(
-                        new ActionsSuggestionsModel.ConversationMessage(
-                                userId, message.getText().toString()));
+            ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
+                    ActionsSuggestionsHelper.toNativeMessages(request.getConversation());
+            if (nativeMessages.length == 0) {
+                return mFallback.suggestConversationActions(request);
             }
             ActionsSuggestionsModel.Conversation nativeConversation =
-                    new ActionsSuggestionsModel.Conversation(nativeMessages.toArray(
-                            new ActionsSuggestionsModel.ConversationMessage[0]));
+                    new ActionsSuggestionsModel.Conversation(nativeMessages);
 
             ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions =
                     actionsImpl.suggestActions(nativeConversation, null);
@@ -523,10 +516,10 @@
         final TextClassification.Builder builder = new TextClassification.Builder()
                 .setText(classifiedText);
 
-        final int size = classifications.length;
+        final int typeCount = classifications.length;
         AnnotatorModel.ClassificationResult highestScoringResult =
-                size > 0 ? classifications[0] : null;
-        for (int i = 0; i < size; i++) {
+                typeCount > 0 ? classifications[0] : null;
+        for (int i = 0; i < typeCount; i++) {
             builder.setEntityType(classifications[i].getCollection(),
                                   classifications[i].getScore());
             if (classifications[i].getScore() > highestScoringResult.getScore()) {
@@ -534,9 +527,12 @@
             }
         }
 
+        // TODO: Make this configurable.
+        final float foreignTextThreshold = typeCount == 0 ? 0.5f : 0.7f;
         boolean isPrimaryAction = true;
         for (LabeledIntent labeledIntent : IntentFactory.create(
-                mContext, classifiedText, referenceTime, highestScoringResult)) {
+                mContext, classifiedText, isForeignText(classifiedText, foreignTextThreshold),
+                referenceTime, highestScoringResult)) {
             final RemoteAction action = labeledIntent.asRemoteAction(mContext);
             if (action == null) {
                 continue;
@@ -558,6 +554,42 @@
         return builder.setId(createId(text, start, end)).build();
     }
 
+    private boolean isForeignText(String text, float threshold) {
+        // TODO: Revisit this algorithm.
+        try {
+            final LangIdModel.LanguageResult[] langResults = getLangIdImpl().detectLanguages(text);
+            if (langResults.length <= 0) {
+                return false;
+            }
+
+            LangIdModel.LanguageResult highestScoringResult = langResults[0];
+            for (int i = 1; i < langResults.length; i++) {
+                if (langResults[i].getScore() > highestScoringResult.getScore()) {
+                    highestScoringResult = langResults[i];
+                }
+            }
+            if (highestScoringResult.getScore() < threshold) {
+                return false;
+            }
+            // TODO: Remove
+            Log.d(LOG_TAG, String.format("Language detected: <%s:%s>",
+                    highestScoringResult.getLanguage(), highestScoringResult.getScore()));
+
+            final Locale detected = new Locale(highestScoringResult.getLanguage());
+            final LocaleList deviceLocales = LocaleList.getDefault();
+            final int size = deviceLocales.size();
+            for (int i = 0; i < size; i++) {
+                if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) {
+                    return false;
+                }
+            }
+            return true;
+        } catch (Throwable t) {
+            Log.e(LOG_TAG, "Error detecting foreign text. Ignored.", t);
+        }
+        return false;
+    }
+
     @Override
     public void dump(@NonNull IndentingPrintWriter printWriter) {
         synchronized (mLock) {
@@ -608,7 +640,8 @@
     /**
      * Helper class to store the information from which RemoteActions are built.
      */
-    private static final class LabeledIntent {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public static final class LabeledIntent {
 
         static final int DEFAULT_REQUEST_CODE = 0;
 
@@ -643,7 +676,8 @@
             return mDescription;
         }
 
-        Intent getIntent() {
+        @VisibleForTesting
+        public Intent getIntent() {
             return mIntent;
         }
 
@@ -687,7 +721,8 @@
     /**
      * Creates intents based on the classification type.
      */
-    static final class IntentFactory {
+    @VisibleForTesting
+    public static final class IntentFactory {
 
         private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5);
         private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1);
@@ -698,53 +733,70 @@
         public static List<LabeledIntent> create(
                 Context context,
                 String text,
+                boolean foreignText,
                 @Nullable Instant referenceTime,
                 @Nullable AnnotatorModel.ClassificationResult classification) {
             final String type = classification != null
                     ? classification.getCollection().trim().toLowerCase(Locale.ENGLISH)
-                    : null;
+                    : "";
             text = text.trim();
+            final List<LabeledIntent> actions;
             switch (type) {
                 case TextClassifier.TYPE_EMAIL:
-                    return createForEmail(context, text);
+                    actions = createForEmail(context, text);
+                    break;
                 case TextClassifier.TYPE_PHONE:
-                    return createForPhone(context, text);
+                    actions = createForPhone(context, text);
+                    break;
                 case TextClassifier.TYPE_ADDRESS:
-                    return createForAddress(context, text);
+                    actions = createForAddress(context, text);
+                    break;
                 case TextClassifier.TYPE_URL:
-                    return createForUrl(context, text);
-                case TextClassifier.TYPE_DATE:
+                    actions = createForUrl(context, text);
+                    break;
+                case TextClassifier.TYPE_DATE:  // fall through
                 case TextClassifier.TYPE_DATE_TIME:
                     if (classification.getDatetimeResult() != null) {
                         final Instant parsedTime = Instant.ofEpochMilli(
                                 classification.getDatetimeResult().getTimeMsUtc());
-                        return createForDatetime(context, type, referenceTime, parsedTime);
+                        actions = createForDatetime(context, type, referenceTime, parsedTime);
                     } else {
-                        return new ArrayList<>();
+                        actions = new ArrayList<>();
                     }
+                    break;
                 case TextClassifier.TYPE_FLIGHT_NUMBER:
-                    return createForFlight(context, text);
+                    actions = createForFlight(context, text);
+                    break;
+                case TextClassifier.TYPE_DICTIONARY:
+                    actions = createForDictionary(context, text);
+                    break;
                 default:
-                    return new ArrayList<>();
+                    actions = new ArrayList<>();
+                    break;
             }
+            if (foreignText) {
+                insertTranslateAction(actions, context, text);
+            }
+            return actions;
         }
 
         @NonNull
         private static List<LabeledIntent> createForEmail(Context context, String text) {
-            return Arrays.asList(
-                    new LabeledIntent(
-                            context.getString(com.android.internal.R.string.email),
-                            context.getString(com.android.internal.R.string.email_desc),
-                            new Intent(Intent.ACTION_SENDTO)
-                                    .setData(Uri.parse(String.format("mailto:%s", text))),
-                            LabeledIntent.DEFAULT_REQUEST_CODE),
-                    new LabeledIntent(
-                            context.getString(com.android.internal.R.string.add_contact),
-                            context.getString(com.android.internal.R.string.add_contact_desc),
-                            new Intent(Intent.ACTION_INSERT_OR_EDIT)
-                                    .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                                    .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
-                            text.hashCode()));
+            final List<LabeledIntent> actions = new ArrayList<>();
+            actions.add(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.email),
+                    context.getString(com.android.internal.R.string.email_desc),
+                    new Intent(Intent.ACTION_SENDTO)
+                            .setData(Uri.parse(String.format("mailto:%s", text))),
+                    LabeledIntent.DEFAULT_REQUEST_CODE));
+            actions.add(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.add_contact),
+                    context.getString(com.android.internal.R.string.add_contact_desc),
+                    new Intent(Intent.ACTION_INSERT_OR_EDIT)
+                            .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+                            .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
+                    text.hashCode()));
+            return actions;
         }
 
         @NonNull
@@ -801,12 +853,14 @@
             if (Uri.parse(text).getScheme() == null) {
                 text = "http://" + text;
             }
-            return Arrays.asList(new LabeledIntent(
+            final List<LabeledIntent> actions = new ArrayList<>();
+            actions.add(new LabeledIntent(
                     context.getString(com.android.internal.R.string.browse),
                     context.getString(com.android.internal.R.string.browse_desc),
                     new Intent(Intent.ACTION_VIEW, Uri.parse(text))
                             .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
                     LabeledIntent.DEFAULT_REQUEST_CODE));
+            return actions;
         }
 
         @NonNull
@@ -828,12 +882,14 @@
 
         @NonNull
         private static List<LabeledIntent> createForFlight(Context context, String text) {
-            return Arrays.asList(new LabeledIntent(
+            final List<LabeledIntent> actions = new ArrayList<>();
+            actions.add(new LabeledIntent(
                     context.getString(com.android.internal.R.string.view_flight),
                     context.getString(com.android.internal.R.string.view_flight_desc),
                     new Intent(Intent.ACTION_WEB_SEARCH)
                             .putExtra(SearchManager.QUERY, text),
                     text.hashCode()));
+            return actions;
         }
 
         @NonNull
@@ -864,5 +920,27 @@
                                     parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION),
                     parsedTime.hashCode());
         }
+
+        private static void insertTranslateAction(
+                List<LabeledIntent> actions, Context context, String text) {
+            actions.add(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.translate),
+                    context.getString(com.android.internal.R.string.translate_desc),
+                    new Intent(Intent.ACTION_TRANSLATE)
+                            // TODO: Probably better to introduce a "translate" scheme instead of
+                            // using EXTRA_TEXT.
+                            .putExtra(Intent.EXTRA_TEXT, text),
+                    text.hashCode()));
+        }
+
+        @NonNull
+        private static List<LabeledIntent> createForDictionary(Context context, String text) {
+            return Arrays.asList(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.define),
+                    context.getString(com.android.internal.R.string.define_desc),
+                    new Intent(Intent.ACTION_DEFINE)
+                            .putExtra(Intent.EXTRA_TEXT, text),
+                    text.hashCode()));
+        }
     }
 }
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 02aee50..1e42c41 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -59,7 +59,7 @@
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({STATUS_LINKS_APPLIED, STATUS_NO_LINKS_FOUND, STATUS_NO_LINKS_APPLIED,
-            STATUS_DIFFERENT_TEXT})
+            STATUS_DIFFERENT_TEXT, STATUS_UNSUPPORTED_CHARACTER})
     public @interface Status {}
 
     /** Links were successfully applied to the text. */
@@ -74,6 +74,9 @@
     /** The specified text does not match the text used to generate the links. */
     public static final int STATUS_DIFFERENT_TEXT = 3;
 
+    /** The specified text contains unsupported characters. */
+    public static final int STATUS_UNSUPPORTED_CHARACTER = 4;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({APPLY_STRATEGY_IGNORE, APPLY_STRATEGY_REPLACE})
diff --git a/core/java/android/view/textclassifier/TextLinksParams.java b/core/java/android/view/textclassifier/TextLinksParams.java
index be4c3bc..8af4233 100644
--- a/core/java/android/view/textclassifier/TextLinksParams.java
+++ b/core/java/android/view/textclassifier/TextLinksParams.java
@@ -107,6 +107,13 @@
         Preconditions.checkNotNull(textLinks);
 
         final String textString = text.toString();
+
+        if (Linkify.containsUnsupportedCharacters(textString)) {
+            // Do not apply links to text containing unsupported characters.
+            android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+            return TextLinks.STATUS_UNSUPPORTED_CHARACTER;
+        }
+
         if (!textString.startsWith(textLinks.getText())) {
             return TextLinks.STATUS_DIFFERENT_TEXT;
         }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1093719..414cb8f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -410,6 +410,9 @@
         if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
             setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
         }
+        if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
+            setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES);
+        }
 
         if (context == null) {
             throw new IllegalArgumentException("Invalid context argument");
@@ -2695,8 +2698,8 @@
     }
 
     @Override
-    public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
-        return mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
+    public void onProvideContentCaptureStructure(ViewStructure structure, int flags) {
+        mProvider.getViewDelegate().onProvideContentCaptureStructure(structure, flags);
     }
 
     @Override
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index ceada07..95e7a986 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -341,10 +341,9 @@
             return true; // true is the default value returned by View.isVisibleToUserForAutofill()
         }
 
-        default boolean onProvideContentCaptureStructure(
+        default void onProvideContentCaptureStructure(
                 @SuppressWarnings("unused") android.view.ViewStructure structure,
                 @SuppressWarnings("unused") int flags) {
-            return false; // WebView provides virtual views and is responsible to notify manager
         }
 
         public AccessibilityNodeProvider getAccessibilityNodeProvider();
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 12cc54d..c21182c 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -106,9 +106,9 @@
     private boolean mHaveFrame = false;
     @UnsupportedAppUsage
     private boolean mAdjustViewBounds = false;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mMaxWidth = Integer.MAX_VALUE;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mMaxHeight = Integer.MAX_VALUE;
 
     // these are applied to the drawable
@@ -1331,9 +1331,17 @@
         }
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
-    public void animateTransform(Matrix matrix) {
+    /**
+     * Applies a temporary transformation {@link Matrix} to the view's drawable when it is drawn.
+     * Allows custom scaling, translation, and perspective distortion during an animation.
+     *
+     * This method is a lightweight analogue of {@link ImageView#setImageMatrix(Matrix)} to use
+     * only during animations as this matrix will be cleared after the next drawable
+     * update or view's bounds change.
+     *
+     * @param matrix The transformation parameters in matrix form.
+     */
+    public void animateTransform(@Nullable Matrix matrix) {
         if (mDrawable == null) {
             return;
         }
@@ -1341,6 +1349,7 @@
             final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
             final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
             mDrawable.setBounds(0, 0, vwidth, vheight);
+            mDrawMatrix = null;
         } else {
             mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight);
             if (mDrawMatrix == null) {
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index f2e478d..15910bb 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -189,7 +189,7 @@
             @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
                 equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
         }, formatToHexString = true)
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mGravity = Gravity.START | Gravity.TOP;
 
     @ViewDebug.ExportedProperty(category = "measurement")
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index f4c25c3..7d02757 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -837,7 +837,7 @@
             mSurfaceSession = new SurfaceSession(parentSurface);
             mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                     .setFormat(PixelFormat.TRANSLUCENT)
-                    .setSize(mSurfaceWidth, mSurfaceHeight)
+                    .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                     .setName("magnifier surface")
                     .setFlags(SurfaceControl.HIDDEN)
                     .build();
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 74051e2..506d615 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -204,7 +204,7 @@
 
     private View mBaselineView = null;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mGravity = Gravity.START | Gravity.TOP;
     private final Rect mContentBounds = new Rect();
     private final Rect mSelfBounds = new Rect();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index c0979fe..7b39efe 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -161,6 +161,7 @@
     private static final int LAYOUT_PARAM_ACTION_TAG = 19;
     private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
     private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
+    private static final int SET_INT_TAG_TAG = 22;
 
     /**
      * Application that hosts the remote views.
@@ -274,6 +275,15 @@
     }
 
     /**
+     * Sets an integer tag to the view.
+     *
+     * @hide
+     */
+    public void setIntTag(int viewId, int key, int tag) {
+        addAction(new SetIntTagAction(viewId, key, tag));
+    }
+
+    /**
      * Set that it is disallowed to reapply another remoteview with the same layout as this view.
      * This should be done if an action is destroying the view tree of the base layout.
      *
@@ -2122,6 +2132,43 @@
         }
     }
 
+    private class SetIntTagAction extends Action {
+        private final int mViewId;
+        private final int mKey;
+        private final int mTag;
+
+        SetIntTagAction(int viewId, int key, int tag) {
+            mViewId = viewId;
+            mKey = key;
+            mTag = tag;
+        }
+
+        SetIntTagAction(Parcel parcel) {
+            mViewId = parcel.readInt();
+            mKey = parcel.readInt();
+            mTag = parcel.readInt();
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mViewId);
+            dest.writeInt(mKey);
+            dest.writeInt(mTag);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+            final View target = root.findViewById(mViewId);
+            if (target == null) return;
+
+            target.setTagInternal(mKey, mTag);
+        }
+
+        @Override
+        public int getActionTag() {
+            return SET_INT_TAG_TAG;
+        }
+    }
+
     /**
      * Create a new RemoteViews object that will display the views contained
      * in the specified layout file.
@@ -2326,6 +2373,8 @@
                 return new OverrideTextColorsAction(parcel);
             case SET_RIPPLE_DRAWABLE_COLOR_TAG:
                 return new SetRippleDrawableColor(parcel);
+            case SET_INT_TAG_TAG:
+                return new SetIntTagAction(parcel);
             default:
                 throw new ActionException("Tag " + tag + " not found");
         }
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 6cb0eaa..4caf288 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -31,6 +31,7 @@
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.TextUtils;
+import android.text.util.Linkify;
 import android.util.Log;
 import android.view.ActionMode;
 import android.view.textclassifier.SelectionEvent;
@@ -687,17 +688,6 @@
             mTokenIterator = SelectionSessionLogger.getTokenIterator(textView.getTextLocale());
         }
 
-        @TextClassifier.WidgetType
-        private static String getWidetType(TextView textView) {
-            if (textView.isTextEditable()) {
-                return TextClassifier.WIDGET_TYPE_EDITTEXT;
-            }
-            if (textView.isTextSelectable()) {
-                return TextClassifier.WIDGET_TYPE_TEXTVIEW;
-            }
-            return TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW;
-        }
-
         public void logSelectionStarted(
                 TextClassifier classificationSession,
                 CharSequence text, int index,
@@ -1045,7 +1035,12 @@
 
                 trimText();
                 final TextClassification classification;
-                if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
+                if (Linkify.containsUnsupportedCharacters(mText)) {
+                    // Do not show smart actions for text containing unsupported characters.
+                    android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
+                    classification = TextClassification.EMPTY;
+                } else if (mContext.getApplicationInfo().targetSdkVersion
+                        >= Build.VERSION_CODES.P) {
                     final TextClassification.Request request =
                             new TextClassification.Request.Builder(
                                     mTrimmedText, mRelativeStart, mRelativeEnd)
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index d55c09f..79dc670 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1419,27 +1419,10 @@
         return Switch.class.getName();
     }
 
+    /** @hide */
     @Override
-    public void onProvideStructure(ViewStructure structure) {
-        super.onProvideStructure(structure);
-        onProvideStructureForAssistOrAutofillOrViewCapture(structure);
-    }
-
-    @Override
-    public void onProvideAutofillStructure(ViewStructure structure, int flags) {
-        super.onProvideAutofillStructure(structure, flags);
-        onProvideStructureForAssistOrAutofillOrViewCapture(structure);
-    }
-
-    @Override
-    public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
-        final boolean notifyManager = super.onProvideContentCaptureStructure(structure, flags);
-        onProvideStructureForAssistOrAutofillOrViewCapture(structure);
-        return notifyManager;
-    }
-
-    // NOTE: currently there is no difference for any type, so it doesn't take flags
-    private void onProvideStructureForAssistOrAutofillOrViewCapture(ViewStructure structure) {
+    protected void onProvideStructure(@NonNull ViewStructure structure,
+            @ViewStructureType int viewFor, int flags) {
         CharSequence switchText = isChecked() ? mTextOn : mTextOff;
         if (!TextUtils.isEmpty(switchText)) {
             CharSequence oldText = structure.getText();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 35be766..85d851a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10942,6 +10942,9 @@
         if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL
                 || viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
             if (mLayout == null) {
+                if (viewFor == VIEW_STRUCTURE_FOR_CONTENT_CAPTURE) {
+                    Log.w(LOG_TAG, "onProvideContentCaptureStructure(): calling assumeLayout()");
+                }
                 assumeLayout();
             }
             Layout layout = mLayout;
@@ -12265,13 +12268,13 @@
     }
 
     /**
-     * Returns the current {@link TextDirectionHeuristic}.
-     *
-     * @return the current {@link TextDirectionHeuristic}.
-     * @hide
+     * Returns resolved {@link TextDirectionHeuristic} that will be used for text layout.
+     * The {@link TextDirectionHeuristic} that is used by TextView is only available after
+     * {@link #getTextDirection()} and {@link #getLayoutDirection()} is resolved. Therefore the
+     * return value may not be the same as the one TextView uses if the View's layout direction is
+     * not resolved or detached from parent root view.
      */
-    @UnsupportedAppUsage
-    protected TextDirectionHeuristic getTextDirectionHeuristic() {
+    public TextDirectionHeuristic getTextDirectionHeuristic() {
         if (hasPasswordTransformationMethod()) {
             // passwords fields should be LTR
             return TextDirectionHeuristics.LTR;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index e2b8f7d..5d08a25 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -309,9 +309,6 @@
         } else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
             prefer = RECOMMEND_INSTALL_INTERNAL;
             checkBoth = false;
-        } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-            prefer = RECOMMEND_INSTALL_EXTERNAL;
-            checkBoth = false;
         } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
             prefer = RECOMMEND_INSTALL_INTERNAL;
             checkBoth = false;
diff --git a/core/java/com/android/internal/os/KernelCpuProcStringReader.java b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
index 22435ae..b3aec0c 100644
--- a/core/java/com/android/internal/os/KernelCpuProcStringReader.java
+++ b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
@@ -25,6 +25,7 @@
 import java.io.IOException;
 import java.nio.CharBuffer;
 import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
@@ -59,6 +60,7 @@
     private static final String PROC_UID_FREQ_TIME = "/proc/uid_time_in_state";
     private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_concurrent_active_time";
     private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_concurrent_policy_time";
+    private static final String PROC_UID_USER_SYS_TIME = "/proc/uid_cputime/show_uid_stat";
 
     private static final KernelCpuProcStringReader FREQ_TIME_READER =
             new KernelCpuProcStringReader(PROC_UID_FREQ_TIME);
@@ -66,19 +68,25 @@
             new KernelCpuProcStringReader(PROC_UID_ACTIVE_TIME);
     private static final KernelCpuProcStringReader CLUSTER_TIME_READER =
             new KernelCpuProcStringReader(PROC_UID_CLUSTER_TIME);
+    private static final KernelCpuProcStringReader USER_SYS_TIME_READER =
+            new KernelCpuProcStringReader(PROC_UID_USER_SYS_TIME);
 
-    public static KernelCpuProcStringReader getFreqTimeReaderInstance() {
+    static KernelCpuProcStringReader getFreqTimeReaderInstance() {
         return FREQ_TIME_READER;
     }
 
-    public static KernelCpuProcStringReader getActiveTimeReaderInstance() {
+    static KernelCpuProcStringReader getActiveTimeReaderInstance() {
         return ACTIVE_TIME_READER;
     }
 
-    public static KernelCpuProcStringReader getClusterTimeReaderInstance() {
+    static KernelCpuProcStringReader getClusterTimeReaderInstance() {
         return CLUSTER_TIME_READER;
     }
 
+    static KernelCpuProcStringReader getUserSysTimeReaderInstance() {
+        return USER_SYS_TIME_READER;
+    }
+
     private int mErrors = 0;
     private final Path mFile;
     private char[] mBuf;
@@ -164,12 +172,12 @@
             // ReentrantReadWriteLock allows lock downgrading.
             mReadLock.lock();
             return new ProcFileIterator(total);
-        } catch (FileNotFoundException e) {
+        } catch (FileNotFoundException | NoSuchFileException e) {
             mErrors++;
             Slog.w(TAG, "File not found. It's normal if not implemented: " + mFile);
         } catch (IOException e) {
             mErrors++;
-            Slog.e(TAG, "Error reading: " + mFile, e);
+            Slog.e(TAG, "Error reading " + mFile, e);
         } finally {
             StrictMode.setThreadPolicyMask(oldMask);
             mWriteLock.unlock();
@@ -193,6 +201,11 @@
             mSize = size;
         }
 
+        /** @return Whether there are more lines in the iterator. */
+        public boolean hasNextLine() {
+            return mPos < mSize;
+        }
+
         /**
          * Fetches the next line. Note that all subsequent return values share the same char[]
          * under the hood.
@@ -214,44 +227,6 @@
             return CharBuffer.wrap(mBuf, start, i - start);
         }
 
-        /**
-         * Fetches the next line, converts all numbers into long, and puts into the given long[].
-         * To avoid GC, caller should try to use the same array for all calls. All non-numeric
-         * chars are treated as delimiters. All numbers are non-negative.
-         *
-         * @param array An array to store the parsed numbers.
-         * @return The number of elements written to the given array. -1 if there is no more line.
-         */
-        public int nextLineAsArray(long[] array) {
-            CharBuffer buf = nextLine();
-            if (buf == null) {
-                return -1;
-            }
-            int count = 0;
-            long num = -1;
-            char c;
-
-            while (buf.remaining() > 0 && count < array.length) {
-                c = buf.get();
-                if (num < 0) {
-                    if (isNumber(c)) {
-                        num = c - '0';
-                    }
-                } else {
-                    if (isNumber(c)) {
-                        num = num * 10 + c - '0';
-                    } else {
-                        array[count++] = num;
-                        num = -1;
-                    }
-                }
-            }
-            if (num >= 0) {
-                array[count++] = num;
-            }
-            return count;
-        }
-
         /** Total size of the proc file in chars. */
         public int size() {
             return mSize;
@@ -262,8 +237,63 @@
             mReadLock.unlock();
         }
 
-        private boolean isNumber(char c) {
-            return c >= '0' && c <= '9';
+
+    }
+
+    /**
+     * Converts all numbers in the CharBuffer into longs, and puts into the given long[].
+     *
+     * Space and colon are treated as delimiters. All other chars are not allowed. All numbers
+     * are non-negative. To avoid GC, caller should try to use the same array for all calls.
+     *
+     * This method also resets the given buffer to the original position before return so that
+     * it can be read again.
+     *
+     * @param buf   The char buffer to be converted.
+     * @param array An array to store the parsed numbers.
+     * @return The number of elements written to the given array. -1 if buf is null, -2 if buf
+     * contains invalid char, -3 if any number overflows.
+     */
+    public static int asLongs(CharBuffer buf, long[] array) {
+        if (buf == null) {
+            return -1;
         }
+        final int initialPos = buf.position();
+        int count = 0;
+        long num = -1;
+        char c;
+
+        while (buf.remaining() > 0 && count < array.length) {
+            c = buf.get();
+            if (!(isNumber(c) || c == ' ' || c == ':')) {
+                buf.position(initialPos);
+                return -2;
+            }
+            if (num < 0) {
+                if (isNumber(c)) {
+                    num = c - '0';
+                }
+            } else {
+                if (isNumber(c)) {
+                    num = num * 10 + c - '0';
+                    if (num < 0) {
+                        buf.position(initialPos);
+                        return -3;
+                    }
+                } else {
+                    array[count++] = num;
+                    num = -1;
+                }
+            }
+        }
+        if (num >= 0) {
+            array[count++] = num;
+        }
+        buf.position(initialPos);
+        return count;
+    }
+
+    private static boolean isNumber(char c) {
+        return c >= '0' && c <= '9';
     }
 }
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 3861739..2e901e4 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -103,10 +103,16 @@
 
     /**
      * Default predicate for what UIDs to check for when getting processes. This filters to only
-     * select system UIDs (1000-1999)
+     * select UID 1000 (the {@code system} user)
      */
-    private static final Predicate<Integer> DEFAULT_UID_PREDICATE =
-            uid -> 1000 <= uid && uid < 2000;
+    private static final Predicate<Integer> DEFAULT_UID_PREDICATE = uid -> uid == 1000;
+
+    /**
+     * Do not report any threads that have a total CPU usage (across all frequencies) less than or
+     * equal to this number. This significantly reduces the amount of reported threads without
+     * losing any important information
+     */
+    private static final int TOTAL_CPU_USAGE_THRESHOLD_MILLIS = 20;
 
     /**
      * Value returned when there was an error getting an integer ID value (e.g. PID, UID)
@@ -339,6 +345,15 @@
         }
         int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong);
 
+        // Filter threads that have low total CPU usage
+        int cpuUsageSum = 0;
+        for (int i = 0; i < cpuUsages.length; i++) {
+            cpuUsageSum += cpuUsages[i];
+        }
+        if (cpuUsageSum <= TOTAL_CPU_USAGE_THRESHOLD_MILLIS) {
+            return null;
+        }
+
         return new ThreadCpuUsage(threadId, threadName, cpuUsages);
     }
 
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
new file mode 100644
index 0000000..7021b57
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -0,0 +1,776 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static com.android.internal.os.KernelCpuProcStringReader.asLongs;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
+
+import java.io.BufferedReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Reads per-UID CPU time proc files. Concrete implementations are all nested inside.
+ *
+ * This class uses a throttler to reject any {@link #readDelta} or {@link #readAbsolute} call
+ * within {@link #mMinTimeBetweenRead}. The throttler can be enable / disabled via a param in
+ * the constructor.
+ *
+ * This class and its subclasses are NOT thread-safe and NOT designed to be accessed by more than
+ * one caller since each caller has its own view of delta.
+ *
+ * @param <T> The type of CPU time for the callback.
+ */
+public abstract class KernelCpuUidTimeReader<T> {
+    protected static final boolean DEBUG = false;
+    private static final long DEFAULT_MIN_TIME_BETWEEN_READ = 1000L; // In milliseconds
+
+    final String mTag = this.getClass().getSimpleName();
+    final SparseArray<T> mLastTimes = new SparseArray<>();
+    final KernelCpuProcStringReader mReader;
+    final boolean mThrottle;
+    private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
+    private long mLastReadTimeMs = 0;
+
+    /**
+     * Callback interface for processing each line of the proc file.
+     *
+     * @param <T> The type of CPU time for the callback function.
+     */
+    public interface Callback<T> {
+        /**
+         * @param uid  UID of the app
+         * @param time Time spent. The exact data structure depends on subclass implementation.
+         */
+        void onUidCpuTime(int uid, T time);
+    }
+
+    KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+        mReader = reader;
+        mThrottle = throttle;
+    }
+
+    /**
+     * Reads the proc file, calling into the callback with a delta of time for each UID.
+     *
+     * @param cb The callback to invoke for each line of the proc file. If null,the data is
+     *           consumed and subsequent calls to readDelta will provide a fresh delta.
+     */
+    public void readDelta(@Nullable Callback<T> cb) {
+        if (!mThrottle) {
+            readDeltaImpl(cb);
+            return;
+        }
+        final long currTimeMs = SystemClock.elapsedRealtime();
+        if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
+            if (DEBUG) {
+                Slog.d(mTag, "Throttle readDelta");
+            }
+            return;
+        }
+        readDeltaImpl(cb);
+        mLastReadTimeMs = currTimeMs;
+    }
+
+    /**
+     * Reads the proc file, calling into the callback with cumulative time for each UID.
+     *
+     * @param cb The callback to invoke for each line of the proc file. It cannot be null.
+     */
+    public void readAbsolute(Callback<T> cb) {
+        if (!mThrottle) {
+            readAbsoluteImpl(cb);
+            return;
+        }
+        final long currTimeMs = SystemClock.elapsedRealtime();
+        if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
+            if (DEBUG) {
+                Slog.d(mTag, "Throttle readAbsolute");
+            }
+            return;
+        }
+        readAbsoluteImpl(cb);
+        mLastReadTimeMs = currTimeMs;
+    }
+
+    abstract void readDeltaImpl(@Nullable Callback<T> cb);
+
+    abstract void readAbsoluteImpl(Callback<T> callback);
+
+    /**
+     * Removes the UID from internal accounting data. This method, overridden in
+     * {@link KernelCpuUidUserSysTimeReader}, also removes the UID from the kernel module.
+     *
+     * @param uid The UID to remove.
+     * @see KernelCpuUidUserSysTimeReader#removeUid(int)
+     */
+    public void removeUid(int uid) {
+        mLastTimes.delete(uid);
+    }
+
+    /**
+     * Removes UIDs in a given range from internal accounting data. This method, overridden in
+     * {@link KernelCpuUidUserSysTimeReader}, also removes the UIDs from the kernel module.
+     *
+     * @param startUid the first uid to remove.
+     * @param endUid   the last uid to remove.
+     * @see KernelCpuUidUserSysTimeReader#removeUidsInRange(int, int)
+     */
+    public void removeUidsInRange(int startUid, int endUid) {
+        if (endUid < startUid) {
+            Slog.e(mTag, "start UID " + startUid + " > end UID " + endUid);
+            return;
+        }
+        mLastTimes.put(startUid, null);
+        mLastTimes.put(endUid, null);
+        final int firstIndex = mLastTimes.indexOfKey(startUid);
+        final int lastIndex = mLastTimes.indexOfKey(endUid);
+        mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+    }
+
+    /**
+     * Set the minimum time in milliseconds between reads. If throttle is not enabled, this method
+     * has no effect.
+     *
+     * @param minTimeBetweenRead The minimum time in milliseconds.
+     */
+    public void setThrottle(long minTimeBetweenRead) {
+        if (mThrottle && minTimeBetweenRead >= 0) {
+            mMinTimeBetweenRead = minTimeBetweenRead;
+        }
+    }
+
+    /**
+     * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+     *
+     * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
+     *
+     * This provides the time a UID's processes spent executing in user-space and kernel-space.
+     * The file contains a monotonically increasing count of time for a single boot. This class
+     * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+     * delta.
+     */
+    public static class KernelCpuUidUserSysTimeReader extends KernelCpuUidTimeReader<long[]> {
+        private static final String REMOVE_UID_PROC_FILE = "/proc/uid_cputime/remove_uid_range";
+
+        // [uid, user_time, system_time, (maybe) power_in_milli-amp-micro_seconds]
+        private final long[] mBuffer = new long[4];
+        // A reusable array to hold [user_time, system_time] for the callback.
+        private final long[] mUsrSysTime = new long[2];
+
+        public KernelCpuUidUserSysTimeReader(boolean throttle) {
+            super(KernelCpuProcStringReader.getUserSysTimeReaderInstance(), throttle);
+        }
+
+        @VisibleForTesting
+        public KernelCpuUidUserSysTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+            super(reader, throttle);
+        }
+
+        @Override
+        void readDeltaImpl(@Nullable Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (iter == null) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) < 3) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    final int uid = (int) mBuffer[0];
+                    long[] lastTimes = mLastTimes.get(uid);
+                    if (lastTimes == null) {
+                        lastTimes = new long[2];
+                        mLastTimes.put(uid, lastTimes);
+                    }
+                    final long currUsrTimeUs = mBuffer[1];
+                    final long currSysTimeUs = mBuffer[2];
+                    mUsrSysTime[0] = currUsrTimeUs - lastTimes[0];
+                    mUsrSysTime[1] = currSysTimeUs - lastTimes[1];
+
+                    if (mUsrSysTime[0] < 0 || mUsrSysTime[1] < 0) {
+                        Slog.e(mTag, "Negative user/sys time delta for UID=" + uid
+                                + "\nPrev times: u=" + lastTimes[0] + " s=" + lastTimes[1]
+                                + " Curr times: u=" + currUsrTimeUs + " s=" + currSysTimeUs);
+                    } else if (mUsrSysTime[0] > 0 || mUsrSysTime[1] > 0) {
+                        if (cb != null) {
+                            cb.onUidCpuTime(uid, mUsrSysTime);
+                        }
+                    }
+                    lastTimes[0] = currUsrTimeUs;
+                    lastTimes[1] = currSysTimeUs;
+                }
+            }
+        }
+
+        @Override
+        void readAbsoluteImpl(Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (iter == null) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) < 3) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    mUsrSysTime[0] = mBuffer[1]; // User time in microseconds
+                    mUsrSysTime[1] = mBuffer[2]; // System time in microseconds
+                    cb.onUidCpuTime((int) mBuffer[0], mUsrSysTime);
+                }
+            }
+        }
+
+        @Override
+        public void removeUid(int uid) {
+            super.removeUid(uid);
+            removeUidsFromKernelModule(uid, uid);
+        }
+
+        @Override
+        public void removeUidsInRange(int startUid, int endUid) {
+            super.removeUidsInRange(startUid, endUid);
+            removeUidsFromKernelModule(startUid, endUid);
+        }
+
+        /**
+         * Removes UIDs in a given range from the kernel module and internal accounting data. Only
+         * {@link BatteryStatsImpl} and its child processes should call this, as the change on
+         * Kernel is
+         * visible system wide.
+         *
+         * @param startUid the first uid to remove
+         * @param endUid   the last uid to remove
+         */
+        private void removeUidsFromKernelModule(int startUid, int endUid) {
+            Slog.d(mTag, "Removing uids " + startUid + "-" + endUid);
+            final int oldMask = StrictMode.allowThreadDiskWritesMask();
+            try (FileWriter writer = new FileWriter(REMOVE_UID_PROC_FILE)) {
+                writer.write(startUid + "-" + endUid);
+                writer.flush();
+            } catch (IOException e) {
+                Slog.e(mTag, "failed to remove uids " + startUid + " - " + endUid
+                        + " from uid_cputime module", e);
+            } finally {
+                StrictMode.setThreadPolicyMask(oldMask);
+            }
+        }
+    }
+
+    /**
+     * Reads /proc/uid_time_in_state which has the format:
+     *
+     * uid: [freq1] [freq2] [freq3] ...
+     * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
+     * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
+     * ...
+     *
+     * This provides the times a UID's processes spent executing at each different cpu frequency.
+     * The file contains a monotonically increasing count of time for a single boot. This class
+     * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+     * delta.
+     */
+    public static class KernelCpuUidFreqTimeReader extends KernelCpuUidTimeReader<long[]> {
+        private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
+        // We check the existence of proc file a few times (just in case it is not ready yet when we
+        // start reading) and if it is not available, we simply ignore further read requests.
+        private static final int MAX_ERROR_COUNT = 5;
+
+        private final Path mProcFilePath;
+        private long[] mBuffer;
+        private long[] mCurTimes;
+        private long[] mDeltaTimes;
+        private long[] mCpuFreqs;
+
+        private int mFreqCount = 0;
+        private int mErrors = 0;
+        private boolean mPerClusterTimesAvailable;
+        private boolean mAllUidTimesAvailable = true;
+
+        public KernelCpuUidFreqTimeReader(boolean throttle) {
+            this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
+                    throttle);
+        }
+
+        @VisibleForTesting
+        public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
+                boolean throttle) {
+            super(reader, throttle);
+            mProcFilePath = Paths.get(procFile);
+        }
+
+        /**
+         * @return Whether per-cluster times are available.
+         */
+        public boolean perClusterTimesAvailable() {
+            return mPerClusterTimesAvailable;
+        }
+
+        /**
+         * @return Whether all-UID times are available.
+         */
+        public boolean allUidTimesAvailable() {
+            return mAllUidTimesAvailable;
+        }
+
+        /**
+         * @return A map of all UIDs to their CPU time-in-state array in milliseconds.
+         */
+        public SparseArray<long[]> getAllUidCpuFreqTimeMs() {
+            return mLastTimes;
+        }
+
+        /**
+         * Reads a list of CPU frequencies from /proc/uid_time_in_state. Uses a given PowerProfile
+         * to determine if per-cluster times are available.
+         *
+         * @param powerProfile The PowerProfile to compare against.
+         * @return A long[] of CPU frequencies in Hz.
+         */
+        public long[] readFreqs(@NonNull PowerProfile powerProfile) {
+            checkNotNull(powerProfile);
+            if (mCpuFreqs != null) {
+                // No need to read cpu freqs more than once.
+                return mCpuFreqs;
+            }
+            if (!mAllUidTimesAvailable) {
+                return null;
+            }
+            final int oldMask = StrictMode.allowThreadDiskReadsMask();
+            try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
+                if (readFreqs(reader.readLine()) == null) {
+                    return null;
+                }
+            } catch (IOException e) {
+                if (++mErrors >= MAX_ERROR_COUNT) {
+                    mAllUidTimesAvailable = false;
+                }
+                Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+                return null;
+            } finally {
+                StrictMode.setThreadPolicyMask(oldMask);
+            }
+            // Check if the freqs in the proc file correspond to per-cluster freqs.
+            final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
+            final int numClusters = powerProfile.getNumCpuClusters();
+            if (numClusterFreqs.size() == numClusters) {
+                mPerClusterTimesAvailable = true;
+                for (int i = 0; i < numClusters; ++i) {
+                    if (numClusterFreqs.get(i) != powerProfile.getNumSpeedStepsInCpuCluster(i)) {
+                        mPerClusterTimesAvailable = false;
+                        break;
+                    }
+                }
+            } else {
+                mPerClusterTimesAvailable = false;
+            }
+            Slog.i(mTag, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
+            return mCpuFreqs;
+        }
+
+        private long[] readFreqs(String line) {
+            if (line == null) {
+                return null;
+            }
+            final String[] lineArray = line.split(" ");
+            if (lineArray.length <= 1) {
+                Slog.wtf(mTag, "Malformed freq line: " + line);
+                return null;
+            }
+            mFreqCount = lineArray.length - 1;
+            mCpuFreqs = new long[mFreqCount];
+            mCurTimes = new long[mFreqCount];
+            mDeltaTimes = new long[mFreqCount];
+            mBuffer = new long[mFreqCount + 1];
+            for (int i = 0; i < mFreqCount; ++i) {
+                mCpuFreqs[i] = Long.parseLong(lineArray[i + 1], 10);
+            }
+            return mCpuFreqs;
+        }
+
+        @Override
+        void readDeltaImpl(@Nullable Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    final int uid = (int) mBuffer[0];
+                    long[] lastTimes = mLastTimes.get(uid);
+                    if (lastTimes == null) {
+                        lastTimes = new long[mFreqCount];
+                        mLastTimes.put(uid, lastTimes);
+                    }
+                    copyToCurTimes();
+                    boolean notify = false;
+                    boolean valid = true;
+                    for (int i = 0; i < mFreqCount; i++) {
+                        // Unit is 10ms.
+                        mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
+                        if (mDeltaTimes[i] < 0) {
+                            Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+                            valid = false;
+                        }
+                        notify |= mDeltaTimes[i] > 0;
+                    }
+                    if (notify && valid) {
+                        System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
+                        if (cb != null) {
+                            cb.onUidCpuTime(uid, mDeltaTimes);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        void readAbsoluteImpl(Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    copyToCurTimes();
+                    cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
+                }
+            }
+        }
+
+        private void copyToCurTimes() {
+            for (int i = 0; i < mFreqCount; i++) {
+                mCurTimes[i] = mBuffer[i + 1] * 10;
+            }
+        }
+
+        private boolean checkPrecondition(ProcFileIterator iter) {
+            if (iter == null || !iter.hasNextLine()) {
+                // Error logged in KernelCpuProcStringReader.
+                return false;
+            }
+            CharBuffer line = iter.nextLine();
+            if (mCpuFreqs != null) {
+                return true;
+            }
+            return readFreqs(line.toString()) != null;
+        }
+
+        /**
+         * Extracts no. of cpu clusters and no. of freqs in each of these clusters from the freqs
+         * read from the proc file.
+         *
+         * We need to assume that freqs in each cluster are strictly increasing.
+         * For e.g. if the freqs read from proc file are: 12, 34, 15, 45, 12, 15, 52. Then it means
+         * there are 3 clusters: (12, 34), (15, 45), (12, 15, 52)
+         *
+         * @return an IntArray filled with no. of freqs in each cluster.
+         */
+        private IntArray extractClusterInfoFromProcFileFreqs() {
+            final IntArray numClusterFreqs = new IntArray();
+            int freqsFound = 0;
+            for (int i = 0; i < mFreqCount; ++i) {
+                freqsFound++;
+                if (i + 1 == mFreqCount || mCpuFreqs[i + 1] <= mCpuFreqs[i]) {
+                    numClusterFreqs.add(freqsFound);
+                    freqsFound = 0;
+                }
+            }
+            return numClusterFreqs;
+        }
+    }
+
+    /**
+     * Reads /proc/uid_concurrent_active_time and reports CPU active time to BatteryStats to
+     * compute {@link PowerProfile#POWER_CPU_ACTIVE}.
+     *
+     * /proc/uid_concurrent_active_time has the following format:
+     * cpus: n
+     * uid0: time0a, time0b, ..., time0n,
+     * uid1: time1a, time1b, ..., time1n,
+     * uid2: time2a, time2b, ..., time2n,
+     * ...
+     * where n is the total number of cpus (num_possible_cpus)
+     * timeXn means the CPU time that a UID X spent running concurrently with n other processes.
+     *
+     * The file contains a monotonically increasing count of time for a single boot. This class
+     * maintains the previous results of a call to {@link #readDelta} in order to provide a
+     * proper delta.
+     */
+    public static class KernelCpuUidActiveTimeReader extends KernelCpuUidTimeReader<Long> {
+        private int mCores = 0;
+        private long[] mBuffer;
+
+        public KernelCpuUidActiveTimeReader(boolean throttle) {
+            super(KernelCpuProcStringReader.getActiveTimeReaderInstance(), throttle);
+        }
+
+        @VisibleForTesting
+        public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+            super(reader, throttle);
+        }
+
+        @Override
+        void readDeltaImpl(@Nullable Callback<Long> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    int uid = (int) mBuffer[0];
+                    long cpuActiveTime = sumActiveTime(mBuffer);
+                    if (cpuActiveTime > 0) {
+                        long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
+                        if (delta > 0) {
+                            mLastTimes.put(uid, cpuActiveTime);
+                            if (cb != null) {
+                                cb.onUidCpuTime(uid, delta);
+                            }
+                        } else if (delta < 0) {
+                            Slog.e(mTag, "Negative delta from active time proc: " + delta);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        void readAbsoluteImpl(Callback<Long> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    long cpuActiveTime = sumActiveTime(mBuffer);
+                    if (cpuActiveTime > 0) {
+                        cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
+                    }
+                }
+            }
+        }
+
+        private static long sumActiveTime(long[] times) {
+            // UID is stored at times[0].
+            double sum = 0;
+            for (int i = 1; i < times.length; i++) {
+                sum += (double) times[i] * 10 / i; // Unit is 10ms.
+            }
+            return (long) sum;
+        }
+
+        private boolean checkPrecondition(ProcFileIterator iter) {
+            if (iter == null || !iter.hasNextLine()) {
+                // Error logged in KernelCpuProcStringReader.
+                return false;
+            }
+            CharBuffer line = iter.nextLine();
+            if (mCores > 0) {
+                return true;
+            }
+
+            String str = line.toString();
+            if (!str.startsWith("cpus:")) {
+                Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + line);
+                return false;
+            }
+            int cores = Integer.parseInt(str.substring(5).trim(), 10);
+            if (cores <= 0) {
+                Slog.wtf(mTag, "Malformed uid_concurrent_active_time line: " + line);
+                return false;
+            }
+            mCores = cores;
+            mBuffer = new long[mCores + 1]; // UID is stored at mBuffer[0].
+            return true;
+        }
+    }
+
+
+    /**
+     * Reads /proc/uid_concurrent_policy_time and reports CPU cluster times to BatteryStats to
+     * compute cluster power. See {@link PowerProfile#getAveragePowerForCpuCluster(int)}.
+     *
+     * /proc/uid_concurrent_policy_time has the following format:
+     * policyX: x policyY: y policyZ: z...
+     * uid1, time1a, time1b, ..., time1n,
+     * uid2, time2a, time2b, ..., time2n,
+     * ...
+     * The first line lists all policies (i.e. clusters) followed by # cores in each policy.
+     * Each uid is followed by x time entries corresponding to the time it spent on clusterX
+     * running concurrently with 0, 1, 2, ..., x - 1 other processes, then followed by y, z, ...
+     * time entries.
+     *
+     * The file contains a monotonically increasing count of time for a single boot. This class
+     * maintains the previous results of a call to {@link #readDelta} in order to provide a
+     * proper delta.
+     */
+    public static class KernelCpuUidClusterTimeReader extends KernelCpuUidTimeReader<long[]> {
+        private int mNumClusters;
+        private int mNumCores;
+        private int[] mCoresOnClusters; // # cores on each cluster.
+        private long[] mBuffer; // To store data returned from ProcFileIterator.
+        private long[] mCurTime;
+        private long[] mDeltaTime;
+
+        public KernelCpuUidClusterTimeReader(boolean throttle) {
+            super(KernelCpuProcStringReader.getClusterTimeReaderInstance(), throttle);
+        }
+
+        @VisibleForTesting
+        public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+            super(reader, throttle);
+        }
+
+        @Override
+        void readDeltaImpl(@Nullable Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    int uid = (int) mBuffer[0];
+                    long[] lastTimes = mLastTimes.get(uid);
+                    if (lastTimes == null) {
+                        lastTimes = new long[mNumClusters];
+                        mLastTimes.put(uid, lastTimes);
+                    }
+                    sumClusterTime();
+                    boolean valid = true;
+                    boolean notify = false;
+                    for (int i = 0; i < mNumClusters; i++) {
+                        mDeltaTime[i] = mCurTime[i] - lastTimes[i];
+                        if (mDeltaTime[i] < 0) {
+                            Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+                            valid = false;
+                        }
+                        notify |= mDeltaTime[i] > 0;
+                    }
+                    if (notify && valid) {
+                        System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+                        if (cb != null) {
+                            cb.onUidCpuTime(uid, mDeltaTime);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        void readAbsoluteImpl(Callback<long[]> cb) {
+            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+                if (!checkPrecondition(iter)) {
+                    return;
+                }
+                CharBuffer buf;
+                while ((buf = iter.nextLine()) != null) {
+                    if (asLongs(buf, mBuffer) != mBuffer.length) {
+                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
+                        continue;
+                    }
+                    sumClusterTime();
+                    cb.onUidCpuTime((int) mBuffer[0], mCurTime);
+                }
+            }
+        }
+
+        private void sumClusterTime() {
+            // UID is stored at mBuffer[0].
+            int core = 1;
+            for (int i = 0; i < mNumClusters; i++) {
+                double sum = 0;
+                for (int j = 1; j <= mCoresOnClusters[i]; j++) {
+                    sum += (double) mBuffer[core++] * 10 / j; // Unit is 10ms.
+                }
+                mCurTime[i] = (long) sum;
+            }
+        }
+
+        private boolean checkPrecondition(ProcFileIterator iter) {
+            if (iter == null || !iter.hasNextLine()) {
+                // Error logged in KernelCpuProcStringReader.
+                return false;
+            }
+            CharBuffer line = iter.nextLine();
+            if (mNumClusters > 0) {
+                return true;
+            }
+            // Parse # cores in clusters.
+            String[] lineArray = line.toString().split(" ");
+            if (lineArray.length % 2 != 0) {
+                Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + line);
+                return false;
+            }
+            int[] clusters = new int[lineArray.length / 2];
+            int cores = 0;
+            for (int i = 0; i < clusters.length; i++) {
+                if (!lineArray[i * 2].startsWith("policy")) {
+                    Slog.wtf(mTag, "Malformed uid_concurrent_policy_time line: " + line);
+                    return false;
+                }
+                clusters[i] = Integer.parseInt(lineArray[i * 2 + 1], 10);
+                cores += clusters[i];
+            }
+            mNumClusters = clusters.length;
+            mNumCores = cores;
+            mCoresOnClusters = clusters;
+            mBuffer = new long[cores + 1];
+            mCurTime = new long[mNumClusters];
+            mDeltaTime = new long[mNumClusters];
+            return true;
+        }
+    }
+
+}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 2b661c2..cf2a297 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -50,6 +50,7 @@
     private int mSamplingInterval;
     private CachedDeviceState.Readonly mDeviceState;
     private long mStartTime = System.currentTimeMillis();
+    private boolean mAddDebugEntries = false;
 
     public LooperStats(int samplingInterval, int entriesSizeCap) {
         this.mSamplingInterval = samplingInterval;
@@ -60,6 +61,10 @@
         mDeviceState = deviceState;
     }
 
+    public void setAddDebugEntries(boolean addDebugEntries) {
+        mAddDebugEntries = addDebugEntries;
+    }
+
     @Override
     public Object messageDispatchStarting() {
         if (deviceStateAllowsCollection() && shouldCollectDetailedData()) {
@@ -142,9 +147,22 @@
         // Add the overflow and collision entries only if they have any data.
         maybeAddSpecialEntry(exportedEntries, mOverflowEntry);
         maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
+        // Debug entries added to help validate the data.
+        if (mAddDebugEntries) {
+            exportedEntries.add(createDebugEntry("start_time_millis", mStartTime));
+            exportedEntries.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+        }
         return exportedEntries;
     }
 
+    private ExportedEntry createDebugEntry(String variableName, long value) {
+        final Entry entry = new Entry("__DEBUG_" + variableName);
+        entry.messageCount = 1;
+        entry.recordedMessageCount = 1;
+        entry.maxDelayMillis = value;
+        return new ExportedEntry(entry);
+    }
+
     /** Returns a timestamp indicating when the statistics were last reset. */
     public long getStartTimeMillis() {
         return mStartTime;
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index b9d53c1..d78bfac 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -120,6 +120,8 @@
         arg5 = null;
         arg6 = null;
         arg7 = null;
+        arg8 = null;
+        arg9 = null;
         argi1 = 0;
         argi2 = 0;
         argi3 = 0;
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 9a7094a..b7ffb57 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -16,6 +16,7 @@
 
 package com.android.internal.statusbar;
 
+import android.app.Notification;
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -55,7 +56,7 @@
     // Mark current notifications as "seen" and stop ringing, vibrating, blinking.
     void clearNotificationEffects();
     void onNotificationClick(String key, in NotificationVisibility nv);
-    void onNotificationActionClick(String key, int actionIndex, in NotificationVisibility nv);
+    void onNotificationActionClick(String key, int actionIndex, in Notification.Action action, in NotificationVisibility nv, boolean generatedByAssistant);
     void onNotificationError(String pkg, String tag, int id,
             int uid, int initialPid, String message, int userId);
     void onClearAllNotifications(int userId);
@@ -66,7 +67,7 @@
     void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
     void onNotificationDirectReplied(String key);
     void onNotificationSmartRepliesAdded(in String key, in int replyCount);
-    void onNotificationSmartReplySent(in String key, in int replyIndex);
+    void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant);
     void onNotificationSettingsViewed(String key);
     void setSystemUiVisibility(int vis, int mask, String cause);
 
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 5da587b..344d7ef 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -63,7 +63,7 @@
     public static final int
     convertValueToList(CharSequence value, String[] options, int defaultValue)
     {
-        if (null != value) {
+        if (!TextUtils.isEmpty(value)) {
             for (int i = 0; i < options.length; i++) {
                 if (value.equals(options[i]))
                     return i;
@@ -79,8 +79,9 @@
     {
         boolean result = false;
 
-        if (null == value)
+        if (TextUtils.isEmpty(value)) {
             return defaultValue;
+        }
 
         if (value.equals("1")
         ||  value.equals("true")
@@ -94,8 +95,9 @@
     public static final int
     convertValueToInt(CharSequence charSeq, int defaultValue)
     {
-        if (null == charSeq)
+        if (TextUtils.isEmpty(charSeq)) {
             return defaultValue;
+        }
 
         String nm = charSeq.toString();
 
@@ -138,7 +140,7 @@
     }
 
     public static int convertValueToUnsignedInt(String value, int defaultValue) {
-        if (null == value) {
+        if (TextUtils.isEmpty(value)) {
             return defaultValue;
         }
 
@@ -1674,7 +1676,7 @@
     public static boolean readBooleanAttribute(XmlPullParser in, String name,
             boolean defaultValue) {
         final String value = in.getAttributeValue(null, name);
-        if (value == null) {
+        if (TextUtils.isEmpty(value)) {
             return defaultValue;
         } else {
             return Boolean.parseBoolean(value);
@@ -1711,7 +1713,7 @@
 
     public static byte[] readByteArrayAttribute(XmlPullParser in, String name) {
         final String value = in.getAttributeValue(null, name);
-        if (value != null) {
+        if (!TextUtils.isEmpty(value)) {
             return Base64.decode(value, Base64.DEFAULT);
         } else {
             return null;
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 137ca7f..36fe4fc 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -27,6 +27,7 @@
 import android.view.IWindow;
 import android.view.IWindowSession;
 import android.view.PointerIcon;
+import android.view.InsetsState;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -53,6 +54,10 @@
     }
 
     @Override
+    public void insetsChanged(InsetsState insetsState) {
+    }
+
+    @Override
     public void moved(int newX, int newY) {
     }
 
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 7635a72..b7e656b 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -814,7 +814,14 @@
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (lp.alwaysShow && child.getVisibility() != GONE) {
-                measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+                if (lp.maxHeight != -1) {
+                    final int remainingHeight = heightSize - heightUsed;
+                    measureChildWithMargins(child, widthSpec, widthPadding,
+                            MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST),
+                            lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0);
+                } else {
+                    measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+                }
                 heightUsed += child.getMeasuredHeight();
             }
         }
@@ -824,9 +831,17 @@
         // And now the rest.
         for (int i = 0; i < childCount; i++) {
             final View child = getChildAt(i);
+
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (!lp.alwaysShow && child.getVisibility() != GONE) {
-                measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+                if (lp.maxHeight != -1) {
+                    final int remainingHeight = heightSize - heightUsed;
+                    measureChildWithMargins(child, widthSpec, widthPadding,
+                            MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST),
+                            lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0);
+                } else {
+                    measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+                }
                 heightUsed += child.getMeasuredHeight();
             }
         }
@@ -938,6 +953,7 @@
         public boolean alwaysShow;
         public boolean ignoreOffset;
         public boolean hasNestedScrollIndicator;
+        public int maxHeight;
 
         public LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
@@ -953,6 +969,8 @@
             hasNestedScrollIndicator = a.getBoolean(
                     R.styleable.ResolverDrawerLayout_LayoutParams_layout_hasNestedScrollIndicator,
                     false);
+            maxHeight = a.getDimensionPixelSize(
+                    R.styleable.ResolverDrawerLayout_LayoutParams_layout_maxHeight, -1);
             a.recycle();
         }
 
@@ -965,6 +983,7 @@
             this.alwaysShow = source.alwaysShow;
             this.ignoreOffset = source.ignoreOffset;
             this.hasNestedScrollIndicator = source.hasNestedScrollIndicator;
+            this.maxHeight = source.maxHeight;
         }
 
         public LayoutParams(MarginLayoutParams source) {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 15745e9..8495850 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -25,6 +25,7 @@
 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;
@@ -930,6 +931,16 @@
                 XmlUtils.skipCurrentTag(parser);
             }
         }
+        // If the storage model feature flag is disabled, we need to fiddle
+        // around with permission definitions to return us to pre-Q behavior.
+        // STOPSHIP(b/112545973): remove once feature enabled by default
+        if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+            if (newPermissions.contains(android.Manifest.permission.READ_MEDIA_AUDIO) ||
+                    newPermissions.contains(android.Manifest.permission.READ_MEDIA_VIDEO) ||
+                    newPermissions.contains(android.Manifest.permission.READ_MEDIA_IMAGES)) {
+                return;
+            }
+        }
         if (!newPermissions.isEmpty()) {
             mSplitPermissions.add(new SplitPermissionInfo(splitPerm, newPermissions, targetSdk));
         }
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index eada690..bc1d5cc 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1450,8 +1450,8 @@
     REG_JNI(register_android_hardware_UsbDeviceConnection),
     REG_JNI(register_android_hardware_UsbRequest),
     REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
-    REG_JNI(register_android_media_AudioRecord),
     REG_JNI(register_android_media_AudioSystem),
+    REG_JNI(register_android_media_AudioRecord),
     REG_JNI(register_android_media_AudioTrack),
     REG_JNI(register_android_media_JetPlayer),
     REG_JNI(register_android_media_MicrophoneInfo),
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 68f5bef..ed6a84b 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -64,11 +64,10 @@
         jint tileModeX, jint tileModeY) {
     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
     sk_sp<SkImage> image;
-    sk_sp<SkColorFilter> colorFilter;
     if (jbitmap) {
         // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
         // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
-        image = android::bitmap::toBitmap(env, jbitmap).makeImage(&colorFilter);
+        image = android::bitmap::toBitmap(env, jbitmap).makeImage();
     }
 
     if (!image.get()) {
@@ -81,9 +80,6 @@
     if (matrix) {
         shader = shader->makeWithLocalMatrix(*matrix);
     }
-    if(colorFilter) {
-        shader = shader->makeWithColorFilter(colorFilter);
-    }
 
     ThrowIAE_IfNull(env, shader.get());
     return reinterpret_cast<jlong>(shader.release());
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index e02741f..719cf74 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -514,14 +514,14 @@
 
     sp<ANativeWindow> anw;
     if ((anw = getNativeWindow(env, surface)) == NULL) {
-        jniThrowException(env, "java/lang/UnsupportedOperationException;",
+        jniThrowException(env, "java/lang/UnsupportedOperationException",
             "Could not retrieve native window from surface.");
         return BAD_VALUE;
     }
     int32_t usage = 0;
     status_t err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage);
     if(err != NO_ERROR) {
-        jniThrowException(env, "java/lang/UnsupportedOperationException;",
+        jniThrowException(env, "java/lang/UnsupportedOperationException",
             "Error while querying surface usage bits");
         OVERRIDE_SURFACE_ERROR(err);
         return err;
@@ -542,7 +542,7 @@
 
     status_t err = native_window_api_disconnect(anw.get(), NATIVE_WINDOW_API_CAMERA);
     if(err != NO_ERROR) {
-        jniThrowException(env, "java/lang/UnsupportedOperationException;",
+        jniThrowException(env, "java/lang/UnsupportedOperationException",
             "Error while disconnecting surface");
         OVERRIDE_SURFACE_ERROR(err);
         return err;
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 5887fa7..10005dd 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -22,6 +22,7 @@
 #include <utils/threads.h>
 
 #include "android_hardware_input_InputApplicationHandle.h"
+#include "android_util_Binder.h"
 
 namespace android {
 
@@ -29,6 +30,7 @@
     jfieldID ptr;
     jfieldID name;
     jfieldID dispatchingTimeoutNanos;
+    jfieldID token;
 } gInputApplicationHandleClassInfo;
 
 static Mutex gHandleMutex;
@@ -75,6 +77,15 @@
     mInfo->dispatchingTimeout = env->GetLongField(obj,
             gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
 
+    jobject tokenObj = env->GetObjectField(obj,
+            gInputApplicationHandleClassInfo.token);
+    if (tokenObj) {
+        mInfo->token = ibinderForJavaObject(env, tokenObj);
+        env->DeleteLocalRef(tokenObj);
+    } else {
+        mInfo->token.clear();
+    }
+
     env->DeleteLocalRef(obj);
     return true;
 }
@@ -153,6 +164,9 @@
             clazz,
             "dispatchingTimeoutNanos", "J");
 
+    GET_FIELD_ID(gInputApplicationHandleClassInfo.token, clazz,
+            "token", "Landroid/os/IBinder;");
+
     return 0;
 }
 
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 6ecb5de..76920f5 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -21,19 +21,19 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/threads.h>
 
-#include <android_view_InputChannel.h>
 #include <android/graphics/Region.h>
 #include <ui/Region.h>
 
 #include "android_hardware_input_InputWindowHandle.h"
 #include "android_hardware_input_InputApplicationHandle.h"
+#include "android_util_Binder.h"
 
 namespace android {
 
 static struct {
     jfieldID ptr;
     jfieldID inputApplicationHandle;
-    jfieldID inputChannel;
+    jfieldID token;
     jfieldID name;
     jfieldID layoutParamsFlags;
     jfieldID layoutParamsType;
@@ -42,6 +42,7 @@
     jfieldID frameTop;
     jfieldID frameRight;
     jfieldID frameBottom;
+    jfieldID surfaceInset;
     jfieldID scaleFactor;
     jfieldID touchableRegion;
     jfieldID visible;
@@ -61,9 +62,7 @@
 
 // --- NativeInputWindowHandle ---
 
-NativeInputWindowHandle::NativeInputWindowHandle(
-        const sp<InputApplicationHandle>& inputApplicationHandle, jweak objWeak) :
-        InputWindowHandle(inputApplicationHandle),
+NativeInputWindowHandle::NativeInputWindowHandle(jweak objWeak) :
         mObjWeak(objWeak) {
 }
 
@@ -86,13 +85,12 @@
 
     mInfo.touchableRegion.clear();
 
-    jobject inputChannelObj = env->GetObjectField(obj,
-            gInputWindowHandleClassInfo.inputChannel);
-    if (inputChannelObj) {
-        mInfo.inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
-        env->DeleteLocalRef(inputChannelObj);
+    jobject tokenObj = env->GetObjectField(obj,
+            gInputWindowHandleClassInfo.token);
+    if (tokenObj) {
+        mInfo.token = ibinderForJavaObject(env, tokenObj);
     } else {
-        mInfo.inputChannel.clear();
+        mInfo.token.clear();
     }
 
     jstring nameObj = jstring(env->GetObjectField(obj,
@@ -120,7 +118,9 @@
             gInputWindowHandleClassInfo.frameRight);
     mInfo.frameBottom = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameBottom);
-    mInfo.scaleFactor = env->GetFloatField(obj,
+    mInfo.surfaceInset = env->GetIntField(obj,
+            gInputWindowHandleClassInfo.surfaceInset);
+    mInfo.globalScaleFactor = env->GetFloatField(obj,
             gInputWindowHandleClassInfo.scaleFactor);
 
     jobject regionObj = env->GetObjectField(obj,
@@ -155,6 +155,18 @@
     mInfo.displayId = env->GetIntField(obj,
             gInputWindowHandleClassInfo.displayId);
 
+    jobject inputApplicationHandleObj = env->GetObjectField(obj,
+            gInputWindowHandleClassInfo.inputApplicationHandle);
+    if (inputApplicationHandleObj) {
+        sp<InputApplicationHandle> inputApplicationHandle =
+            android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
+        if (inputApplicationHandle != nullptr) {
+            inputApplicationHandle->updateInfo();
+            mInfo.applicationInfo = *(inputApplicationHandle->getInfo());
+        }
+        env->DeleteLocalRef(inputApplicationHandleObj);
+    }
+
     env->DeleteLocalRef(obj);
     return true;
 }
@@ -175,14 +187,8 @@
     if (ptr) {
         handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);
     } else {
-        jobject inputApplicationHandleObj = env->GetObjectField(inputWindowHandleObj,
-                gInputWindowHandleClassInfo.inputApplicationHandle);
-        sp<InputApplicationHandle> inputApplicationHandle =
-                android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
-        env->DeleteLocalRef(inputApplicationHandleObj);
-
         jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj);
-        handle = new NativeInputWindowHandle(inputApplicationHandle, objWeak);
+        handle = new NativeInputWindowHandle(objWeak);
         handle->incStrong((void*)android_server_InputWindowHandle_getHandle);
         env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr,
                 reinterpret_cast<jlong>(handle));
@@ -236,8 +242,8 @@
             clazz,
             "inputApplicationHandle", "Landroid/view/InputApplicationHandle;");
 
-    GET_FIELD_ID(gInputWindowHandleClassInfo.inputChannel, clazz,
-            "inputChannel", "Landroid/view/InputChannel;");
+    GET_FIELD_ID(gInputWindowHandleClassInfo.token, clazz,
+            "token", "Landroid/os/IBinder;");
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.name, clazz,
             "name", "Ljava/lang/String;");
@@ -263,6 +269,9 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.frameBottom, clazz,
             "frameBottom", "I");
 
+    GET_FIELD_ID(gInputWindowHandleClassInfo.surfaceInset, clazz,
+            "surfaceInset", "I");
+
     GET_FIELD_ID(gInputWindowHandleClassInfo.scaleFactor, clazz,
             "scaleFactor", "F");
 
diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
index 2be267e..54b89f5 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -26,8 +26,7 @@
 
 class NativeInputWindowHandle : public InputWindowHandle {
 public:
-    NativeInputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
-            jweak objWeak);
+    NativeInputWindowHandle(jweak objWeak);
     virtual ~NativeInputWindowHandle();
 
     jobject getInputWindowHandleObjLocalRef(JNIEnv* env);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7410b52..adab8e2 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -175,6 +175,17 @@
     jmethodID postRecordConfigEventFromNative;
 } gAudioPolicyEventHandlerMethods;
 
+//
+// JNI Initialization for OpenSLES routing
+//
+jmethodID gMidAudioTrackRoutingProxy_ctor;
+jmethodID gMidAudioTrackRoutingProxy_release;
+jmethodID gMidAudioRecordRoutingProxy_ctor;
+jmethodID gMidAudioRecordRoutingProxy_release;
+
+jclass gClsAudioTrackRoutingProxy;
+jclass gClsAudioRecordRoutingProxy;
+
 static Mutex gLock;
 
 enum AudioError {
@@ -2017,6 +2028,39 @@
     return (jint)nativeToJavaStatus(status);
 }
 
+static jint android_media_AudioSystem_get_FCC_8(JNIEnv *env, jobject thiz) {
+    return FCC_8;
+}
+
+static jint
+android_media_AudioSystem_setAssistantUid(JNIEnv *env, jobject thiz, jint uid)
+{
+    status_t status = AudioSystem::setAssistantUid(uid);
+    return (jint)nativeToJavaStatus(status);
+}
+
+static jint
+android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArray uids) {
+    std::vector<uid_t> nativeUidsVector;
+
+    if (uids != nullptr) {
+       jsize len = env->GetArrayLength(uids);
+
+       if (len > 0) {
+           int *nativeUids = nullptr;
+           nativeUids = env->GetIntArrayElements(uids, 0);
+           if (nativeUids != nullptr) {
+               for (size_t i = 0; i < len; i++) {
+                   nativeUidsVector.push_back(nativeUids[i]);
+               }
+               env->ReleaseIntArrayElements(uids, nativeUids, 0);
+           }
+       }
+    }
+    status_t status = AudioSystem::setA11yServicesUids(nativeUidsVector);
+    return (jint)nativeToJavaStatus(status);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] = {
@@ -2077,9 +2121,10 @@
     {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones},
     {"getSurroundFormats", "(Ljava/util/Map;Z)I", (void *)android_media_AudioSystem_getSurroundFormats},
     {"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled},
+    {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
+    {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
 };
 
-
 static const JNINativeMethod gEventHandlerMethods[] = {
     {"native_setup",
         "(Ljava/lang/Object;)V",
@@ -2089,8 +2134,15 @@
         (void *)android_media_AudioSystem_eventHandlerFinalize},
 };
 
+static const JNINativeMethod gGetFCC8Methods[] = {
+    {"native_get_FCC_8", "()I", (void *)android_media_AudioSystem_get_FCC_8},
+};
+
 int register_android_media_AudioSystem(JNIEnv *env)
 {
+    // This needs to be done before hooking up methods AudioTrackRoutingProxy (below)
+    RegisterMethodsOrDie(env, kClassPathName, gGetFCC8Methods, NELEM(gGetFCC8Methods));
+
     jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
     gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
     gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
@@ -2247,6 +2299,28 @@
     gAudioAttributesFields.mFormattedTags = GetFieldIDOrDie(env,
             audioAttributesClass, "mFormattedTags", "Ljava/lang/String;");
 
+    // AudioTrackRoutingProxy methods
+    gClsAudioTrackRoutingProxy =
+            android::FindClassOrDie(env, "android/media/AudioTrackRoutingProxy");
+    // make sure this reference doesn't get deleted
+    gClsAudioTrackRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioTrackRoutingProxy);
+
+    gMidAudioTrackRoutingProxy_ctor =
+            android::GetMethodIDOrDie(env, gClsAudioTrackRoutingProxy, "<init>", "(J)V");
+    gMidAudioTrackRoutingProxy_release =
+            android::GetMethodIDOrDie(env, gClsAudioTrackRoutingProxy, "native_release", "()V");
+
+    // AudioRecordRoutingProxy
+    gClsAudioRecordRoutingProxy =
+            android::FindClassOrDie(env, "android/media/AudioRecordRoutingProxy");
+    // make sure this reference doesn't get deleted
+    gClsAudioRecordRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioRecordRoutingProxy);
+
+    gMidAudioRecordRoutingProxy_ctor =
+            android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "<init>", "(J)V");
+    gMidAudioRecordRoutingProxy_release =
+            android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "native_release", "()V");
+
     AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);
 
     RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index bf22dd2..04f0a53 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1226,10 +1226,6 @@
     pJniStorage->mDeviceCallback.clear();
 }
 
-static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
-    return FCC_8;
-}
-
 // Pass through the arguments to the AudioFlinger track implementation.
 static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz,
         jobject jconfig, jobject joperation) {
@@ -1351,7 +1347,6 @@
     {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
     {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
     {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
-    {"native_get_FCC_8",     "()I",      (void *)android_media_AudioTrack_get_FCC_8},
     {"native_applyVolumeShaper",
             "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
                                          (void *)android_media_AudioTrack_apply_volume_shaper},
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 7de8020..d917536 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -550,6 +550,10 @@
     }
     optional MultiSim multi_sim = 76;
 
+    // Whether we've enabled native flags health check on this device. Takes effect on
+    // reboot. The value "1" enables native flags health check; otherwise it's disabled.
+    optional SettingProto native_flags_health_check_enabled = 144 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     message Netstats {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -987,5 +991,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 144;
+    // Next tag = 145;
 }
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 99f096d..679a1d2 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -156,7 +156,7 @@
     optional int32 rotation = 11;
     optional ScreenRotationAnimationProto screen_rotation_animation = 12;
     optional DisplayFramesProto display_frames = 13;
-    optional int32 surface_size = 14;
+    optional int32 surface_size = 14 [deprecated=true];
     optional string focused_app = 15;
     optional AppTransitionProto app_transition = 16;
 }
diff --git a/core/proto/android/stats/devicepolicy/Android.bp b/core/proto/android/stats/devicepolicy/Android.bp
new file mode 100644
index 0000000..6ae54e2
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/Android.bp
@@ -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.
+
+java_library_static {
+    name: "devicepolicyprotosnano",
+    proto: {
+        type: "nano",
+    },
+    srcs: [
+        "*.proto",
+    ],
+    java_version: "1.8",
+    target: {
+        android: {
+            jarjar_rules: "jarjar-rules.txt",
+        },
+        host: {
+            static_libs: ["libprotobuf-java-nano"],
+        }
+    },
+    no_framework_libs: true,
+}
diff --git a/core/proto/android/stats/devicepolicy/device_policy.proto b/core/proto/android/stats/devicepolicy/device_policy.proto
new file mode 100644
index 0000000..af30cf3
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/device_policy.proto
@@ -0,0 +1,24 @@
+/*
+ * 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.devicepolicy;
+option java_multiple_files = true;
+
+message StringList {
+  repeated string string_value = 1;
+}
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
new file mode 100644
index 0000000..8fbea12
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -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.
+ */
+
+syntax = "proto2";
+
+package android.stats.devicepolicy;
+option java_multiple_files = true;
+
+/**
+ * Id for device policy features.
+ */
+enum EventId {
+  SET_PASSWORD_QUALITY = 1;
+  SET_PASSWORD_MINIMUM_LENGTH = 2;
+  SET_PASSWORD_MINIMUM_NUMERIC = 3;
+  SET_PASSWORD_MINIMUM_NON_LETTER = 4;
+  SET_PASSWORD_MINIMUM_LETTERS = 5;
+  SET_PASSWORD_MINIMUM_LOWER_CASE = 6;
+  SET_PASSWORD_MINIMUM_UPPER_CASE = 7;
+  SET_PASSWORD_MINIMUM_SYMBOLS = 8;
+  SET_KEYGUARD_DISABLED_FEATURES = 9;
+  LOCK_NOW = 10;
+  WIPE_DATA_WITH_REASON = 11;
+  ADD_USER_RESTRICTION = 12;
+  REMOVE_USER_RESTRICTION = 13;
+  SET_SECURE_SETTING = 14;
+  SET_SECURITY_LOGGING_ENABLED = 15;
+  RETRIEVE_SECURITY_LOGS = 16;
+  RETRIEVE_PRE_REBOOT_SECURITY_LOGS = 17;
+  SET_PERMISSION_POLICY = 18;
+  SET_PERMISSION_GRANT_STATE = 19;
+  INSTALL_KEY_PAIR = 20;
+  INSTALL_CA_CERT = 21;
+  ON_CHOOSE_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_CAMERA_DISABLED = 30;
+  QUERY_SUMMARY_FOR_USER = 31;
+  QUERY_SUMMARY = 32;
+  QUERY_DETAILS = 33;
+  REBOOT = 34;
+  SET_MASTER_VOLUME_MUTED = 35;
+  SET_AUTO_TIME_REQUIRED = 36;
+  SET_KEYGUARD_DISABLED = 37;
+  SET_STATUS_BAR_DISABLED = 38;
+  SET_ORGANIZATION_COLOR = 39;
+  SET_PROFILE_NAME = 40;
+  SET_USER_ICON = 41;
+  SET_DEVICE_OWNER_LOCKSCREEN_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_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;
+  ADD_PERSISTENT_PREFERRED_ACTIVITY = 52;
+  REQUEST_BUGREPORT = 53;
+  GET_WIFI_MAC_ADDRESS = 54;
+  REQUEST_QUIET_MODE_ENABLED = 55;
+  WORK_PROFILE_LOCATION_CHANGED = 56;
+  DO_USER_INFO_CLICKED = 57;
+  TRANSFER_OWNERSHIP = 58;
+  GENERATE_KEY_PAIR = 59;
+  SET_KEY_PAIR_CERTIFICATE = 60;
+  SET_KEEP_UNINSTALLED_PACKAGES = 61;
+  SET_APPLICATION_RESTRICTIONS = 62;
+  SET_APPLICATION_HIDDEN = 63;
+  ENABLE_SYSTEM_APP = 64;
+  ENABLE_SYSTEM_APP_WITH_INTENT = 65;
+  INSTALL_EXISTING_PACKAGE = 66;
+  SET_UNINSTALL_BLOCKED = 67;
+  SET_PACKAGES_SUSPENDED = 68;
+  ON_LOCK_TASK_MODE_ENTERING = 69;
+  ADD_CROSS_PROFILE_CALENDAR_PACKAGE = 70;
+  REMOVE_CROSS_PROFILE_CALENDAR_PACKAGE = 71;
+  GET_USER_PASSWORD_COMPLEXITY_LEVEL = 72;
+  INSTALL_SYSTEM_UPDATE = 73;
+  INSTALL_SYSTEM_UPDATE_ERROR = 74;
+  IS_MANAGED_KIOSK = 75;
+  IS_UNATTENDED_MANAGED_KIOSK = 76;
+  PROVISIONING_TO_COMP = 77;
+  PROVISIONING_FORCED_DO = 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_ADB = 82;
+  PROVISIONING_ENTRY_POINT_TRUSTED_SOURCE = 83;
+  PROVISIONING_DPC_PACKAGE_NAME = 84;
+  PROVISIONING_DPC_INSTALLED_BY_PACKAGE = 85;
+  PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS = 86;
+  PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS = 87;
+  PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS = 88;
+  PROVISIONING_WEB_ACTIVITY_TIME_MS = 89;
+  PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90;
+  PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91;
+  PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92;
+  PROVISIONING_NETWORK_TYPE = 93;
+  PROVISIONING_ACTION = 94;
+  PROVISIONING_EXTRAS = 95;
+  PROVISIONING_COPY_ACCOUNT_TASK_MS = 96;
+  PROVISIONING_CREATE_PROFILE_TASK_MS = 97;
+  PROVISIONING_START_PROFILE_TASK_MS = 98;
+  PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS = 99;
+  PROVISIONING_INSTALL_PACKAGE_TASK_MS = 100;
+  PROVISIONING_CANCELLED = 101;
+  PROVISIONING_ERROR = 102;
+  PROVISIONING_COPY_ACCOUNT_STATUS = 103;
+  PROVISIONING_TOTAL_TASK_TIME_MS = 104;
+  PROVISIONING_SESSION_STARTED = 105;
+  PROVISIONING_SESSION_COMPLETED = 106;
+  PROVISIONING_TERMS_ACTIVITY_TIME_MS = 107;
+  PROVISIONING_TERMS_COUNT = 108;
+  PROVISIONING_TERMS_READ = 109;
+}
diff --git a/core/proto/android/stats/devicepolicy/jarjar-rules.txt b/core/proto/android/stats/devicepolicy/jarjar-rules.txt
new file mode 100644
index 0000000..40043a86
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.nano.** com.android.framework.protobuf.nano.@1
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e35b701..6c3085f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -620,6 +620,8 @@
 
     <protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" />
 
+    <protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
@@ -794,7 +796,8 @@
     <permission android:name="android.permission.READ_EXTERNAL_STORAGE"
         android:label="@string/permlab_sdcardRead"
         android:description="@string/permdesc_sdcardRead"
-        android:protectionLevel="normal" />
+        android:protectionLevel="dangerous"
+        android:permissionFlags="removed" />
 
     <!-- Allows an application to write to external storage.
          <p class="note"><strong>Note:</strong> If <em>both</em> your <a
@@ -814,7 +817,8 @@
     <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
         android:label="@string/permlab_sdcardWrite"
         android:description="@string/permdesc_sdcardWrite"
-        android:protectionLevel="normal" />
+        android:protectionLevel="dangerous"
+        android:permissionFlags="removed" />
 
     <!-- Runtime permission controlling access to the user's shared aural media
          collection. -->
@@ -831,12 +835,6 @@
         android:description="@string/permdesc_audioRead"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to modify the user's shared audio collection. -->
-    <permission android:name="android.permission.WRITE_MEDIA_AUDIO"
-        android:label="@string/permlab_audioWrite"
-        android:description="@string/permdesc_audioWrite"
-        android:protectionLevel="dangerous" />
-
     <!-- Runtime permission controlling access to the user's shared visual media
          collection, including images and videos. -->
     <permission-group android:name="android.permission-group.MEDIA_VISUAL"
@@ -852,24 +850,12 @@
         android:description="@string/permdesc_imagesRead"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to modify the user's shared images collection. -->
-    <permission android:name="android.permission.WRITE_MEDIA_IMAGES"
-        android:label="@string/permlab_imagesWrite"
-        android:description="@string/permdesc_imagesWrite"
-        android:protectionLevel="dangerous" />
-
     <!-- Allows an application to read the user's shared video collection. -->
     <permission android:name="android.permission.READ_MEDIA_VIDEO"
         android:label="@string/permlab_videoRead"
         android:description="@string/permdesc_videoRead"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to modify the user's shared video collection. -->
-    <permission android:name="android.permission.WRITE_MEDIA_VIDEO"
-        android:label="@string/permlab_videoWrite"
-        android:description="@string/permdesc_videoWrite"
-        android:protectionLevel="dangerous" />
-
     <!-- Allows an application to access any geographic locations persisted in the
          user's shared collection. -->
     <permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
@@ -3024,6 +3010,13 @@
     <permission android:name="android.permission.BIND_TV_INPUT"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Must be required by an {@link android.service.sms.FinancialSmsService}
+         to ensure that only the system can bind to it.
+         @hide This is not a third-party API (intended for OEMs and system apps).
+    -->
+    <permission android:name="android.permission.BIND_FINANCIAL_SMS_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- @SystemApi
          Must be required by a {@link com.android.media.tv.remoteprovider.TvRemoteProvider}
          to ensure that only the system can bind to it.
@@ -4213,6 +4206,18 @@
     <permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
         android:protectionLevel="signature|appop" />
 
+    <!-- @SystemApi Allows requesting the framework broadcast the
+         {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} intent.
+         @hide -->
+    <permission android:name="android.permission.SEND_DEVICE_CUSTOMIZATION_READY"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Permission that protects the {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY}
+         intent.
+         @hide -->
+    <permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"
+        android:protectionLevel="signature|preinstalled" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
@@ -4500,6 +4505,14 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="com.android.server.updates.ConversationActionsInstallReceiver"
+                  android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.intent.action.ACTION_UPDATE_CONVERSATION_ACTIONS" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
         <receiver android:name="com.android.server.updates.CarrierIdInstallReceiver"
                   android:permission="android.permission.UPDATE_CONFIG">
             <intent-filter>
diff --git a/core/res/res/layout/notification_material_action.xml b/core/res/res/layout/notification_material_action.xml
index 3c9f6ee..c024dbe 100644
--- a/core/res/res/layout/notification_material_action.xml
+++ b/core/res/res/layout/notification_material_action.xml
@@ -16,16 +16,14 @@
   -->
 
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+    style="@android:style/NotificationAction"
     android:id="@+id/action0"
     android:layout_width="wrap_content"
     android:layout_height="48dp"
     android:layout_gravity="center"
     android:gravity="start|center_vertical"
     android:layout_marginStart="4dp"
-    android:textColor="@color/notification_default_color"
     android:singleLine="true"
     android:textAlignment="viewStart"
     android:ellipsize="end"
-    android:background="@drawable/notification_material_action_background"
     />
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 07559f4..4258019 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -18,6 +18,7 @@
         android:id="@+id/actions_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/notification_action_list_margin_top"
         android:layout_gravity="bottom">
     <com.android.internal.widget.NotificationActionListLayout
             android:id="@+id/actions"
@@ -27,6 +28,7 @@
             android:orientation="horizontal"
             android:gravity="center_vertical"
             android:visibility="gone"
+            android:background="@color/notification_action_list_background_color"
             >
         <!-- actions will be added here -->
     </com.android.internal.widget.NotificationActionListLayout>
diff --git a/core/res/res/layout/notification_material_action_tombstone.xml b/core/res/res/layout/notification_material_action_tombstone.xml
index 9fa7c6a..f165724 100644
--- a/core/res/res/layout/notification_material_action_tombstone.xml
+++ b/core/res/res/layout/notification_material_action_tombstone.xml
@@ -16,7 +16,7 @@
   -->
 
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+    style="@android:style/NotificationTombstoneAction"
     android:id="@+id/action0"
     android:layout_width="wrap_content"
     android:layout_height="48dp"
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index 9de8842..a2ad3b9 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -20,7 +20,7 @@
         <!-- Color palette -->
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
-        <item name="colorAccent">@color/white</item>
+        <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
         <item name="colorControlNormal">?attr/textColorPrimary</item>
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 605662a..470e1cc 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7941,6 +7941,11 @@
         <!-- Uri that specifies a settings Slice for this wallpaper. -->
         <attr name="settingsSliceUri" />
 
+        <!-- Indicates that this wallpaper service can support multiple engines to render on each
+             surface independently. An example use case is a multi-display set-up where the
+             wallpaper service can render surfaces to each of the connected displays. -->
+        <attr name="supportsMultipleDisplays" format="boolean" />
+
     </declare-styleable>
 
     <!-- Use <code>dream</code> as the root tag of the XML resource that
@@ -8798,6 +8803,7 @@
         <attr name="layout_ignoreOffset" format="boolean" />
         <attr name="layout_gravity" />
         <attr name="layout_hasNestedScrollIndicator" format="boolean" />
+        <attr name="layout_maxHeight" />
     </declare-styleable>
 
     <!-- @hide -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 6fc0f5b..18d1d5d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2148,6 +2148,21 @@
         <attr name="visibleToInstantApps" />
         <!-- The code for this component is located in the given split. -->
         <attr name="splitName" />
+        <!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service
+             will be spawned from an Application Zygote, instead of the regular Zygote.
+             <p>
+             The Application Zygote will pre-initialize the application's class loader,
+             and call a static callback into the application to allow it to perform
+             application-specific preloads (such as loading a shared library). Therefore,
+             spawning from the Application Zygote will typically reduce the service
+             launch time and reduce its memory usage. The downside of using this flag
+             is that you will have an additional process (the app zygote itself) that
+             is taking up memory. Whether actual memory usage is improved therefore strongly
+             depends on the number of isolated services that an application starts,
+             and how much memory those services save by preloading. Therefore, it is
+             recommended to measure memory usage under typical workloads to determine
+             whether it makes sense to use this flag. -->
+        <attr name="useAppZygote" format="boolean" />
     </declare-styleable>
 
     <!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 4122cf0..16c0744 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -146,10 +146,14 @@
 
     <color name="notification_default_color">#757575</color> <!-- Gray 600 -->
 
+    <color name="notification_action_button_text_color">@color/notification_default_color</color>
+
     <color name="notification_progress_background_color">@color/secondary_text_material_light</color>
 
     <color name="notification_action_list">#ffeeeeee</color>
 
+    <color name="notification_action_list_background_color">@null</color>
+
     <!-- Keyguard colors -->
     <color name="keyguard_avatar_frame_color">#ffffffff</color>
     <color name="keyguard_avatar_frame_shadow_color">#80000000</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 829d6f5..1d80961 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -364,7 +364,7 @@
 
     <!-- Max number of Bluetooth tethering connections allowed. If this is
          updated config_tether_dhcp_range has to be updated appropriately. -->
-    <integer translateable="false" name="config_max_pan_devices">5</integer>
+    <integer translatable="false" name="config_max_pan_devices">5</integer>
 
     <!-- Dhcp range (min, max) to use for tethering purposes -->
     <string-array translatable="false" name="config_tether_dhcp_range">
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index e902989..f7b9961 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -200,6 +200,9 @@
     <!-- The height of the notification action list -->
     <dimen name="notification_action_list_height">60dp</dimen>
 
+    <!-- The margin of the notification action list at the top -->
+    <dimen name="notification_action_list_margin_top">0dp</dimen>
+
     <!-- The height of the notification action list -->
     <dimen name="notification_action_emphasized_height">48dp</dimen>
 
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 64e5bc0..bbe3ff9 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -190,4 +190,7 @@
 
   <!-- A tag used to save the view added to a transition overlay -->
   <item type="id" name="transition_overlay_view_tag" />
+
+  <!-- A tag used to save the notification action object -->
+  <item type="id" name="notification_action_index_tag" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 73dae08..63cac51 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2920,6 +2920,8 @@
         <public name="shell" />
         <public name="interactiveUiTimeout" />
         <public name="importantForContentCapture" />
+        <public name="supportsMultipleDisplays" />
+        <public name="useAppZygote" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 200c35d..bd6d976 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1586,22 +1586,14 @@
     <string name="permdesc_readSyncStats">Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced. </string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permlab_sdcardRead" product="nosdcard">read the contents of your USB storage</string>
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_sdcardRead" product="default">read the contents of your SD card</string>
+    <string name="permlab_sdcardRead">read the contents of your shared storage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to read the contents of your USB storage.</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_sdcardRead" product="default">Allows the app to read the contents of your SD card.</string>
+    <string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permlab_sdcardWrite" product="nosdcard">modify or delete the contents of your USB storage</string>
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_sdcardWrite" product="default">modify or delete the contents of your SD card</string>
+    <string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permdesc_sdcardWrite" product="nosdcard">Allows the app to write to the USB storage.</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_sdcardWrite" product="default">Allows the app to write to the SD card.</string>
+    <string name="permdesc_sdcardWrite">Allows the app to write the contents of your shared storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_use_sip">make/receive SIP calls</string>
@@ -2989,6 +2981,18 @@
     <!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] -->
     <string name="view_flight_desc">Track selected flight</string>
 
+    <!-- Label for item in the text selection menu to translate selected text with a translation app. Should be a verb. [CHAR LIMIT=30] -->
+    <string name="translate">Translate</string>
+    
+    <!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] -->
+    <string name="translate_desc">Translate selected text</string>
+
+    <!-- Label for item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=30] -->
+    <string name="define">Define</string>
+
+    <!-- Accessibility description for an item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=NONE] -->
+    <string name="define_desc">Define selected text</string>
+
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the title of that notification. -->
     <string name="low_internal_storage_view_title">Storage space running out</string>
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the message of that notification. -->
@@ -3867,10 +3871,8 @@
     <string name="action_mode_done">Done</string>
 
     <!-- Strings for MasterClearReceiver. -->
-    <!-- Text for progress dialog while erasing USB storage volume [CHAR LIMIT=NONE] -->
-    <string name="progress_erasing" product="nosdcard">Erasing USB storage\u2026</string>
-    <!-- Text for progress dialog while erasing SD card [CHAR LIMIT=NONE] -->
-    <string name="progress_erasing" product="default">Erasing SD card\u2026</string>
+    <!-- Text for progress dialog while erasing the shared storage volume [CHAR LIMIT=NONE] -->
+    <string name="progress_erasing">Erasing shared storage\u2026</string>
 
     <!-- Text for WebView's text selection Action Mode -->
     <!-- ActionBar action to share the current selection [CHAR LIMIT=10] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index bd53936..18f7e48 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1526,10 +1526,22 @@
         <item name="gravity">top</item>
     </style>
 
-    <!-- Colored bordered ink button -->
+    <!-- The style for normal action button on notification -->
+    <style name="NotificationAction" parent="Widget.Material.Light.Button.Borderless.Small">
+      <item name="textColor">@color/notification_action_button_text_color</item>
+      <item name="background">@drawable/notification_material_action_background</item>
+    </style>
+
+    <!-- The style for emphasized action button on notification: Colored bordered ink button -->
     <style name="NotificationEmphasizedAction" parent="Widget.Material.Button">
         <item name="background">@drawable/btn_notification_emphasized</item>
         <item name="stateListAnimator">@anim/flat_button_state_list_anim_material</item>
     </style>
 
+    <!-- The style for disabled action button on notification -->
+    <style name="NotificationTombstoneAction" parent="NotificationAction">
+      <item name="textColor">#555555</item>
+    </style>
+
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 82c9ff3..e251e27 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,10 @@
   <java-symbol type="string" name="add_calendar_event_desc" />
   <java-symbol type="string" name="view_flight" />
   <java-symbol type="string" name="view_flight_desc" />
+  <java-symbol type="string" name="translate" />
+  <java-symbol type="string" name="translate_desc" />
+  <java-symbol type="string" name="define" />
+  <java-symbol type="string" name="define_desc" />
   <java-symbol type="string" name="textSelectionCABTitle" />
   <java-symbol type="string" name="BaMmi" />
   <java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
@@ -2679,6 +2683,7 @@
   <java-symbol type="id" name="smart_reply_container" />
   <java-symbol type="id" name="remote_input_tag" />
   <java-symbol type="id" name="pending_intent_tag" />
+  <java-symbol type="id" name="notification_action_index_tag" />
 
   <java-symbol type="attr" name="seekBarDialogPreferenceStyle" />
   <java-symbol type="string" name="ext_media_status_removed" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index f60d8d0ad..3b650e5 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1316,6 +1316,12 @@
         <service android:name="android.os.BinderThreadPriorityService"
                 android:process=":BinderThreadPriorityService" />
 
+        <!-- Used by BinderWorkSourceTest -->
+        <service android:name="android.os.BinderWorkSourceService"
+                android:process=":BinderWorkSourceService" />
+        <service android:name="android.os.BinderWorkSourceNestedService"
+                android:process=":BinderWorkSourceNestedService" />
+
         <!-- Application components used for search manager tests -->
 
         <activity android:name="android.app.activity.SearchableActivity"
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index e1cb911..e89a4d3 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -106,9 +106,9 @@
         int backgroundColor = 0xff585868;
         int initialForegroundColor = 0xff505868;
         builder.setColorPalette(backgroundColor, initialForegroundColor);
-        int primaryTextColor = builder.getPrimaryTextColor();
+        int primaryTextColor = builder.getPrimaryTextColor(builder.mParams);
         assertTrue(satisfiesTextContrast(primaryTextColor, backgroundColor));
-        int secondaryTextColor = builder.getSecondaryTextColor();
+        int secondaryTextColor = builder.getSecondaryTextColor(builder.mParams);
         assertTrue(satisfiesTextContrast(secondaryTextColor, backgroundColor));
     }
 
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index a317c99..8ac9451d 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -364,9 +364,7 @@
 
     private int getInstallLoc(int flags, int expInstallLocation, long pkgLen) {
         // Flags explicitly over ride everything else.
-        if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
-            return INSTALL_LOC_SD;
-        } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
+        if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
             return INSTALL_LOC_INT;
         }
         // Manifest option takes precedence next
@@ -437,8 +435,6 @@
 
             int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen);
             if (rLoc == INSTALL_LOC_INT) {
-                assertFalse(
-                        (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
                 assertEquals(appInstallPath, srcPath);
                 assertEquals(appInstallPath, publicSrcPath);
                 assertStartsWith("Native library should point to shared lib directory",
@@ -461,8 +457,6 @@
                     }
                 }
             } else if (rLoc == INSTALL_LOC_SD) {
-                assertFalse("The application should not be installed forward locked",
-                        (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
                 assertTrue("Application flags (" + info.flags
                         + ") should contain FLAG_EXTERNAL_STORAGE",
                         (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
@@ -845,31 +839,10 @@
     }
 
     @LargeTest
-    public void testReplaceFailSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        sampleReplaceFromRawResource(PackageManager.INSTALL_EXTERNAL);
-    }
-
-    @LargeTest
     public void testReplaceNormalInternal() throws Exception {
         sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING);
     }
 
-    @LargeTest
-    public void testReplaceSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING
-                | PackageManager.INSTALL_EXTERNAL);
-    }
-
     /* -------------- Delete tests --- */
     private static class DeleteObserver extends IPackageDeleteObserver.Stub {
         private CountDownLatch mLatch = new CountDownLatch(1);
@@ -1015,31 +988,12 @@
         deleteFromRawResource(0, 0);
     }
 
-    @LargeTest
-    public void testDeleteSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, 0);
-    }
 
     @LargeTest
     public void testDeleteNormalInternalRetainData() throws Exception {
         deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA);
     }
 
-    @LargeTest
-    public void testDeleteSdcardRetainData() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DELETE_KEEP_DATA);
-    }
-
     void cleanUpInstall(InstallParams ip) throws Exception {
         if (ip == null) {
             return;
@@ -1104,60 +1058,6 @@
                 0, true, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
     }
 
-    /*
-     * Install a package on internal flash via PackageManager install flag. Replace
-     * the package via flag to install on sdcard. Make sure the new flag overrides
-     * the old install location.
-     */
-    @LargeTest
-    public void testReplaceFlagInternalSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = 0;
-        int rFlags = PackageManager.INSTALL_EXTERNAL;
-        InstallParams ip = sampleInstallFromRawResource(iFlags, false);
-        GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
-        int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
-        try {
-            invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
-            assertInstall(ip.pkg, rFlags, ip.pkg.installLocation);
-        } catch (Exception e) {
-            failStr("Failed with exception : " + e);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
-    /*
-     * Install a package on sdcard via PackageManager install flag. Replace
-     * the package with no flags or manifest option and make sure the old
-     * install location is retained.
-     */
-    @LargeTest
-    public void testReplaceFlagSdcardInternal() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = 0;
-        InstallParams ip = sampleInstallFromRawResource(iFlags, false);
-        GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
-        int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
-        try {
-            invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
-            assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
-        } catch (Exception e) {
-            failStr("Failed with exception : " + e);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
     @LargeTest
     public void testManifestInstallLocationReplaceInternalSdcard() throws Exception {
         // Do not run on devices with emulated external storage.
@@ -1375,34 +1275,6 @@
     }
 
     @LargeTest
-    public void testMoveAppExternalToExternal() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int installFlags = PackageManager.INSTALL_EXTERNAL;
-        int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA;
-        boolean fail = true;
-        int result = PackageManager.MOVE_FAILED_INVALID_LOCATION;
-        sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
-    }
-
-    @LargeTest
-    public void testMoveAppExternalToInternal() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int installFlags = PackageManager.INSTALL_EXTERNAL;
-        int moveFlags = PackageManager.MOVE_INTERNAL;
-        boolean fail = false;
-        int result = PackageManager.MOVE_SUCCEEDED;
-        sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
-    }
-
-    @LargeTest
     public void testMoveAppFailInternalToExternalDelete() throws Exception {
         // Do not run on devices with emulated external storage.
         if (Environment.isExternalStorageEmulated()) {
@@ -1458,19 +1330,6 @@
     }
 
     /*
-     * Install an app on sdcard.
-     */
-    @LargeTest
-    public void testFlagE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, true);
-    }
-
-    /*
      * Install an app with both internal and manifest option set.
      * should install on internal.
      */
@@ -1506,59 +1365,6 @@
                 false, -1,
                 PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
     }
-    /*
-     * Install an app with both external and manifest option set.
-     * should install externally.
-     */
-    @LargeTest
-    public void testFlagEManifestI() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        installFromRawResource("install.apk", R.raw.install_loc_internal,
-                PackageManager.INSTALL_EXTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    /*
-     * Install an app with both external and manifest preference for
-     * preferExternal. Should install externally.
-     */
-    @LargeTest
-    public void testFlagEManifestE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                PackageManager.INSTALL_EXTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    /*
-     * Install an app with both external and manifest preference for
-     * auto. should install on external media.
-     */
-    @LargeTest
-    public void testFlagEManifestA() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        installFromRawResource("install.apk", R.raw.install_loc_auto,
-                PackageManager.INSTALL_EXTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
 
     /*
      * The following test functions verify install location for existing apps.
@@ -1586,75 +1392,6 @@
                 -1);
     }
 
-    @LargeTest
-    public void testFlagIExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install,
-                rFlags,
-                true,
-                false, -1,
-                -1);
-    }
-
-    @LargeTest
-    public void testFlagEExistingI() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_INTERNAL;
-        int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install,
-                rFlags,
-                true,
-                false, -1,
-                -1);
-    }
-
-    @LargeTest
-    public void testFlagEExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install,
-                rFlags,
-                true,
-                false, -1,
-                -1);
-    }
-
     /*
      * The following set of tests verify the installation of apps with
      * install location attribute set to internalOnly, preferExternal and auto.
@@ -1720,29 +1457,6 @@
     }
 
     @LargeTest
-    public void testManifestIExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_internal,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-
-    @LargeTest
     public void testManifestEExistingI() throws Exception {
         // Do not run on devices with emulated external storage.
         if (Environment.isExternalStorageEmulated()) {
@@ -1766,29 +1480,6 @@
     }
 
     @LargeTest
-    public void testManifestEExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
     public void testManifestAExistingI() throws Exception {
         int iFlags = PackageManager.INSTALL_INTERNAL;
         int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
@@ -1806,29 +1497,6 @@
                 PackageInfo.INSTALL_LOCATION_AUTO);
     }
 
-    @LargeTest
-    public void testManifestAExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_auto,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
     /*
      * The following set of tests check install location for existing
      * application based on user setting.
@@ -1896,42 +1564,6 @@
         setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
     }
 
-    @LargeTest
-    public void testExistingEUserI() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
-    public void testExistingEUserE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
-    public void testExistingEUserA() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_AUTO;
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
     /*
      * The following set of tests verify that the user setting defines
      * the install location.
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java b/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java
new file mode 100644
index 0000000..dddeda3
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java
@@ -0,0 +1,91 @@
+/*
+ * 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.os;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+* Service used by {@link BinderWorkSourceTest}.
+*/
+public class BinderWorkSourceNestedService extends Service {
+    private final IBinderWorkSourceNestedService.Stub mBinder =
+            new IBinderWorkSourceNestedService.Stub() {
+
+        public int[] nestedCallWithWorkSourceToSet(int uidToBlame) {
+            final int uid =  Binder.getCallingWorkSourceUid();
+            if (uidToBlame != ThreadLocalWorkSource.UID_NONE) {
+                Binder.setCallingWorkSourceUid(uidToBlame);
+            }
+            final int nestedUid = callGetIncomingWorkSourceUid();
+            return new int[] {uid, nestedUid};
+        }
+
+        public int[] nestedCall() {
+            final int uid =  Binder.getCallingWorkSourceUid();
+            final int nestedUid = callGetIncomingWorkSourceUid();
+            return new int[] {uid, nestedUid};
+        }
+
+        private int callGetIncomingWorkSourceUid() {
+            BlockingQueue<IBinderWorkSourceService> blockingQueue =
+                    new LinkedBlockingQueue<>();
+            ServiceConnection mConnection = new ServiceConnection() {
+                public void onServiceConnected(ComponentName name, IBinder service) {
+                    blockingQueue.add(IBinderWorkSourceService.Stub.asInterface(service));
+                }
+
+                public void onServiceDisconnected(ComponentName name) {
+                }
+            };
+
+            Context context = getApplicationContext();
+            context.bindService(
+                    new Intent(context, BinderWorkSourceService.class),
+                    mConnection, Context.BIND_AUTO_CREATE);
+
+            final IBinderWorkSourceService service;
+            try {
+                service = blockingQueue.poll(30, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+            if (service == null) {
+                throw new RuntimeException("Gave up waiting for BinderWorkSourceService");
+            }
+
+            try {
+                return service.getIncomingWorkSourceUid();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            } finally {
+                context.unbindService(mConnection);
+            }
+        }
+    };
+
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+}
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceService.java b/core/tests/coretests/src/android/os/BinderWorkSourceService.java
new file mode 100644
index 0000000..ac8d7ab9
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.os;
+
+import android.app.Service;
+import android.content.Intent;
+
+/**
+ * Service used by {@link BinderWorkSourceTest}.
+ */
+public class BinderWorkSourceService extends Service {
+    private final IBinderWorkSourceService.Stub mBinder =
+            new IBinderWorkSourceService.Stub() {
+        public int getIncomingWorkSourceUid() {
+            return Binder.getCallingWorkSourceUid();
+        }
+    };
+
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+}
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
new file mode 100644
index 0000000..ec17803
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test whether Binder calls work source is propagated correctly.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BinderWorkSourceTest {
+    private static Context sContext;
+    private static final int UID = 100;
+    private static final int SECOND_UID = 200;
+    private static final int UID_NONE = ThreadLocalWorkSource.UID_NONE;
+
+    private IBinderWorkSourceService mService;
+    private IBinderWorkSourceNestedService mNestedService;
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mService = IBinderWorkSourceService.Stub.asInterface(service);
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            mService = null;
+        }
+    };
+
+    private ServiceConnection mNestedConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mNestedService = IBinderWorkSourceNestedService.Stub.asInterface(service);
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            mNestedService = null;
+        }
+    };
+
+    @BeforeClass
+    public static void setUpOnce() throws Exception {
+        sContext = InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        sContext.bindService(
+                new Intent(sContext, BinderWorkSourceService.class),
+                mConnection, Context.BIND_AUTO_CREATE);
+        sContext.bindService(
+                new Intent(sContext, BinderWorkSourceNestedService.class),
+                mNestedConnection, Context.BIND_AUTO_CREATE);
+
+        final long timeoutMs = System.currentTimeMillis() + 30_000;
+        while ((mService == null || mNestedService == null)
+                && System.currentTimeMillis() < timeoutMs) {
+            Thread.sleep(1_000);
+        }
+        assertNotNull("Gave up waiting for BinderWorkSourceService", mService);
+        assertNotNull("Gave up waiting for BinderWorkSourceNestedService", mNestedService);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        sContext.unbindService(mConnection);
+        sContext.unbindService(mNestedConnection);
+    }
+
+    @Test
+    public void setWorkSource() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+        assertEquals(UID, mService.getIncomingWorkSourceUid());
+        assertEquals(UID, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void clearWorkSource() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+        Binder.clearCallingWorkSource();
+        assertEquals(UID_NONE, mService.getIncomingWorkSourceUid());
+        assertEquals(UID_NONE, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void setWorkSource_propagatedForMultipleCalls() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+        assertEquals(UID, mService.getIncomingWorkSourceUid());
+        assertEquals(UID, mService.getIncomingWorkSourceUid());
+        assertEquals(UID, mService.getIncomingWorkSourceUid());
+        assertEquals(UID, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void restoreWorkSource() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+        long token = Binder.clearCallingWorkSource();
+        Binder.restoreCallingWorkSource(token);
+
+        assertEquals(UID, mService.getIncomingWorkSourceUid());
+        assertEquals(UID, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void nestedSetWorkSoucePropagated() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+
+        int[] workSources = mNestedService.nestedCallWithWorkSourceToSet(SECOND_UID);
+        assertEquals(UID, workSources[0]);
+        // UID set in ested call.
+        assertEquals(SECOND_UID, workSources[1]);
+        // Initial work source restored.
+        assertEquals(UID, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void nestedSetWorkSouceDoesNotEnablePropagation() throws Exception {
+        int[] workSources = mNestedService.nestedCallWithWorkSourceToSet(UID);
+        assertEquals(UID_NONE, workSources[0]);
+        // UID set in ested call.
+        assertEquals(UID, workSources[1]);
+        // Initial work source restored.
+        assertEquals(UID_NONE, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void nestedSetWorkSouceNotPropagated() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+
+        int[] workSources = mNestedService.nestedCall();
+        assertEquals(UID, workSources[0]);
+        // No UID propagated.
+        assertEquals(UID_NONE, workSources[1]);
+        // Initial work source restored.
+        assertEquals(UID, Binder.getCallingWorkSourceUid());
+    }
+}
diff --git a/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
new file mode 100644
index 0000000..365aebb
--- /dev/null
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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 android.os;
+
+interface IBinderWorkSourceNestedService {
+    int[] nestedCallWithWorkSourceToSet(int uidToBlame);
+    int[] nestedCall();
+}
diff --git a/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
new file mode 100644
index 0000000..05d4e82
--- /dev/null
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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 android.os;
+
+interface IBinderWorkSourceService {
+    int getIncomingWorkSourceUid();
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 002b6a8..305d2af 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -124,7 +124,9 @@
                     Settings.Global.AUTOFILL_LOGGING_LEVEL,
                     Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
                     Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
+                    Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS,
                     Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
+                    Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED,
                     Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
                     Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
                     Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
@@ -181,6 +183,8 @@
                     Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE,
                     Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
                     Settings.Global.CONTACT_METADATA_SYNC_ENABLED,
+                    Settings.Global.CONVERSATION_ACTIONS_UPDATE_CONTENT_URL,
+                    Settings.Global.CONVERSATION_ACTIONS_UPDATE_METADATA_URL,
                     Settings.Global.CONTACTS_DATABASE_WAL_ENABLED,
                     Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
                     Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
@@ -189,6 +193,10 @@
                     Settings.Global.DATA_ROAMING,
                     Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
                     Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS,
+                    Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD,
+                    Settings.Global.DATA_STALL_EVALUATION_TYPE,
+                    Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL,
+                    Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD,
                     Settings.Global.DEBUG_APP,
                     Settings.Global.DEBUG_VIEW_ATTRIBUTES,
                     Settings.Global.DEFAULT_DNS_SERVER,
@@ -281,6 +289,7 @@
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+                    Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS,
                     Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
                     Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
                     Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
@@ -306,6 +315,7 @@
                     Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
                     Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
                     Settings.Global.MULTI_SIM_VOICE_PROMPT,
+                    Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
                     Settings.Global.NETSTATS_DEV_BUCKET_DURATION,
                     Settings.Global.NETSTATS_DEV_DELETE_AGE,
                     Settings.Global.NETSTATS_DEV_PERSIST_BYTES,
@@ -653,7 +663,8 @@
                  Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION,
                  Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
                  Settings.Secure.FLASHLIGHT_AVAILABLE,
-                 Settings.Secure.FLASHLIGHT_ENABLED);
+                 Settings.Secure.FLASHLIGHT_ENABLED,
+                 Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED);
 
     @Test
     public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
new file mode 100644
index 0000000..ed472d2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static junit.framework.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsSourceTest {
+
+    private InsetsSource mSource = new InsetsSource(TYPE_NAVIGATION_BAR);
+
+    @Before
+    public void setUp() {
+        mSource.setVisible(true);
+    }
+
+    @Test
+    public void testCalculateInsetsTop() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 100, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsetsBottom() {
+        mSource.setFrame(new Rect(0, 400, 500, 500));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 0, 0, 100), insets);
+    }
+
+    @Test
+    public void testCalculateInsetsLeft() {
+        mSource.setFrame(new Rect(0, 0, 100, 500));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(100, 0, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsetsRight() {
+        mSource.setFrame(new Rect(400, 0, 500, 500));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 0, 100, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsets_overextend() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 100, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsets_invisible() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        mSource.setVisible(false);
+        Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 0, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsets_ignoreVisibility() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        mSource.setVisible(false);
+        Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+                true /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 100, 0, 0), insets);
+    }
+
+    // Parcel and equals already tested via InsetsStateTest
+}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
new file mode 100644
index 0000000..6bb9539
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsStateTest {
+
+    private InsetsState mState = new InsetsState();
+    private InsetsState mState2 = new InsetsState();
+
+    @Test
+    public void testCalculateInsets() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_TOP_BAR).setVisible(true);
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+        mState.getSource(TYPE_IME).setVisible(true);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets());
+    }
+
+    @Test
+    public void testCalculateInsets_imeAndNav() {
+        mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300));
+        mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300));
+        mState.getSource(TYPE_IME).setVisible(true);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(100, insets.getStableInsetBottom());
+        assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets());
+    }
+
+    @Test
+    public void testCalculateInsets_navRightStatusTop() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_TOP_BAR).setVisible(true);
+        mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
+        mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets());
+    }
+
+    @Test
+    public void testStripForDispatch() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_TOP_BAR).setVisible(true);
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+        mState.getSource(TYPE_IME).setVisible(true);
+        mState.removeSource(TYPE_IME);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(0, insets.getSystemWindowInsetBottom());
+    }
+
+    @Test
+    public void testEquals_differentRect() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 10, 10));
+        assertNotEquals(mState, mState2);
+    }
+
+    @Test
+    public void testEquals_differentSource() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        assertNotEquals(mState, mState2);
+    }
+
+    @Test
+    public void testEquals_sameButDifferentInsertOrder() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        assertEquals(mState, mState2);
+    }
+
+    @Test
+    public void testEquals_visibility() {
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_IME).setVisible(true);
+        mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        assertNotEquals(mState, mState2);
+    }
+
+    @Test
+    public void testParcelUnparcel() {
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_IME).setVisible(true);
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        Parcel p = Parcel.obtain();
+        mState.writeToParcel(p, 0 /* flags */);
+        mState2.readFromParcel(p);
+        p.recycle();
+        assertEquals(mState, mState2);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
new file mode 100644
index 0000000..f0faaf6
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Person;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.textclassifier.ActionsSuggestionsModel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ActionsSuggestionsHelperTest {
+    @Test
+    public void testToNativeMessages_emptyInput() {
+        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+                ActionsSuggestionsHelper.toNativeMessages(Collections.emptyList());
+
+        assertThat(conversationMessages).isEmpty();
+    }
+
+    @Test
+    public void testToNativeMessages_noTextMessages() {
+        ConversationActions.Message messageWithoutText =
+                new ConversationActions.Message.Builder().build();
+
+        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+                ActionsSuggestionsHelper.toNativeMessages(
+                        Collections.singletonList(messageWithoutText));
+
+        assertThat(conversationMessages).isEmpty();
+    }
+
+    @Test
+    public void testToNativeMessages_missingPersonInFirstMessage() {
+        ConversationActions.Message firstMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("first")
+                        .build();
+        ConversationActions.Message secondMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("second")
+                        .setAuthor(new Person.Builder().build())
+                        .build();
+        ConversationActions.Message thirdMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("third")
+                        .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
+                        .build();
+
+        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+                ActionsSuggestionsHelper.toNativeMessages(
+                        Arrays.asList(firstMessage, secondMessage, thirdMessage));
+
+        assertThat(conversationMessages).hasLength(2);
+        assertNativeMessage(conversationMessages[0], secondMessage.getText(), 1);
+        assertNativeMessage(conversationMessages[1], thirdMessage.getText(), 0);
+    }
+
+    @Test
+    public void testToNativeMessages_missingPersonInMiddleOfConversation() {
+        ConversationActions.Message firstMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("first")
+                        .setAuthor(new Person.Builder().setName("first").build())
+                        .build();
+        ConversationActions.Message secondMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("second")
+                        .build();
+        ConversationActions.Message thirdMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("third")
+                        .setAuthor(new Person.Builder().setName("third").build())
+                        .build();
+        ConversationActions.Message fourthMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("fourth")
+                        .setAuthor(new Person.Builder().setName("fourth").build())
+                        .build();
+
+        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+                ActionsSuggestionsHelper.toNativeMessages(
+                        Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
+
+        assertThat(conversationMessages).hasLength(2);
+        assertNativeMessage(conversationMessages[0], thirdMessage.getText(), 2);
+        assertNativeMessage(conversationMessages[1], fourthMessage.getText(), 1);
+    }
+
+    @Test
+    public void testToNativeMessages_userIdEncoding() {
+        Person userA = new Person.Builder().setName("userA").build();
+        Person userB = new Person.Builder().setName("userB").build();
+
+        ConversationActions.Message firstMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("first")
+                        .setAuthor(userB)
+                        .build();
+        ConversationActions.Message secondMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("second")
+                        .setAuthor(userA)
+                        .build();
+        ConversationActions.Message thirdMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("third")
+                        .setAuthor(ConversationActions.Message.PERSON_USER_LOCAL)
+                        .build();
+        ConversationActions.Message fourthMessage =
+                new ConversationActions.Message.Builder()
+                        .setText("fourth")
+                        .setAuthor(userA)
+                        .build();
+
+        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
+                ActionsSuggestionsHelper.toNativeMessages(
+                        Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage));
+
+        assertThat(conversationMessages).hasLength(4);
+        assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2);
+        assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1);
+        assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0);
+        assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1);
+    }
+
+    private static void assertNativeMessage(
+            ActionsSuggestionsModel.ConversationMessage nativeMessage,
+            CharSequence text,
+            int userId) {
+        assertThat(nativeMessage.getText()).isEqualTo(text.toString());
+        assertThat(nativeMessage.getUserId()).isEqualTo(userId);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
new file mode 100644
index 0000000..0180856
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/FakeContextBuilder.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.test.InstrumentationRegistry;
+
+import androidx.annotation.Nullable;
+
+import com.google.common.base.Preconditions;
+
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * A builder used to build a fake context for testing.
+ */
+// TODO: Consider making public.
+final class FakeContextBuilder {
+
+    /**
+     * A component name that can be used for tests.
+     */
+    public static final ComponentName DEFAULT_COMPONENT = new ComponentName("pkg", "cls");
+
+    private final PackageManager mPackageManager;
+    private final ContextWrapper mContext;
+    private final Map<String, ComponentName> mComponents = new HashMap<>();
+    private @Nullable ComponentName mAllIntentComponent;
+
+    FakeContextBuilder() {
+        mPackageManager = mock(PackageManager.class);
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(null);
+        mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) {
+            @Override
+            public PackageManager getPackageManager() {
+                return mPackageManager;
+            }
+        };
+    }
+
+    /**
+     * Sets the component name of an activity to handle the specified intent action.
+     * <p>
+     * <strong>NOTE: </strong>By default, no component is set to handle any intent.
+     */
+    public FakeContextBuilder setIntentComponent(
+            String intentAction, @Nullable ComponentName component) {
+        Preconditions.checkNotNull(intentAction);
+        mComponents.put(intentAction, component);
+        return this;
+    }
+
+
+    /**
+     * Sets the component name of an activity to handle all intents.
+     * <p>
+     * <strong>NOTE: </strong>By default, no component is set to handle any intent.
+     */
+    public FakeContextBuilder setAllIntentComponent(@Nullable ComponentName component) {
+        mAllIntentComponent = component;
+        return this;
+    }
+
+    /**
+     * Builds and returns a fake context.
+     */
+    public Context build() {
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenAnswer(
+                (Answer<ResolveInfo>) invocation -> {
+                    final String action = ((Intent) invocation.getArgument(0)).getAction();
+                    final ComponentName component = mComponents.containsKey(action)
+                            ? mComponents.get(action)
+                            : mAllIntentComponent;
+                    return getResolveInfo(component);
+                });
+        return mContext;
+    }
+
+    /**
+     * Returns a component name with random package and class names.
+     */
+    public static ComponentName newComponent() {
+        return new ComponentName(UUID.randomUUID().toString(), UUID.randomUUID().toString());
+    }
+
+    private static ResolveInfo getResolveInfo(ComponentName component) {
+        final ResolveInfo info;
+        if (component == null) {
+            info = null;
+        } else {
+            // NOTE: If something breaks in TextClassifier because we expect more fields to be set
+            // in here, just add them.
+            info = new ResolveInfo();
+            info.activityInfo = new ActivityInfo();
+            info.activityInfo.packageName = component.getPackageName();
+            info.activityInfo.name = component.getClassName();
+            info.activityInfo.exported = true;
+            info.activityInfo.applicationInfo = new ApplicationInfo();
+            info.activityInfo.applicationInfo.icon = 0;
+        }
+        return info;
+    }
+
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
new file mode 100644
index 0000000..bae2be3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.textclassifier.AnnotatorModel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IntentFactoryTest {
+
+    private static final String TEXT = "text";
+
+    @Test
+    public void create_typeDictionary() {
+        AnnotatorModel.ClassificationResult classificationResult =
+                new AnnotatorModel.ClassificationResult(
+                        TextClassifier.TYPE_DICTIONARY,
+                        1.0f,
+                        null,
+                        null);
+
+        List<TextClassifierImpl.LabeledIntent> intents = TextClassifierImpl.IntentFactory.create(
+                InstrumentationRegistry.getContext(),
+                TEXT,
+                false,
+                null,
+                classificationResult);
+
+        assertThat(intents).hasSize(1);
+        TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
+        Intent intent = labeledIntent.getIntent();
+        assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
+        assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 46aa5b4..a3f69d9 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -20,18 +20,10 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
 import android.os.LocaleList;
 import android.service.textclassifier.TextClassifierService;
 import android.support.test.InstrumentationRegistry;
@@ -41,7 +33,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -78,23 +69,10 @@
 
     @Test
     public void testCannotResolveIntent() {
-        final PackageManager fakePackageMgr = mock(PackageManager.class);
-
-        ResolveInfo validInfo = mContext.getPackageManager().resolveActivity(
-                new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:+12122537077")), 0);
-        // Make packageManager fail when it gets the following intent:
-        ArgumentMatcher<Intent> toFailIntent =
-                intent -> intent.getAction().equals(Intent.ACTION_INSERT_OR_EDIT);
-
-        when(fakePackageMgr.resolveActivity(any(Intent.class), anyInt())).thenReturn(validInfo);
-        when(fakePackageMgr.resolveActivity(argThat(toFailIntent), anyInt())).thenReturn(null);
-
-        ContextWrapper fakeContext = new ContextWrapper(mContext) {
-            @Override
-            public PackageManager getPackageManager() {
-                return fakePackageMgr;
-            }
-        };
+        Context fakeContext = new FakeContextBuilder()
+                .setAllIntentComponent(FakeContextBuilder.DEFAULT_COMPONENT)
+                .setIntentComponent(Intent.ACTION_INSERT_OR_EDIT, null)
+                .build();
 
         TextClassifier fallback = TextClassifier.NO_OP;
         TextClassifier classifier = new TextClassifierImpl(
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 06ba15e..fbcb629 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -23,9 +23,12 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.content.Intent;
 import android.os.LocaleList;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.text.Spannable;
+import android.text.SpannableString;
 
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
@@ -52,12 +55,18 @@
 
     @Parameterized.Parameters(name = "{0}")
     public static Iterable<Object> textClassifierTypes() {
-        return Arrays.asList(LOCAL, SYSTEM);
+        return Arrays.asList(LOCAL);
+
+        // TODO: The following will fail on any device that specifies a no-op TextClassifierService.
+        // Enable when we can set a specified TextClassifierService for testing.
+        // return Arrays.asList(LOCAL, SYSTEM);
     }
 
     @Parameterized.Parameter
     public String mTextClassifierType;
 
+    private static final TextClassificationConstants TC_CONSTANTS =
+            TextClassificationConstants.loadFromString("");
     private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
     private static final String NO_TYPE = null;
 
@@ -74,7 +83,7 @@
     }
 
     @Test
-    public void testSmartSelection() {
+    public void testSuggestSelection() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Contact me at droid@android.com";
@@ -95,7 +104,7 @@
     }
 
     @Test
-    public void testSmartSelection_url() {
+    public void testSuggestSelection_url() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Visit http://www.android.com for more information";
@@ -151,7 +160,7 @@
     }
 
     @Test
-    public void testTextClassifyText_url() {
+    public void testClassifyText_url() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Visit www.android.com for more information";
@@ -168,7 +177,7 @@
     }
 
     @Test
-    public void testTextClassifyText_address() {
+    public void testClassifyText_address() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Brandschenkestrasse 110, Zürich, Switzerland";
@@ -182,7 +191,7 @@
     }
 
     @Test
-    public void testTextClassifyText_url_inCaps() {
+    public void testClassifyText_url_inCaps() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Visit HTTP://ANDROID.COM for more information";
@@ -199,7 +208,7 @@
     }
 
     @Test
-    public void testTextClassifyText_date() {
+    public void testClassifyText_date() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Let's meet on January 9, 2018.";
@@ -216,7 +225,7 @@
     }
 
     @Test
-    public void testTextClassifyText_datetime() {
+    public void testClassifyText_datetime() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Let's meet 2018/01/01 10:30:20.";
@@ -234,6 +243,30 @@
     }
 
     @Test
+    public void testClassifyText_foreignText() {
+        LocaleList originalLocales = LocaleList.getDefault();
+        LocaleList.setDefault(LocaleList.forLanguageTags("en"));
+        String foreignText = "これは日本語のテキストです";
+
+        Context context = new FakeContextBuilder()
+                .setIntentComponent(Intent.ACTION_TRANSLATE, FakeContextBuilder.DEFAULT_COMPONENT)
+                .build();
+        TextClassifier classifier = new TextClassifierImpl(context, TC_CONSTANTS);
+        TextClassification.Request request = new TextClassification.Request.Builder(
+                foreignText, 0, foreignText.length())
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextClassification classification = classifier.classifyText(request);
+        assertEquals(1, classification.getActions().size());
+        assertEquals(
+                context.getString(com.android.internal.R.string.translate),
+                classification.getActions().get(0).getTitle());
+
+        LocaleList.setDefault(originalLocales);
+    }
+
+    @Test
     public void testGenerateLinks_phone() {
         if (isTextClassifierDisabled()) return;
         String text = "The number is +12122537077. See you tonight!";
@@ -296,6 +329,17 @@
         assertTrue(links.getLinks().isEmpty());
     }
 
+    @Test
+    public void testApplyLinks_unsupportedCharacter() {
+        if (isTextClassifierDisabled()) return;
+        Spannable url = new SpannableString("\u202Emoc.diordna.com");
+        TextLinks.Request request = new TextLinks.Request.Builder(url).build();
+        assertEquals(
+                TextLinks.STATUS_UNSUPPORTED_CHARACTER,
+                mClassifier.generateLinks(request).apply(url, 0, null));
+    }
+
+
     @Test(expected = IllegalArgumentException.class)
     public void testGenerateLinks_tooLong() {
         if (isTextClassifierDisabled()) {
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 4456122..36792bb 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -467,6 +467,21 @@
         assertArrayEquals(container.mSharedViewNames, new String[] {"e0", "e1", "e2"});
     }
 
+    @Test
+    public void setIntTag() {
+        RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
+        int index = 10;
+        views.setIntTag(
+                R.id.layout, com.android.internal.R.id.notification_action_index_tag, index);
+
+        RemoteViews recovered = parcelAndRecreate(views);
+        RemoteViews cloned = new RemoteViews(recovered);
+        View inflated = cloned.apply(mContext, mContainer);
+
+        assertEquals(
+                index, inflated.getTag(com.android.internal.R.id.notification_action_index_tag));
+    }
+
     private class WidgetContainer extends AppWidgetHostView {
         int[] mSharedViewIds;
         String[] mSharedViewNames;
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 70dc618..90758ba 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -983,6 +983,19 @@
     }
 
     @Test
+    public void testNoAssistItemForTextFieldWithUnsupportedCharacters() throws Throwable {
+        useSystemDefaultTextClassifier();
+        final String text = "\u202Emoc.diordna.com";
+        final TextView textView = mActivity.findViewById(R.id.textview);
+        mActivityRule.runOnUiThread(() -> textView.setText(text));
+        mInstrumentation.waitForIdleSync();
+
+        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('.')));
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
+    }
+
+    @Test
     public void testSelectionMetricsLogger_noAbandonAfterCopy() throws Throwable {
         final List<SelectionEvent> selectionEvents = new ArrayList<>();
         final TextClassifier classifier = new TextClassifier() {
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 9fcb06e..404c99c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -103,6 +103,49 @@
     }
 
     @Test
+    public void setMaxHeight() throws Exception {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+        waitForIdle();
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        final View resolverList = activity.findViewById(R.id.resolver_list);
+        final int initialResolverHeight = resolverList.getHeight();
+
+        activity.runOnUiThread(() -> {
+            ResolverDrawerLayout layout = (ResolverDrawerLayout)
+                    activity.findViewById(
+                            R.id.contentPanel);
+            ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+                = initialResolverHeight - 1;
+            // Force a relayout
+            layout.invalidate();
+            layout.requestLayout();
+        });
+        waitForIdle();
+        assertThat("Drawer should be capped at maxHeight",
+            resolverList.getHeight() == (initialResolverHeight - 1));
+
+        activity.runOnUiThread(() -> {
+            ResolverDrawerLayout layout = (ResolverDrawerLayout)
+                    activity.findViewById(
+                            R.id.contentPanel);
+            ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+                = initialResolverHeight + 1;
+            // Force a relayout
+            layout.invalidate();
+            layout.requestLayout();
+        });
+        waitForIdle();
+        assertThat("Drawer should not change height if its height is less than maxHeight",
+            resolverList.getHeight() == initialResolverHeight);
+    }
+
+    @Test
     public void setShowAtTopToTrue() throws Exception {
         Intent sendIntent = createSendImageIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 3cfc644..225515e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -39,6 +39,10 @@
         BatteryStatsUserLifecycleTests.class,
         KernelCpuProcReaderTest.class,
         KernelCpuProcStringReaderTest.class,
+        KernelCpuUidActiveTimeReaderTest.class,
+        KernelCpuUidClusterTimeReaderTest.class,
+        KernelCpuUidFreqTimeReaderTest.class,
+        KernelCpuUidUserSysTimeReaderTest.class,
         KernelMemoryBandwidthStatsTest.class,
         KernelSingleUidTimeReaderTest.class,
         KernelUidCpuFreqTimeReaderTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
index dae9eb5..2663f2b 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -37,6 +37,7 @@
 import java.io.File;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.nio.CharBuffer;
 import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -149,7 +150,7 @@
                             + "0 0 1 1 1 0 2 0 221",
                     iter.nextLine().toString());
             long[] actual = new long[43];
-            iter.nextLineAsArray(actual);
+            KernelCpuProcStringReader.asLongs(iter.nextLine(), actual);
             assertArrayEquals(
                     new long[]{50227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 0, 0,
                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 721},
@@ -183,7 +184,7 @@
         }
     }
 
-    /** Tests nextLineToArray functionality. */
+    /** Tests reading lines, then converting to long[]. */
     @Test
     public void testReadLineToArray() throws Exception {
         final long[][] data = getTestArray(800, 50);
@@ -193,12 +194,32 @@
         long[] actual = new long[50];
         try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
             for (long[] expected : data) {
-                assertEquals(50, iter.nextLineAsArray(actual));
+                CharBuffer cb = iter.nextLine();
+                String before = cb.toString();
+                assertEquals(50, KernelCpuProcStringReader.asLongs(cb, actual));
                 assertArrayEquals(expected, actual);
+                assertEquals("Buffer not reset to the pos before reading", before, cb.toString());
             }
         }
     }
 
+    /** Tests error handling of converting to long[]. */
+    @Test
+    public void testLineToArrayErrorHandling() {
+        long[] actual = new long[100];
+        String invalidChar = "123: -1234 456";
+        String overflow = "123: 999999999999999999999999999999999999999999999999999999999 123";
+        CharBuffer cb = CharBuffer.wrap("----" + invalidChar + "+++", 4, 4 + invalidChar.length());
+        assertEquals("Failed to report err for: " + invalidChar, -2,
+                KernelCpuProcStringReader.asLongs(cb, actual));
+        assertEquals("Buffer not reset to the same pos before reading", invalidChar, cb.toString());
+
+        cb = CharBuffer.wrap("----" + overflow + "+++", 4, 4 + overflow.length());
+        assertEquals("Failed to report err for: " + overflow, -3,
+                KernelCpuProcStringReader.asLongs(cb, actual));
+        assertEquals("Buffer not reset to the pos before reading", overflow, cb.toString());
+    }
+
     /**
      * Tests that reading a file over the limit (1MB) will return null.
      */
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index b242a34..385bad5 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -39,7 +39,9 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.List;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -56,8 +58,8 @@
             1000, 2000, 3000, 4000,
     };
     private static final int[][] THREAD_CPU_TIMES = {
-            {1, 0, 0, 1},
-            {0, 0, 0, 0},
+            {100, 0, 0, 100},
+            {0, 0, 9999999, 0},
             {1000, 1000, 1000, 1000},
             {0, 1, 2, 3},
     };
@@ -108,6 +110,42 @@
     }
 
     @Test
+    public void testReader_filtersLowTotalCpuUsage() throws IOException {
+        KernelCpuThreadReader.Injector processUtils =
+                new KernelCpuThreadReader.Injector() {
+                    @Override
+                    public int myPid() {
+                        return PROCESS_ID;
+                    }
+
+                    @Override
+                    public int myUid() {
+                        return UID;
+                    }
+
+                    @Override
+                    public int getUidForPid(int pid) {
+                        return 0;
+                    }
+                };
+        setupDirectory(mProcDirectory.toPath().resolve("self"), new int[]{1, 2}, PROCESS_NAME,
+                THREAD_NAMES, new int[]{1000, 2000}, new int[][]{{0, 1}, {100, 0}});
+
+        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+                mProcDirectory.toPath(),
+                mProcDirectory.toPath().resolve("self/task/1/time_in_state"),
+                processUtils);
+        final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+                kernelCpuThreadReader.getCurrentProcessCpuUsage();
+
+        List<Integer> threadIds = processCpuUsage.threadCpuUsages.stream()
+                .map(t -> t.threadId)
+                .collect(Collectors.toList());
+        assertEquals(1, threadIds.size());
+        assertEquals(2, (long) threadIds.get(0));
+    }
+
+    @Test
     public void testReader_byUids() throws IOException {
         int[] uids = new int[]{0, 2, 3, 4, 5, 6000};
         Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4;
@@ -134,7 +172,7 @@
             setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)),
                     new int[]{uid * 10},
                     "process" + uid, new String[]{"thread" + uid}, new int[]{1000},
-                    new int[][]{{uid}});
+                    new int[][]{{uid + 100}});
         }
         final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
                 mProcDirectory.toPath(),
@@ -151,7 +189,7 @@
             int uid = expectedUids[i];
             checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(),
                     uid, uid, new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid},
-                    new int[]{1000}, new int[][]{{uid}});
+                    new int[]{1000}, new int[][]{{uid + 100}});
         }
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
new file mode 100644
index 0000000..adafda0
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseLongArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidActiveTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidActiveTimeReaderTest {
+    private File mTestDir;
+    private File mTestFile;
+    private KernelCpuUidActiveTimeReader mReader;
+    private VerifiableCallback mCallback;
+
+    private Random mRand = new Random(12345);
+    private final int mCpus = 4;
+    private final String mHeadline = "cpus: 4\n";
+    private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() {
+        mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+        mTestFile = new File(mTestDir, "test.file");
+        mReader = new KernelCpuUidActiveTimeReader(
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+        mCallback = new VerifiableCallback();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mTestDir);
+        FileUtils.deleteContents(getContext().getFilesDir());
+    }
+
+    @Test
+    public void testReadDelta() throws Exception {
+        final long[][] times = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], getActiveTime(times[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that a second call will only return deltas.
+        mCallback.clear();
+        final long[][] newTimes1 = increaseTime(times);
+        writeToFile(mHeadline + uidLines(mUids, newTimes1));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that there won't be a callback if the proc file values didn't change.
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that calling with a null callback doesn't result in any crashes
+        mCallback.clear();
+        final long[][] newTimes2 = increaseTime(newTimes1);
+        writeToFile(mHeadline + uidLines(mUids, newTimes2));
+        mReader.readDelta(null);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that the readDelta call will only return deltas when
+        // the previous call had null callback.
+        mCallback.clear();
+        final long[][] newTimes3 = increaseTime(newTimes2);
+        writeToFile(mHeadline + uidLines(mUids, newTimes3));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadAbsolute() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that a second call should still return absolute values
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadDeltaDecreasedTime() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+
+        // Verify that there should not be a callback for a particular UID if its time decreases.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
+        times2[0][0] = 100;
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+
+        // Verify that the internal state was not modified.
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        times3[0] = increaseTime(times1)[0];
+        writeToFile(mHeadline + uidLines(mUids, times3));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testReadDeltaNegativeTime() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+
+        // Verify that there should not be a callback for a particular UID if its time is -ve.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        times2[0][0] *= -1;
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+
+        // Verify that the internal state was not modified.
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        times3[0] = increaseTime(times1)[0];
+        writeToFile(mHeadline + uidLines(mUids, times3));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+    }
+
+    private String uidLines(int[] uids, long[][] times) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < uids.length; i++) {
+            sb.append(uids[i]).append(':');
+            for (int j = 0; j < times[i].length; j++) {
+                sb.append(' ').append(times[i][j] / 10);
+            }
+            sb.append('\n');
+        }
+        return sb.toString();
+    }
+
+    private void writeToFile(String s) throws IOException {
+        try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+            w.write(s);
+            w.flush();
+        }
+    }
+
+    private long[][] increaseTime(long[][] original) {
+        long[][] newTime = new long[original.length][original[0].length];
+        for (int i = 0; i < original.length; i++) {
+            for (int j = 0; j < original[0].length; j++) {
+                newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000;
+            }
+        }
+        return newTime;
+    }
+
+    private long getActiveTime(long[] times) {
+        return times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4;
+    }
+
+    private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<Long> {
+        SparseLongArray mData = new SparseLongArray();
+
+        public void verify(int uid, long time) {
+            assertEquals(time, mData.get(uid));
+            mData.delete(uid);
+        }
+
+        public void clear() {
+            mData.clear();
+        }
+
+        @Override
+        public void onUidCpuTime(int uid, Long time) {
+            mData.put(uid, time);
+        }
+
+        public void verifyNoMoreInteractions() {
+            assertEquals(0, mData.size());
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
new file mode 100644
index 0000000..ad20d84
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidClusterTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidClusterTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidClusterTimeReaderTest {
+    private File mTestDir;
+    private File mTestFile;
+    private KernelCpuUidClusterTimeReader mReader;
+    private VerifiableCallback mCallback;
+
+    private Random mRand = new Random(12345);
+    private final int mCpus = 6;
+    private final String mHeadline = "policy0: 4 policy4: 2\n";
+    private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() {
+        mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+        mTestFile = new File(mTestDir, "test.file");
+        mReader = new KernelCpuUidClusterTimeReader(
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+        mCallback = new VerifiableCallback();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mTestDir);
+        FileUtils.deleteContents(getContext().getFilesDir());
+    }
+
+    @Test
+    public void testReadDelta() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], clusterTime(times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that a second call will only return deltas.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that there won't be a callback if the proc file values didn't change.
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that calling with a null callback doesn't result in any crashes
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        writeToFile(mHeadline + uidLines(mUids, times3));
+        mReader.readDelta(null);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that the readDelta call will only return deltas when
+        // the previous call had null callback.
+        mCallback.clear();
+        final long[][] times4 = increaseTime(times3);
+        writeToFile(mHeadline + uidLines(mUids, times4));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times4[i]), clusterTime(times3[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadAbsolute() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], clusterTime(times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that a second call should still return absolute values
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], clusterTime(times2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadDeltaDecreasedTime() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+
+        // Verify that there should not be a callback for a particular UID if its time decreases.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
+        times2[0][0] = 100;
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+
+        // Verify that the internal state was not modified.
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        times3[0] = increaseTime(times1)[0];
+        writeToFile(mHeadline + uidLines(mUids, times3));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times3[i]), clusterTime(times2[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testReadDeltaNegativeTime() throws Exception {
+        final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
+        writeToFile(mHeadline + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+
+        // Verify that there should not be a callback for a particular UID if its time decreases.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        times2[0][0] *= -1;
+        writeToFile(mHeadline + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+
+        // Verify that the internal state was not modified.
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        times3[0] = increaseTime(times1)[0];
+        writeToFile(mHeadline + uidLines(mUids, times3));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(clusterTime(times3[i]), clusterTime(times2[i])));
+        }
+        mCallback.verifyNoMoreInteractions();
+    }
+
+    private long[] clusterTime(long[] times) {
+        // Assumes 4 + 2 cores
+        return new long[]{times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4,
+                times[4] + times[5] / 2};
+    }
+
+    private String uidLines(int[] uids, long[][] times) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < uids.length; i++) {
+            sb.append(uids[i]).append(':');
+            for (int j = 0; j < times[i].length; j++) {
+                sb.append(' ').append(times[i][j] / 10);
+            }
+            sb.append('\n');
+        }
+        return sb.toString();
+    }
+
+    private void writeToFile(String s) throws IOException {
+        try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+            w.write(s);
+            w.flush();
+        }
+    }
+
+    private long[][] increaseTime(long[][] original) {
+        long[][] newTime = new long[original.length][original[0].length];
+        for (int i = 0; i < original.length; i++) {
+            for (int j = 0; j < original[0].length; j++) {
+                newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000;
+            }
+        }
+        return newTime;
+    }
+
+    private long[] subtract(long[] a1, long[] a2) {
+        long[] val = new long[a1.length];
+        for (int i = 0; i < val.length; ++i) {
+            val[i] = a1[i] - a2[i];
+        }
+        return val;
+    }
+
+    private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+        SparseArray<long[]> mData = new SparseArray<>();
+
+        public void verify(int uid, long[] cpuTimes) {
+            long[] array = mData.get(uid);
+            assertNotNull(array);
+            assertArrayEquals(cpuTimes, array);
+            mData.remove(uid);
+        }
+
+        public void clear() {
+            mData.clear();
+        }
+
+        @Override
+        public void onUidCpuTime(int uid, long[] times) {
+            long[] array = new long[times.length];
+            System.arraycopy(times, 0, array, 0, array.length);
+            mData.put(uid, array);
+        }
+
+        public void verifyNoMoreInteractions() {
+            assertEquals(0, mData.size());
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
new file mode 100644
index 0000000..1d3a98a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidFreqTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidFreqTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidFreqTimeReaderTest {
+    private File mTestDir;
+    private File mTestFile;
+    private KernelCpuUidFreqTimeReader mReader;
+    private VerifiableCallback mCallback;
+    @Mock
+    private PowerProfile mPowerProfile;
+
+    private Random mRand = new Random(12345);
+    private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+        mTestFile = new File(mTestDir, "test.file");
+        mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+        mCallback = new VerifiableCallback();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mTestDir);
+        FileUtils.deleteContents(getContext().getFilesDir());
+    }
+
+    @Test
+    public void testReadFreqs_perClusterTimesNotAvailable() throws Exception {
+        final long[][] freqs = {
+                {1, 12, 123, 1234},
+                {1, 12, 123, 23, 123, 1234, 12345, 123456},
+                {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345},
+                {1, 12, 123, 23, 2345, 234567}
+        };
+        final int[] numClusters = {2, 2, 3, 1};
+        final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
+        for (int i = 0; i < freqs.length; ++i) {
+            mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+                    new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+            setCpuClusterFreqs(numClusters[i], numFreqs[i]);
+            writeToFile(freqsLine(freqs[i]));
+            long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+            assertArrayEquals(freqs[i], actualFreqs);
+            final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
+                    Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
+            assertFalse(errMsg, mReader.perClusterTimesAvailable());
+
+            // Verify that a second call won't read the proc file again
+            assertTrue(mTestFile.delete());
+            actualFreqs = mReader.readFreqs(mPowerProfile);
+            assertArrayEquals(freqs[i], actualFreqs);
+            assertFalse(errMsg, mReader.perClusterTimesAvailable());
+        }
+    }
+
+    @Test
+    public void testReadFreqs_perClusterTimesAvailable() throws Exception {
+        final long[][] freqs = {
+                {1, 12, 123, 1234},
+                {1, 12, 123, 23, 123, 1234, 12345, 123456},
+                {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345, 1234567}
+        };
+        final int[] numClusters = {1, 2, 3};
+        final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
+        for (int i = 0; i < freqs.length; ++i) {
+            mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
+                    new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+            setCpuClusterFreqs(numClusters[i], numFreqs[i]);
+            writeToFile(freqsLine(freqs[i]));
+            long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+            assertArrayEquals(freqs[i], actualFreqs);
+            final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
+                    Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
+            assertTrue(errMsg, mReader.perClusterTimesAvailable());
+
+            // Verify that a second call won't read the proc file again
+            assertTrue(mTestFile.delete());
+            actualFreqs = mReader.readFreqs(mPowerProfile);
+            assertArrayEquals(freqs[i], actualFreqs);
+            assertTrue(errMsg, mReader.perClusterTimesAvailable());
+        }
+    }
+
+    @Test
+    public void testReadDelta() throws Exception {
+        final long[] freqs = {110, 123, 145, 167, 289, 997};
+        final long[][] times = increaseTime(new long[mUids.length][freqs.length]);
+
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], times[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that readDelta also reads the frequencies if not already available.
+        assertTrue(mTestFile.delete());
+        long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+        assertArrayEquals(freqs, actualFreqs);
+
+        // Verify that a second call will only return deltas.
+        mCallback.clear();
+        final long[][] newTimes1 = increaseTime(times);
+        writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes1));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], subtract(newTimes1[i], times[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that there won't be a callback if the proc file values didn't change.
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that calling with a null callback doesn't result in any crashes
+        mCallback.clear();
+        final long[][] newTimes2 = increaseTime(newTimes1);
+        writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes2));
+        mReader.readDelta(null);
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that the readDelta call will only return deltas when
+        // the previous call had null callback.
+        mCallback.clear();
+        final long[][] newTimes3 = increaseTime(newTimes2);
+        writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes3));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], subtract(newTimes3[i], newTimes2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadAbsolute() throws Exception {
+        final long[] freqs = {110, 123, 145, 167, 289, 997};
+        final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
+
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times1[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that readDelta also reads the frequencies if not already available.
+        assertTrue(mTestFile.delete());
+        long[] actualFreqs = mReader.readFreqs(mPowerProfile);
+        assertArrayEquals(freqs, actualFreqs);
+
+        // Verify that a second call should still return absolute values
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times2[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadDeltaWrongData() throws Exception {
+        final long[] freqs = {110, 123, 145, 167, 289, 997};
+        final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
+
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+
+        // Verify that there should not be a callback for a particular UID if its time decreases.
+        mCallback.clear();
+        final long[][] times2 = increaseTime(times1);
+        times2[0][0] = 1000;
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that the internal state was not modified.
+        mCallback.clear();
+        final long[][] times3 = increaseTime(times2);
+        times3[0] = increaseTime(times1)[0];
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times3));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], subtract(times3[0], times1[0]));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times3[i], times2[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that there is no callback if any value in the proc file is -ve.
+        mCallback.clear();
+        final long[][] times4 = increaseTime(times3);
+        times4[0][0] *= -1;
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times4));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; ++i) {
+            mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+
+        // Verify that the internal state was not modified when the proc file had -ve value.
+        mCallback.clear();
+        final long[][] times5 = increaseTime(times4);
+        times5[0] = increaseTime(times3)[0];
+        writeToFile(freqsLine(freqs) + uidLines(mUids, times5));
+        mReader.readDelta(mCallback);
+        mCallback.verify(mUids[0], subtract(times5[0], times3[0]));
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times5[i], times4[i]));
+        }
+
+        assertTrue(mTestFile.delete());
+    }
+
+    private String freqsLine(long[] freqs) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("uid:");
+        for (int i = 0; i < freqs.length; ++i) {
+            sb.append(" " + freqs[i]);
+        }
+        return sb.append('\n').toString();
+    }
+
+    private void setCpuClusterFreqs(int numClusters, int... clusterFreqs) {
+        assertEquals(numClusters, clusterFreqs.length);
+        when(mPowerProfile.getNumCpuClusters()).thenReturn(numClusters);
+        for (int i = 0; i < numClusters; ++i) {
+            when(mPowerProfile.getNumSpeedStepsInCpuCluster(i)).thenReturn(clusterFreqs[i]);
+        }
+    }
+
+    private String uidLines(int[] uids, long[][] times) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < uids.length; i++) {
+            sb.append(uids[i]).append(':');
+            for (int j = 0; j < times[i].length; j++) {
+                sb.append(' ').append(times[i][j] / 10);
+            }
+            sb.append('\n');
+        }
+        return sb.toString();
+    }
+
+    private void writeToFile(String s) throws IOException {
+        try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+            w.write(s);
+            w.flush();
+        }
+    }
+
+    private long[][] increaseTime(long[][] original) {
+        long[][] newTime = new long[original.length][original[0].length];
+        for (int i = 0; i < original.length; i++) {
+            for (int j = 0; j < original[0].length; j++) {
+                newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 10 + 10;
+            }
+        }
+        return newTime;
+    }
+
+    private long[] subtract(long[] a1, long[] a2) {
+        long[] val = new long[a1.length];
+        for (int i = 0; i < val.length; ++i) {
+            val[i] = a1[i] - a2[i];
+        }
+        return val;
+    }
+
+    private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+        SparseArray<long[]> mData = new SparseArray<>();
+
+        public void verify(int uid, long[] cpuTimes) {
+            long[] array = mData.get(uid);
+            assertNotNull(array);
+            assertArrayEquals(cpuTimes, array);
+            mData.remove(uid);
+        }
+
+        public void clear() {
+            mData.clear();
+        }
+
+        @Override
+        public void onUidCpuTime(int uid, long[] times) {
+            long[] array = new long[times.length];
+            System.arraycopy(times, 0, array, 0, array.length);
+            mData.put(uid, array);
+        }
+
+        public void verifyNoMoreInteractions() {
+            assertEquals(0, mData.size());
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
new file mode 100644
index 0000000..9b4512b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuUidUserSysTimeReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidUserSysTimeReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidUserSysTimeReaderTest {
+    private File mTestDir;
+    private File mTestFile;
+    private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mReader;
+    private VerifiableCallback mCallback;
+
+    private Random mRand = new Random(12345);
+    private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
+    private final long[][] mInitialTimes = new long[][]{
+            {15334000, 310964000},
+            {537000, 114000},
+            {40000, 10000},
+            {170000, 57000},
+            {5377000, 867000},
+            {47000, 17000}
+    };
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() {
+        mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+        mTestFile = new File(mTestDir, "test.file");
+        mReader = new KernelCpuUidUserSysTimeReader(
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+        mCallback = new VerifiableCallback();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mTestDir);
+        FileUtils.deleteContents(getContext().getFilesDir());
+    }
+
+    @Test
+    public void testThrottler() throws Exception {
+        mReader = new KernelCpuUidUserSysTimeReader(
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), true);
+        mReader.setThrottle(500);
+
+        writeToFile(uidLines(mUids, mInitialTimes));
+        mReader.readDelta(mCallback);
+        assertEquals(6, mCallback.mData.size());
+
+        long[][] times1 = increaseTime(mInitialTimes);
+        writeToFile(uidLines(mUids, times1));
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        assertEquals(0, mCallback.mData.size());
+
+        SystemClock.sleep(600);
+
+        long[][] times2 = increaseTime(times1);
+        writeToFile(uidLines(mUids, times2));
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        assertEquals(6, mCallback.mData.size());
+
+        long[][] times3 = increaseTime(times2);
+        writeToFile(uidLines(mUids, times3));
+        mCallback.clear();
+        mReader.readDelta(mCallback);
+        assertEquals(0, mCallback.mData.size());
+    }
+
+    @Test
+    public void testReadDelta() throws Exception {
+        final long[][] times1 = mInitialTimes;
+        writeToFile(uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times1[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+
+        // Verify that a second call will only return deltas.
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+
+        // Verify that there won't be a callback if the proc file values didn't change.
+        mReader.readDelta(mCallback);
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+
+        // Verify that calling with a null callback doesn't result in any crashes
+        final long[][] times3 = increaseTime(times2);
+        writeToFile(uidLines(mUids, times3));
+        mReader.readDelta(null);
+
+        // Verify that the readDelta call will only return deltas when
+        // the previous call had null callback.
+        final long[][] times4 = increaseTime(times3);
+        writeToFile(uidLines(mUids, times4));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadDeltaWrongData() throws Exception {
+        final long[][] times1 = mInitialTimes;
+        writeToFile(uidLines(mUids, times1));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times1[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+
+        // Verify that there should not be a callback for a particular UID if its time decreases.
+        final long[][] times2 = increaseTime(times1);
+        times2[0][0] = 1000;
+        writeToFile(uidLines(mUids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 1; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+        assertTrue(mTestFile.delete());
+    }
+
+    @Test
+    public void testReadAbsolute() throws Exception {
+        final long[][] times1 = mInitialTimes;
+        writeToFile(uidLines(mUids, times1));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times1[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+
+        // Verify that a second call should still return absolute values
+        final long[][] times2 = increaseTime(times1);
+        writeToFile(uidLines(mUids, times2));
+        mReader.readAbsolute(mCallback);
+        for (int i = 0; i < mUids.length; i++) {
+            mCallback.verify(mUids[i], times2[i]);
+        }
+        mCallback.verifyNoMoreInteractions();
+        mCallback.clear();
+        assertTrue(mTestFile.delete());
+    }
+
+    private String uidLines(int[] uids, long[][] times) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < uids.length; i++) {
+            sb.append(uids[i]).append(':');
+            for (int j = 0; j < times[i].length; j++) {
+                sb.append(' ').append(times[i][j]);
+            }
+            sb.append('\n');
+        }
+        return sb.toString();
+    }
+
+    private void writeToFile(String s) throws IOException {
+        try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+            w.write(s);
+            w.flush();
+        }
+    }
+
+    private long[][] increaseTime(long[][] original) {
+        long[][] newTime = new long[original.length][original[0].length];
+        for (int i = 0; i < original.length; i++) {
+            for (int j = 0; j < original[0].length; j++) {
+                newTime[i][j] = original[i][j] + mRand.nextInt(1000) * 1000 + 1000;
+            }
+        }
+        return newTime;
+    }
+
+    private long[] subtract(long[] a1, long[] a2) {
+        long[] val = new long[a1.length];
+        for (int i = 0; i < val.length; ++i) {
+            val[i] = a1[i] - a2[i];
+        }
+        return val;
+    }
+
+    private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<long[]> {
+        SparseArray<long[]> mData = new SparseArray<>();
+
+        public void verify(int uid, long[] cpuTimes) {
+            long[] array = mData.get(uid);
+            assertNotNull(array);
+            assertArrayEquals(cpuTimes, array);
+            mData.remove(uid);
+        }
+
+        public void clear() {
+            mData.clear();
+        }
+
+        @Override
+        public void onUidCpuTime(int uid, long[] times) {
+            long[] array = new long[times.length];
+            System.arraycopy(times, 0, array, 0, array.length);
+            mData.put(uid, array);
+        }
+
+        public void verifyNoMoreInteractions() {
+            assertEquals(0, mData.size());
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index f637b7c..31dde5c 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -105,7 +105,6 @@
         assertThat(entry.recordedDelayMessageCount).isEqualTo(1);
         assertThat(entry.delayMillis).isEqualTo(30);
         assertThat(entry.maxDelayMillis).isEqualTo(30);
-
     }
 
     @Test
@@ -429,6 +428,28 @@
         assertThat(entries).hasSize(0);
     }
 
+    @Test
+    public void testAddsDebugEntries() {
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+        looperStats.setAddDebugEntries(true);
+
+        Message message = mHandlerFirst.obtainMessage(1000);
+        message.when = looperStats.getSystemUptimeMillis();
+        Object token = looperStats.messageDispatchStarting();
+        looperStats.messageDispatched(token, message);
+
+        List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+        assertThat(entries).hasSize(3);
+        LooperStats.ExportedEntry debugEntry1 = entries.get(1);
+        assertThat(debugEntry1.handlerClassName).isEqualTo("");
+        assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis");
+        assertThat(debugEntry1.maxDelayMillis).isEqualTo(looperStats.getStartTimeMillis());
+        LooperStats.ExportedEntry debugEntry2 = entries.get(2);
+        assertThat(debugEntry2.handlerClassName).isEqualTo("");
+        assertThat(debugEntry2.messageName).isEqualTo("__DEBUG_end_time_millis");
+        assertThat(debugEntry2.maxDelayMillis).isAtLeast(looperStats.getStartTimeMillis());
+    }
+
     private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
         try {
             r.run();
@@ -450,6 +471,7 @@
             super(samplingInterval, sizeCap);
             this.mSamplingInterval = samplingInterval;
             this.setDeviceState(mDeviceState.getReadonlyClient());
+            this.setAddDebugEntries(false);
         }
 
         void tickRealtime(long micros) {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index a4c5ed2..141948f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -201,6 +201,24 @@
         <new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     </split-permission>
 
+    <!-- Apps holding either the legacy READ or WRITE permissions will inherit
+         the ability to <em>read</em> new typed permissions in the Q release; they
+         won't gain the ability to <em>write</em> that content. -->
+    <!-- STOPSHIP(b/112545973): change targetSdk to Q when SDK version finalised -->
+    <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+                      targetSdk="10000">
+        <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+        <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+        <new-permission name="android.permission.READ_MEDIA_IMAGES" />
+    </split-permission>
+    <!-- STOPSHIP(b/112545973): change targetSdk to Q when SDK version finalised -->
+    <split-permission name="android.permission.WRITE_EXTERNAL_STORAGE"
+                      targetSdk="10000">
+        <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+        <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+        <new-permission name="android.permission.READ_MEDIA_IMAGES" />
+    </split-permission>
+
     <!-- This is a list of all the libraries available for application
          code to link against. -->
 
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index de110c8..d9da27c 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -82,6 +82,17 @@
     }
 
     /**
+     * Add two Insets.
+     *
+     * @param a The first Insets to add.
+     * @param b The second Insets to add.
+     * @return a + b, i. e. all insets on every side are added together.
+     */
+    public static @NonNull Insets add(@NonNull Insets a, @NonNull Insets b) {
+        return Insets.of(a.left + b.left, a.top + b.top, a.right + b.right, a.bottom + b.bottom);
+    }
+
+    /**
      * Two Insets instances are equal iff they belong to the same class and their fields are
      * pairwise equal.
      *
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index c4dc0ad..40a32f3 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -106,6 +106,20 @@
     }
 
     /**
+     * @hide
+     */
+    public Rect(@Nullable Insets r) {
+        if (r == null) {
+            left = top = right = bottom = 0;
+        } else {
+            left = r.left;
+            top = r.top;
+            right = r.right;
+            bottom = r.bottom;
+        }
+    }
+
+    /**
      * Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise.
      *
      * @hide
@@ -418,6 +432,18 @@
     }
 
     /**
+     * Insets the rectangle on all sides specified by the dimensions of {@code insets}.
+     * @hide
+     * @param insets The insets to inset the rect by.
+     */
+    public void inset(Insets insets) {
+        left += insets.left;
+        top += insets.top;
+        right -= insets.right;
+        bottom -= insets.bottom;
+    }
+
+    /**
      * Insets the rectangle on all sides specified by the insets.
      * @hide
      * @param left The amount to add from the rectangle's left
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index bf969ef..acb46b3 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -652,6 +652,17 @@
         private @Nullable FontStyle mStyle;
 
         /**
+         * Returns the maximum capacity of custom fallback families.
+         *
+         * This includes the the first font family passed to the constructor.
+         *
+         * @return the maximum number of font families for the custom fallback
+         */
+        public static @IntRange(from = 1) int getMaxCustomFallbackCount() {
+            return MAX_CUSTOM_FALLBACK;
+        }
+
+        /**
          * Constructs a builder with a font family.
          *
          * @param family a family object
@@ -706,8 +717,8 @@
          */
         public CustomFallbackBuilder addCustomFallback(@NonNull FontFamily family) {
             Preconditions.checkNotNull(family);
-            Preconditions.checkArgument(mFamilies.size() < MAX_CUSTOM_FALLBACK,
-                    "Custom fallback limit exceeded(" + MAX_CUSTOM_FALLBACK + ")");
+            Preconditions.checkArgument(mFamilies.size() < getMaxCustomFallbackCount(),
+                    "Custom fallback limit exceeded(" + getMaxCustomFallbackCount() + ")");
             mFamilies.add(family);
             return this;
         }
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index fdd638a..dea2f45 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -108,11 +108,10 @@
      * Scaled mask based on the view bounds.
      */
     private final Path mMask;
+    private final Path mMaskScaleOnly;
     private final Matrix mMaskMatrix;
     private final Region mTransparentRegion;
 
-    private Bitmap mMaskBitmap;
-
     /**
      * Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and
      * background layer.
@@ -156,8 +155,8 @@
             sMask = PathParser.createPathFromPathData(
                 Resources.getSystem().getString(R.string.config_icon_mask));
         }
-        mMask = PathParser.createPathFromPathData(
-            Resources.getSystem().getString(R.string.config_icon_mask));
+        mMask = new Path(sMask);
+        mMaskScaleOnly = new Path(mMask);
         mMaskMatrix = new Matrix();
         mCanvas = new Canvas();
         mTransparentRegion = new Region();
@@ -329,24 +328,19 @@
     }
 
     private void updateMaskBoundsInternal(Rect b) {
+        // reset everything that depends on the view bounds
         mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
+        sMask.transform(mMaskMatrix, mMaskScaleOnly);
+
+        mMaskMatrix.postTranslate(b.left, b.top);
         sMask.transform(mMaskMatrix, mMask);
 
-        if (mMaskBitmap == null || mMaskBitmap.getWidth() != b.width() ||
-            mMaskBitmap.getHeight() != b.height()) {
-            mMaskBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ALPHA_8);
+        if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width()
+                || mLayersBitmap.getHeight() != b.height()) {
             mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
         }
-        // mMaskBitmap bound [0, w] x [0, h]
-        mCanvas.setBitmap(mMaskBitmap);
-        mPaint.setShader(null);
-        mCanvas.drawPath(mMask, mPaint);
 
-        // mMask bound [left, top, right, bottom]
-        mMaskMatrix.postTranslate(b.left, b.top);
-        mMask.reset();
-        sMask.transform(mMaskMatrix, mMask);
-        // reset everything that depends on the view bounds
+        mPaint.setShader(null);
         mTransparentRegion.setEmpty();
         mLayersShader = null;
     }
@@ -371,9 +365,11 @@
             mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
             mPaint.setShader(mLayersShader);
         }
-        if (mMaskBitmap != null) {
+        if (mMaskScaleOnly != null) {
             Rect bounds = getBounds();
-            canvas.drawBitmap(mMaskBitmap, bounds.left, bounds.top, mPaint);
+            canvas.translate(bounds.left, bounds.top);
+            canvas.drawPath(mMaskScaleOnly, mPaint);
+            canvas.translate(-bounds.left, -bounds.top);
         }
     }
 
@@ -549,7 +545,7 @@
 
         final ChildDrawable[] layers = mLayerState.mChildren;
         for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
-            if (layers[i].mDrawable.isProjected()) {
+            if (layers[i].mDrawable != null && layers[i].mDrawable.isProjected()) {
                 return true;
             }
         }
@@ -674,7 +670,7 @@
 
     @Override
     public int getAlpha() {
-        return PixelFormat.TRANSLUCENT;
+        return mPaint.getAlpha();
     }
 
     @Override
@@ -718,10 +714,7 @@
 
     @Override
     public int getOpacity() {
-        if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
-            return mLayerState.mOpacityOverride;
-        }
-        return mLayerState.getOpacity();
+        return PixelFormat.TRANSLUCENT;
     }
 
     @Override
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 1cb0d25..365be10 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -349,7 +349,7 @@
                 goto exit;
             }
         }
-        ret = tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
+        ret = tables[1].createIdmap(tables[0], targetCrc, overlayCrc,
                 targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
     }
 
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 76db18d..2fe98b0 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -26,7 +26,9 @@
 
 #include <algorithm>
 #include <limits>
+#include <map>
 #include <memory>
+#include <set>
 #include <type_traits>
 
 #include <android-base/macros.h>
@@ -7033,170 +7035,210 @@
     return NO_ERROR;
 }
 
-struct IdmapTypeMap {
-    ssize_t overlayTypeId;
-    size_t entryOffset;
-    Vector<uint32_t> entryMap;
+class IdmapMatchingResources;
+
+class IdmapTypeMapping {
+public:
+    void add(uint32_t targetResId, uint32_t overlayResId) {
+        uint8_t targetTypeId = Res_GETTYPE(targetResId);
+        if (mData.find(targetTypeId) == mData.end()) {
+            mData.emplace(targetTypeId, std::set<std::pair<uint32_t, uint32_t>>());
+        }
+        auto& entries = mData[targetTypeId];
+        entries.insert(std::make_pair(targetResId, overlayResId));
+    }
+
+    bool empty() const {
+        return mData.empty();
+    }
+
+private:
+    // resource type ID in context of target -> set of resource entries mapping target -> overlay
+    std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>> mData;
+
+    friend IdmapMatchingResources;
 };
 
-status_t ResTable::createIdmap(const ResTable& overlay,
+class IdmapMatchingResources {
+public:
+    IdmapMatchingResources(std::unique_ptr<IdmapTypeMapping> tm) : mTypeMapping(std::move(tm)) {
+        assert(mTypeMapping);
+        for (auto ti = mTypeMapping->mData.cbegin(); ti != mTypeMapping->mData.cend(); ++ti) {
+            uint32_t lastSeen = 0xffffffff;
+            size_t totalEntries = 0;
+            for (auto ei = ti->second.cbegin(); ei != ti->second.cend(); ++ei) {
+                assert(lastSeen == 0xffffffff || lastSeen < ei->first);
+                mEntryPadding[ei->first] = (lastSeen == 0xffffffff) ? 0 : ei->first - lastSeen - 1;
+                lastSeen = ei->first;
+                totalEntries += 1 + mEntryPadding[ei->first];
+            }
+            mNumberOfEntriesIncludingPadding[ti->first] = totalEntries;
+        }
+    }
+
+    const auto& getTypeMapping() const {
+        return mTypeMapping->mData;
+    }
+
+    size_t getNumberOfEntriesIncludingPadding(uint8_t type) const {
+        return mNumberOfEntriesIncludingPadding.at(type);
+    }
+
+    size_t getPadding(uint32_t resid) const {
+        return mEntryPadding.at(resid);
+    }
+
+private:
+    // resource type ID in context of target -> set of resource entries mapping target -> overlay
+    const std::unique_ptr<IdmapTypeMapping> mTypeMapping;
+
+    // resource ID in context of target -> trailing padding for that resource (call FixPadding
+    // before use)
+    std::map<uint32_t, size_t> mEntryPadding;
+
+    // resource type ID in context of target -> total number of entries, including padding entries,
+    // for that type (call FixPadding before use)
+    std::map<uint8_t, size_t> mNumberOfEntriesIncludingPadding;
+};
+
+status_t ResTable::createIdmap(const ResTable& targetResTable,
         uint32_t targetCrc, uint32_t overlayCrc,
         const char* targetPath, const char* overlayPath,
         void** outData, size_t* outSize) const
 {
-    // see README for details on the format of map
-    if (mPackageGroups.size() == 0) {
-        ALOGW("idmap: target package has no package groups, cannot create idmap\n");
+    if (targetPath == NULL || overlayPath == NULL || outData == NULL || outSize == NULL) {
+        ALOGE("idmap: unexpected NULL parameter");
+        return UNKNOWN_ERROR;
+    }
+    if (strlen(targetPath) > 255) {
+        ALOGE("idmap: target path exceeds idmap file format limit of 255 chars");
+        return UNKNOWN_ERROR;
+    }
+    if (strlen(overlayPath) > 255) {
+        ALOGE("idmap: overlay path exceeds idmap file format limit of 255 chars");
+        return UNKNOWN_ERROR;
+    }
+    if (mPackageGroups.size() == 0 || mPackageGroups[0]->packages.size() == 0) {
+        ALOGE("idmap: invalid overlay package");
+        return UNKNOWN_ERROR;
+    }
+    if (targetResTable.mPackageGroups.size() == 0 ||
+            targetResTable.mPackageGroups[0]->packages.size() == 0) {
+        ALOGE("idmap: invalid target package");
         return UNKNOWN_ERROR;
     }
 
-    if (mPackageGroups[0]->packages.size() == 0) {
-        ALOGW("idmap: target package has no packages in its first package group, "
-                "cannot create idmap\n");
-        return UNKNOWN_ERROR;
-    }
+    const ResTable_package* targetPackageStruct =
+        targetResTable.mPackageGroups[0]->packages[0]->package;
+    const size_t tmpNameSize = arraysize(targetPackageStruct->name);
+    char16_t tmpName[tmpNameSize];
+    strcpy16_dtoh(tmpName, targetPackageStruct->name, tmpNameSize);
+    const String16 targetPackageName(tmpName);
 
-    // The number of resources overlaid that were not explicitly marked overlayable.
+    const PackageGroup* packageGroup = mPackageGroups[0];
+
+    // the number of resources overlaid that were not explicitly marked overlayable
     size_t forcedOverlayCount = 0u;
 
-    KeyedVector<uint8_t, IdmapTypeMap> map;
-
-    // overlaid packages are assumed to contain only one package group
-    const PackageGroup* pg = mPackageGroups[0];
-
-    // starting size is header
-    *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES;
-
-    // target package id and number of types in map
-    *outSize += 2 * sizeof(uint16_t);
-
-    // overlay packages are assumed to contain only one package group
-    const ResTable_package* overlayPackageStruct = overlay.mPackageGroups[0]->packages[0]->package;
-    char16_t tmpName[sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0])];
-    strcpy16_dtoh(tmpName, overlayPackageStruct->name, sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0]));
-    const String16 overlayPackage(tmpName);
-
-    for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
-        const TypeList& typeList = pg->types[typeIndex];
+    // find the resources that exist in both packages
+    auto typeMapping = std::make_unique<IdmapTypeMapping>();
+    for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) {
+        const TypeList& typeList = packageGroup->types[typeIndex];
         if (typeList.isEmpty()) {
             continue;
         }
-
         const Type* typeConfigs = typeList[0];
 
-        IdmapTypeMap typeMap;
-        typeMap.overlayTypeId = -1;
-        typeMap.entryOffset = 0;
-
         for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
-            uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex);
-            resource_name resName;
-            if (!this->getResourceName(resID, false, &resName)) {
-                if (typeMap.entryMap.isEmpty()) {
-                    typeMap.entryOffset++;
-                }
+            uint32_t overlay_resid = Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex);
+            resource_name current_res;
+            if (!getResourceName(overlay_resid, false, &current_res)) {
                 continue;
             }
 
             uint32_t typeSpecFlags = 0u;
-            const String16 overlayType(resName.type, resName.typeLen);
-            const String16 overlayName(resName.name, resName.nameLen);
-            uint32_t overlayResID = overlay.identifierForName(overlayName.string(),
-                                                              overlayName.size(),
-                                                              overlayType.string(),
-                                                              overlayType.size(),
-                                                              overlayPackage.string(),
-                                                              overlayPackage.size(),
-                                                              &typeSpecFlags);
-            if (overlayResID == 0) {
-                // No such target resource was found.
-                if (typeMap.entryMap.isEmpty()) {
-                    typeMap.entryOffset++;
-                }
+            const uint32_t target_resid = targetResTable.identifierForName(
+                    current_res.name,
+                    current_res.nameLen,
+                    current_res.type,
+                    current_res.typeLen,
+                    targetPackageName.string(),
+                    targetPackageName.size(),
+                    &typeSpecFlags);
+
+            if (target_resid == 0) {
                 continue;
             }
 
-            // Now that we know this is being overlaid, check if it can be, and emit a warning if
-            // it can't.
             if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) &
                     ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) {
-                forcedOverlayCount++;
+                ++forcedOverlayCount;
             }
 
-            if (typeMap.overlayTypeId == -1) {
-                typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1;
-            }
-
-            if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) {
-                ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x"
-                        " but entries should map to resources of type %02zx",
-                        resID, overlayResID, typeMap.overlayTypeId);
-                return BAD_TYPE;
-            }
-
-            if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) {
-                // pad with 0xffffffff's (indicating non-existing entries) before adding this entry
-                size_t index = typeMap.entryMap.size();
-                size_t numItems = entryIndex - (typeMap.entryOffset + index);
-                if (typeMap.entryMap.insertAt(0xffffffff, index, numItems) < 0) {
-                    return NO_MEMORY;
-                }
-            }
-            typeMap.entryMap.add(Res_GETENTRY(overlayResID));
-        }
-
-        if (!typeMap.entryMap.isEmpty()) {
-            if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) {
-                return NO_MEMORY;
-            }
-            *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t));
+            typeMapping->add(target_resid, overlay_resid);
         }
     }
 
-    if (map.isEmpty()) {
-        ALOGW("idmap: no resources in overlay package present in base package");
+    if (typeMapping->empty()) {
+        ALOGE("idmap: no matching resources");
         return UNKNOWN_ERROR;
     }
 
+    const IdmapMatchingResources matchingResources(std::move(typeMapping));
+
+    // write idmap
+    *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; // magic, version, target and overlay crc
+    *outSize += 2 * sizeof(uint16_t); // target package id, type count
+    auto fixedTypeMapping = matchingResources.getTypeMapping();
+    const auto typesEnd = fixedTypeMapping.cend();
+    for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
+        *outSize += 4 * sizeof(uint16_t); // target type, overlay type, entry count, entry offset
+        *outSize += matchingResources.getNumberOfEntriesIncludingPadding(ti->first) *
+            sizeof(uint32_t); // entries
+    }
     if ((*outData = malloc(*outSize)) == NULL) {
         return NO_MEMORY;
     }
 
-    uint32_t* data = (uint32_t*)*outData;
-    *data++ = htodl(IDMAP_MAGIC);
-    *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION);
-    *data++ = htodl(targetCrc);
-    *data++ = htodl(overlayCrc);
-    const char* paths[] = { targetPath, overlayPath };
-    for (int j = 0; j < 2; ++j) {
-        char* p = (char*)data;
-        const char* path = paths[j];
-        const size_t I = strlen(path);
-        if (I > 255) {
-            ALOGV("path exceeds expected 255 characters: %s\n", path);
-            return UNKNOWN_ERROR;
-        }
-        for (size_t i = 0; i < 256; ++i) {
-            *p++ = i < I ? path[i] : '\0';
-        }
-        data += 256 / sizeof(uint32_t);
-    }
-    const size_t mapSize = map.size();
-    uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
-    *typeData++ = htods(pg->id);
-    *typeData++ = htods(mapSize);
-    for (size_t i = 0; i < mapSize; ++i) {
-        uint8_t targetTypeId = map.keyAt(i);
-        const IdmapTypeMap& typeMap = map[i];
-        *typeData++ = htods(targetTypeId + 1);
-        *typeData++ = htods(typeMap.overlayTypeId);
-        *typeData++ = htods(typeMap.entryMap.size());
-        *typeData++ = htods(typeMap.entryOffset);
+    // write idmap header
+    uint32_t* data = reinterpret_cast<uint32_t*>(*outData);
+    *data++ = htodl(IDMAP_MAGIC); // write: magic
+    *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION); // write: version
+    *data++ = htodl(targetCrc); // write: target crc
+    *data++ = htodl(overlayCrc); // write: overlay crc
 
-        const size_t entryCount = typeMap.entryMap.size();
-        uint32_t* entries = reinterpret_cast<uint32_t*>(typeData);
-        for (size_t j = 0; j < entryCount; j++) {
-            entries[j] = htodl(typeMap.entryMap[j]);
+    char* charData = reinterpret_cast<char*>(data);
+    size_t pathLen = strlen(targetPath);
+    for (size_t i = 0; i < 256; ++i) {
+        *charData++ = i < pathLen ? targetPath[i] : '\0'; // write: target path
+    }
+    pathLen = strlen(overlayPath);
+    for (size_t i = 0; i < 256; ++i) {
+        *charData++ = i < pathLen ? overlayPath[i] : '\0'; // write: overlay path
+    }
+    data += (2 * 256) / sizeof(uint32_t);
+
+    // write idmap data header
+    uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
+    *typeData++ = htods(targetPackageStruct->id); // write: target package id
+    *typeData++ =
+        htods(static_cast<uint16_t>(fixedTypeMapping.size())); // write: type count
+
+    // write idmap data
+    for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
+        const size_t entryCount = matchingResources.getNumberOfEntriesIncludingPadding(ti->first);
+        auto ei = ti->second.cbegin();
+        *typeData++ = htods(Res_GETTYPE(ei->first) + 1); // write: target type id
+        *typeData++ = htods(Res_GETTYPE(ei->second) + 1); // write: overlay type id
+        *typeData++ = htods(entryCount); // write: entry count
+        *typeData++ = htods(Res_GETENTRY(ei->first)); // write: (target) entry offset
+        uint32_t *entryData = reinterpret_cast<uint32_t*>(typeData);
+        for (; ei != ti->second.cend(); ++ei) {
+            const size_t padding = matchingResources.getPadding(ei->first);
+            for (size_t i = 0; i < padding; ++i) {
+                *entryData++ = htodl(0xffffffff); // write: padding
+            }
+            *entryData++ = htodl(Res_GETENTRY(ei->second)); // write: (overlay) entry
         }
         typeData += entryCount * 2;
     }
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 59abad45..ad33fcf 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1990,7 +1990,7 @@
     // Return value: on success: NO_ERROR; caller is responsible for free-ing
     // outData (using free(3)). On failure, any status_t value other than
     // NO_ERROR; the caller should not free outData.
-    status_t createIdmap(const ResTable& overlay,
+    status_t createIdmap(const ResTable& targetResTable,
             uint32_t targetCrc, uint32_t overlayCrc,
             const char* targetPath, const char* overlayPath,
             void** outData, size_t* outSize) const;
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 26d2896..10b83a7 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -40,7 +40,7 @@
     ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size()));
 
     char target_name[256] = "com.android.basic";
-    ASSERT_EQ(NO_ERROR, target_table_.createIdmap(overlay_table, 0, 0, target_name, target_name,
+    ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name,
                                                   &data_, &data_size_));
   }
 
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 17d2db71..4a5b61a 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -45,9 +45,6 @@
     ],
 
     product_variables: {
-        device_uses_hwc2: {
-            cflags: ["-DUSE_HWC2"],
-        },
         eng: {
             lto: {
                 never: true,
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index b772e5b..3bee301 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -85,19 +85,18 @@
             mUpdateTexImage = false;
             sk_sp<SkImage> layerImage;
             SkMatrix textureTransform;
-            android_dataspace dataSpace;
             bool queueEmpty = true;
             // If the SurfaceTexture queue is in synchronous mode, need to discard all
             // but latest frame. Since we can't tell which mode it is in,
             // do this unconditionally.
             do {
-                layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty,
+                layerImage = mSurfaceTexture->dequeueImage(textureTransform, &queueEmpty,
                         mRenderState);
             } while (layerImage.get() && (!queueEmpty));
             if (layerImage.get()) {
                 // force filtration if buffer size != layer size
                 bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height();
-                updateLayer(forceFilter, textureTransform, dataSpace, layerImage);
+                updateLayer(forceFilter, textureTransform, layerImage);
             }
         }
 
@@ -109,12 +108,11 @@
 }
 
 void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform,
-        android_dataspace dataspace, const sk_sp<SkImage>& layerImage) {
+        const sk_sp<SkImage>& layerImage) {
     mLayer->setBlend(mBlend);
     mLayer->setForceFilter(forceFilter);
     mLayer->setSize(mWidth, mHeight);
     mLayer->getTexTransform() = textureTransform;
-    mLayer->setDataSpace(dataspace);
     mLayer->setImage(layerImage);
 }
 
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index b2c5131..a91c111 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -95,7 +95,7 @@
     void detachSurfaceTexture();
 
     void updateLayer(bool forceFilter, const SkMatrix& textureTransform,
-            android_dataspace dataspace, const sk_sp<SkImage>& layerImage);
+                     const sk_sp<SkImage>& layerImage);
 
     void destroyLayer();
 
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index e7ae767..ccbb6c1 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -82,7 +82,6 @@
 JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo) {
     mGlobalData = globalData;
     nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps);
-#if USE_HWC2
     nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms);
     nsecs_t offsetDelta = sfOffset - displayInfo.appVsyncOffset;
     // There are two different offset cases. If the offsetDelta is positive
@@ -96,7 +95,6 @@
         // return due to the staggering of VSYNC-app & VSYNC-sf.
         mDequeueTimeForgiveness = offsetDelta + 4_ms;
     }
-#endif
     setFrameInterval(frameIntervalNanos);
 }
 
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 32aaa54..d0df200 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -33,7 +33,6 @@
     // TODO: This is a violation of Android's typical ref counting, but it
     // preserves the old inc/dec ref locations. This should be changed...
     incStrong(nullptr);
-    buildColorSpaceWithFilter();
     renderState.registerLayer(this);
     texTransform.setIdentity();
     transform.setIdentity();
@@ -43,36 +42,6 @@
     mRenderState.unregisterLayer(this);
 }
 
-void Layer::setColorFilter(sk_sp<SkColorFilter> filter) {
-    if (filter != mColorFilter) {
-        mColorFilter = filter;
-        buildColorSpaceWithFilter();
-    }
-}
-
-void Layer::setDataSpace(android_dataspace dataspace) {
-    if (dataspace != mCurrentDataspace) {
-        mCurrentDataspace = dataspace;
-        buildColorSpaceWithFilter();
-    }
-}
-
-void Layer::buildColorSpaceWithFilter() {
-    sk_sp<SkColorFilter> colorSpaceFilter;
-    sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(mCurrentDataspace);
-    if (colorSpace && !colorSpace->isSRGB()) {
-        colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
-    }
-
-    if (mColorFilter && colorSpaceFilter) {
-        mColorSpaceWithFilter = mColorFilter->makeComposed(colorSpaceFilter);
-    } else if (colorSpaceFilter) {
-        mColorSpaceWithFilter = colorSpaceFilter;
-    } else {
-        mColorSpaceWithFilter = mColorFilter;
-    }
-}
-
 void Layer::postDecStrong() {
     mRenderState.postDecStrong(this);
 }
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e4f96e9..98600db 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -69,15 +69,9 @@
 
     SkBlendMode getMode() const;
 
-    inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
+    inline sk_sp<SkColorFilter> getColorFilter() const { return mColorFilter; }
 
-    void setColorFilter(sk_sp<SkColorFilter> filter);
-
-    void setDataSpace(android_dataspace dataspace);
-
-    void setColorSpace(sk_sp<SkColorSpace> colorSpace);
-
-    inline sk_sp<SkColorFilter> getColorSpaceWithFilter() const { return mColorSpaceWithFilter; }
+    void setColorFilter(sk_sp<SkColorFilter> filter) { mColorFilter = filter; };
 
     inline SkMatrix& getTexTransform() { return texTransform; }
 
@@ -98,24 +92,12 @@
     RenderState& mRenderState;
 
 private:
-    void buildColorSpaceWithFilter();
-
     /**
      * Color filter used to draw this layer. Optional.
      */
     sk_sp<SkColorFilter> mColorFilter;
 
     /**
-     * Colorspace of the contents of the layer. Optional.
-     */
-    android_dataspace mCurrentDataspace = HAL_DATASPACE_UNKNOWN;
-
-    /**
-     * A color filter that is the combination of the mColorFilter and mColorSpace. Optional.
-     */
-    sk_sp<SkColorFilter> mColorSpaceWithFilter;
-
-    /**
      * Indicates raster data backing the layer is scaled, requiring filtration.
      */
     bool forceFilter = false;
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index f0a3a95..1b5cb60 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -27,6 +27,7 @@
 namespace uirenderer {
 
 #define SK_MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
+#define SK_MATRIX_STRING_V "[%.9f %.9f %.9f] [%.9f %.9f %.9f] [%.9f %.9f %.9f]"
 #define SK_MATRIX_ARGS(m)                                                                      \
     (m)->get(0), (m)->get(1), (m)->get(2), (m)->get(3), (m)->get(4), (m)->get(5), (m)->get(6), \
             (m)->get(7), (m)->get(8)
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 80f2b57..2a48837 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -66,13 +66,10 @@
 
     sk_sp<SkColorSpace> colorSpace =
             DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace()));
-    sk_sp<SkColorFilter> colorSpaceFilter;
-    if (colorSpace && !colorSpace->isSRGB()) {
-        colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
-    }
     sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
-            reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()), kPremul_SkAlphaType);
-    return copyImageInto(image, colorSpaceFilter, texTransform, srcRect, bitmap);
+            reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()),
+            kPremul_SkAlphaType, colorSpace);
+    return copyImageInto(image, texTransform, srcRect, bitmap);
 }
 
 CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
@@ -83,20 +80,7 @@
     transform.loadScale(1, -1, 1);
     transform.translate(0, -1);
 
-    // TODO: Try to take and reuse the image inside HW bitmap with "hwBitmap->makeImage".
-    // TODO: When this was attempted, it resulted in instability.
-    sk_sp<SkColorFilter> colorSpaceFilter;
-    sk_sp<SkColorSpace> colorSpace = hwBitmap->info().refColorSpace();
-    if (colorSpace && !colorSpace->isSRGB()) {
-        colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
-    }
-    sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
-            reinterpret_cast<AHardwareBuffer*>(hwBitmap->graphicBuffer()), kPremul_SkAlphaType);
-
-    // HW Bitmap currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 format
-    // and SRGB color space. ImageDecoder can create a new HW Bitmap with non-SRGB color space: for
-    // example see android.graphics.cts.BitmapColorSpaceTest#testEncodeP3hardware test.
-    return copyImageInto(image, colorSpaceFilter, transform, srcRect, bitmap);
+    return copyImageInto(hwBitmap->makeImage(), transform, srcRect, bitmap);
 }
 
 CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
@@ -118,8 +102,7 @@
     return copyResult;
 }
 
-CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image,
-                                   sk_sp<SkColorFilter>& colorSpaceFilter, Matrix4& texTransform,
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
                                    const Rect& srcRect, SkBitmap* bitmap) {
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
         mRenderThread.requireGlContext();
@@ -157,11 +140,7 @@
         return copyResult;
     }
 
-    // See Readback::copyLayerInto for an overview of color space conversion.
-    // HW Bitmap are allowed to be in a non-SRGB color space (for example coming from ImageDecoder).
-    // For Surface and HW Bitmap readback flows we pass colorSpaceFilter, which does the conversion.
-    // TextureView readback is using Layer::setDataSpace, which creates a SkColorFilter internally.
-    Layer layer(mRenderThread.renderState(), colorSpaceFilter, 255, SkBlendMode::kSrc);
+    Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc);
     bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) &&
                          MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
     layer.setForceFilter(!disableFilter);
@@ -177,38 +156,6 @@
 
 bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
                              SkBitmap* bitmap) {
-    /*
-     * In the past only TextureView readback was setting the temporary surface color space to null.
-     * Now all 3 readback flows are drawing into a SkSurface with null color space.
-     * At readback there are 3 options to convert the source image color space to the destination
-     * color space requested in "bitmap->info().colorSpace()":
-     * 1. Set color space for temporary surface render target to null (disables color management),
-     *    colorspace tag from source SkImage is ignored by Skia,
-     *    convert SkImage to SRGB at draw time with SkColorFilter/SkToSRGBColorFilter,
-     *    do a readback from temporary SkSurface to a temporary SRGB SkBitmap "bitmap2",
-     *    read back from SRGB "bitmap2" into non-SRGB "bitmap" which will do a CPU color conversion.
-     *
-     * 2. Set color space for temporary surface render target to SRGB (not nullptr),
-     *    colorspace tag on the source SkImage is used by Skia to enable conversion,
-     *    convert SkImage to SRGB at draw time with drawImage (no filters),
-     *    do a readback from temporary SkSurface, which will do a color conversion from SRGB to
-     *    bitmap->info().colorSpace() on the CPU.
-     *
-     * 3. Set color space for temporary surface render target to bitmap->info().colorSpace(),
-     *    colorspace tag on the source SkImage is used by Skia to enable conversion,
-     *    convert SkImage to bitmap->info().colorSpace() at draw time with drawImage (no filters),
-     *    do a readback from SkSurface, which will not do any color conversion, because
-     *    surface was created with the same color space as the "bitmap".
-     *
-     * Option 1 is used for all readback flows.
-     * Options 2 and 3 are new, because skia added support for non-SRGB render targets without
-     * linear blending.
-     * TODO: evaluate if options 2 or 3 for color space conversion are better.
-     */
-
-    // drop the colorSpace from the temporary surface.
-    SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr);
-
     /* This intermediate surface is present to work around a bug in SwiftShader that
      * prevents us from reading the contents of the layer's texture directly. The
      * workaround involves first rendering that texture into an intermediate buffer and
@@ -217,70 +164,44 @@
      * with reading incorrect data from EGLImage backed SkImage (likely a driver bug).
      */
     sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
-                                                              SkBudgeted::kYes, surfaceInfo);
+                                                              SkBudgeted::kYes, bitmap->info());
 
+    // if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we
+    // attempt to do the intermediate rendering step in 8888
     if (!tmpSurface.get()) {
-        surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType);
+        SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
         tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
-                                                 surfaceInfo);
+                                                 tmpInfo);
         if (!tmpSurface.get()) {
-            ALOGW("Unable to readback GPU contents into the provided bitmap");
+            ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
             return false;
         }
     }
 
-    if (skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
-                                               tmpSurface->getCanvas(), layer, srcRect, dstRect,
-                                               false)) {
-        // If bitmap->info().colorSpace() is non-SRGB, convert the data from SRGB to non-SRGB on
-        // CPU. We can't just pass bitmap->info() to SkSurface::readPixels, because "tmpSurface" has
-        // disabled color conversion.
-        SkColorSpace* destColorSpace = bitmap->info().colorSpace();
-        SkBitmap tempSRGBBitmap;
-        SkBitmap tmpN32Bitmap;
-        SkBitmap* bitmapInSRGB;
-        if (destColorSpace && !destColorSpace->isSRGB()) {
-            tempSRGBBitmap.allocPixels(bitmap->info().makeColorSpace(SkColorSpace::MakeSRGB()));
-            bitmapInSRGB = &tempSRGBBitmap;  // Need to convert latter from SRGB to non-SRGB.
-        } else {
-            bitmapInSRGB = bitmap;  // No need for color conversion - write directly into output.
-        }
-        bool success = false;
+    if (!skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
+                                                tmpSurface->getCanvas(), layer, srcRect, dstRect,
+                                                false)) {
+        ALOGW("Unable to draw content from GPU into the provided bitmap");
+        return false;
+    }
 
-        // TODO: does any of the readbacks below clamp F16 exSRGB?
-        // Readback into a SRGB SkBitmap.
-        if (tmpSurface->readPixels(bitmapInSRGB->info(), bitmapInSRGB->getPixels(),
-                                   bitmapInSRGB->rowBytes(), 0, 0)) {
-            success = true;
-        } else {
-            // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
-            // 8888 and then convert that into the destination format before giving up.
-            SkImageInfo bitmapInfo =
-                    SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType(),
-                                         SkColorSpace::MakeSRGB());
-            if (tmpN32Bitmap.tryAllocPixels(bitmapInfo) &&
-                tmpSurface->readPixels(bitmapInfo, tmpN32Bitmap.getPixels(),
-                                       tmpN32Bitmap.rowBytes(), 0, 0)) {
-                success = true;
-                bitmapInSRGB = &tmpN32Bitmap;
-            }
-        }
-
-        if (success) {
-            if (bitmapInSRGB != bitmap) {
-                // Convert from SRGB to non-SRGB color space if needed. Convert from N32 to
-                // destination bitmap color format if needed.
-                if (!bitmapInSRGB->readPixels(bitmap->info(), bitmap->getPixels(),
-                                              bitmap->rowBytes(), 0, 0)) {
-                    return false;
-                }
-            }
-            bitmap->notifyPixelsChanged();
-            return true;
+    if (!tmpSurface->readPixels(*bitmap, 0, 0)) {
+        // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
+        // 8888 and then convert that into the destination format before giving up.
+        SkBitmap tmpBitmap;
+        SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
+        if (bitmap->info().colorType() == SkColorType::kN32_SkColorType ||
+                !tmpBitmap.tryAllocPixels(tmpInfo) ||
+                !tmpSurface->readPixels(tmpBitmap, 0, 0) ||
+                !tmpBitmap.readPixels(bitmap->info(), bitmap->getPixels(),
+                                      bitmap->rowBytes(), 0, 0)) {
+            ALOGW("Unable to convert content into the provided bitmap");
+            return false;
         }
     }
 
-    return false;
+    bitmap->notifyPixelsChanged();
+    return true;
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index d9e10ce..e86a813 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -54,8 +54,8 @@
     CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
 
 private:
-    CopyResult copyImageInto(const sk_sp<SkImage>& image, sk_sp<SkColorFilter>& colorSpaceFilter,
-                             Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap);
+    CopyResult copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
+                             const Rect& srcRect, SkBitmap* bitmap);
 
     bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
                        SkBitmap* bitmap);
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 9a15ff2..ba34384 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -27,7 +27,6 @@
 #include <SkAnimatedImage.h>
 #include <SkCanvasStateUtils.h>
 #include <SkColorFilter.h>
-#include <SkColorSpaceXformCanvas.h>
 #include <SkDeque.h>
 #include <SkDrawable.h>
 #include <SkFont.h>
@@ -61,19 +60,8 @@
 SkiaCanvas::SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {}
 
 SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
-    sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
-    mCanvasOwned =
-            std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
-    if (cs.get() == nullptr || cs->isSRGB()) {
-        mCanvas = mCanvasOwned.get();
-    } else {
-        /** The wrapper is needed if we are drawing into a non-sRGB destination, since
-         *  we need to transform all colors (not just bitmaps via filters) into the
-         *  destination's colorspace.
-         */
-        mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), std::move(cs));
-        mCanvas = mCanvasWrapper.get();
-    }
+    mCanvasOwned = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap));
+    mCanvas = mCanvasOwned.get();
 }
 
 SkiaCanvas::~SkiaCanvas() {}
@@ -82,7 +70,6 @@
     if (mCanvas != skiaCanvas) {
         mCanvas = skiaCanvas;
         mCanvasOwned.reset();
-        mCanvasWrapper.reset();
     }
     mSaveStack.reset(nullptr);
 }
@@ -92,18 +79,9 @@
 // ----------------------------------------------------------------------------
 
 void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
-    sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
-    std::unique_ptr<SkCanvas> newCanvas =
-            std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
-    std::unique_ptr<SkCanvas> newCanvasWrapper;
-    if (cs.get() != nullptr && !cs->isSRGB()) {
-        newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), std::move(cs));
-    }
-
     // deletes the previously owned canvas (if any)
-    mCanvasOwned = std::move(newCanvas);
-    mCanvasWrapper = std::move(newCanvasWrapper);
-    mCanvas = mCanvasWrapper ? mCanvasWrapper.get() : mCanvasOwned.get();
+    mCanvasOwned.reset(new SkCanvas(bitmap));
+    mCanvas = mCanvasOwned.get();
 
     // clean up the old save stack
     mSaveStack.reset(nullptr);
@@ -548,40 +526,14 @@
 // Canvas draw operations: Bitmaps
 // ----------------------------------------------------------------------------
 
-SkiaCanvas::PaintCoW&& SkiaCanvas::filterBitmap(PaintCoW&& paint,
-                                                sk_sp<SkColorFilter> colorSpaceFilter) const {
-    /* We don't apply the colorSpace filter if this canvas is already wrapped with
-     * a SkColorSpaceXformCanvas since it already takes care of converting the
-     * contents of the bitmap into the appropriate colorspace.  The mCanvasWrapper
-     * should only be used if this canvas is backed by a surface/bitmap that is known
-     * to have a non-sRGB colorspace.
-     */
-    if (!mCanvasWrapper && colorSpaceFilter) {
-        SkPaint& tmpPaint = paint.writeable();
-        if (tmpPaint.getColorFilter()) {
-            tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(tmpPaint.refColorFilter(),
-                                                                     std::move(colorSpaceFilter)));
-            LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
-        } else {
-            tmpPaint.setColorFilter(std::move(colorSpaceFilter));
-        }
-    }
-    return filterPaint(std::move(paint));
-}
-
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
+    mCanvas->drawImage(bitmap.makeImage(), left, top, filterPaint(paint));
 }
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
     SkAutoCanvasRestore acr(mCanvas, true);
     mCanvas->concat(matrix);
-
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
+    mCanvas->drawImage(bitmap.makeImage(), 0, 0, filterPaint(paint));
 }
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
@@ -590,9 +542,7 @@
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
+    mCanvas->drawImageRect(bitmap.makeImage(), srcRect, dstRect, filterPaint(paint),
                            SkCanvas::kFast_SrcRectConstraint);
 }
 
@@ -674,13 +624,9 @@
     PaintCoW paintCoW(paint);
     SkPaint& tmpPaint = paintCoW.writeable();
 
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+    sk_sp<SkImage> image = bitmap.makeImage();
     sk_sp<SkShader> shader =
             image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
-    if (colorFilter) {
-        shader = shader->makeWithColorFilter(std::move(colorFilter));
-    }
     tmpPaint.setShader(std::move(shader));
 
     mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate,
@@ -711,10 +657,7 @@
     lattice.fBounds = nullptr;
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImageLattice(image.get(), lattice, dst,
-                              filterBitmap(paint, std::move(colorFilter)));
+    mCanvas->drawImageLattice(bitmap.makeImage().get(), lattice, dst, filterPaint(paint));
 }
 
 double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) {
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 3a877cf..24d9c08 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -232,7 +232,6 @@
 
     class Clip;
 
-    std::unique_ptr<SkCanvas> mCanvasWrapper;  // might own a wrapper on the canvas
     std::unique_ptr<SkCanvas> mCanvasOwned;    // might own a canvas we allocated
     SkCanvas* mCanvas;                         // we do NOT own this canvas, it must survive us
                                                // unless it is the same as mCanvasOwned.get()
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 75a6e72..6c77f9e 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -32,7 +32,6 @@
 
 #include <SkCanvas.h>
 #include <SkImagePriv.h>
-#include <SkToSRGBColorFilter.h>
 
 #include <SkHighContrastFilter.h>
 #include <limits>
@@ -287,14 +286,8 @@
 
 void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
     if (isHardware()) {
-        outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
-                                                 info().colorType(), info().alphaType(), nullptr));
+        outBitmap->allocPixels(mInfo);
         uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap);
-        if (mInfo.colorSpace()) {
-            sk_sp<SkPixelRef> pixelRef = sk_ref_sp(outBitmap->pixelRef());
-            outBitmap->setInfo(mInfo);
-            outBitmap->setPixelRef(std::move(pixelRef), 0, 0);
-        }
         return;
     }
     outBitmap->setInfo(mInfo, rowBytes());
@@ -313,7 +306,7 @@
     return nullptr;
 }
 
-sk_sp<SkImage> Bitmap::makeImage(sk_sp<SkColorFilter>* outputColorFilter) {
+sk_sp<SkImage> Bitmap::makeImage() {
     sk_sp<SkImage> image = mImage;
     if (!image) {
         SkASSERT(!isHardware());
@@ -325,9 +318,6 @@
         // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here.
         image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
     }
-    if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) {
-        *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace());
-    }
     return image;
 }
 
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 238c764..d446377 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -105,14 +105,8 @@
      * Creates or returns a cached SkImage and is safe to be invoked from either
      * the UI or RenderThread.
      *
-     * @param outputColorFilter is a required param that will be populated by
-     *     this function if the bitmap's colorspace is not sRGB. If populated the
-     *     filter will convert colors from the bitmaps colorspace into sRGB. It
-     *     is the callers responsibility to use this colorFilter when drawing
-     *     this image into any destination that is presumed to be sRGB (i.e. a
-     *     buffer that has no colorspace defined).
      */
-    sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter);
+    sk_sp<SkImage> makeImage();
 
     static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes);
 
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 13d2dae..9b408fb 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <utils/MathUtils.h>
 #include "LayerDrawable.h"
 
 #include "GrBackendSurface.h"
@@ -32,6 +33,24 @@
     }
 }
 
+// This is a less-strict matrix.isTranslate() that will still report being translate-only
+// on imperceptibly small scaleX & scaleY values.
+static bool isBasicallyTranslate(const SkMatrix& matrix) {
+    if (!matrix.isScaleTranslate()) return false;
+    return MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY());
+}
+
+static bool shouldFilter(const SkMatrix& matrix) {
+    if (!matrix.isScaleTranslate()) return true;
+
+    // We only care about meaningful scale here
+    bool noScale = MathUtils::isOne(matrix.getScaleX())
+            && MathUtils::isOne(matrix.getScaleY());
+    bool pixelAligned = SkScalarIsInt(matrix.getTranslateX())
+            && SkScalarIsInt(matrix.getTranslateY());
+    return !(noScale && pixelAligned);
+}
+
 bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
                               const SkRect* srcRect, const SkRect* dstRect,
                               bool useLayerTransform) {
@@ -70,7 +89,7 @@
         SkPaint paint;
         paint.setAlpha(layer->getAlpha());
         paint.setBlendMode(layer->getMode());
-        paint.setColorFilter(layer->getColorSpaceWithFilter());
+        paint.setColorFilter(layer->getColorFilter());
         const bool nonIdentityMatrix = !matrix.isIdentity();
         if (nonIdentityMatrix) {
             canvas->save();
@@ -101,7 +120,7 @@
             // Integer translation is defined as when src rect and dst rect align fractionally.
             // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
             // only for SrcOver blending and without color filter (readback uses Src blending).
-            bool isIntegerTranslate = totalMatrix.isTranslate()
+            bool isIntegerTranslate = isBasicallyTranslate(totalMatrix)
                     && SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
                     == SkScalarFraction(skiaSrcRect.fLeft)
                     && SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
@@ -112,10 +131,7 @@
             canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint,
                                   SkCanvas::kFast_SrcRectConstraint);
         } else {
-            bool isIntegerTranslate = totalMatrix.isTranslate()
-                    && SkScalarIsInt(totalMatrix[SkMatrix::kMTransX])
-                    && SkScalarIsInt(totalMatrix[SkMatrix::kMTransY]);
-            if (layer->getForceFilter() || !isIntegerTranslate) {
+            if (layer->getForceFilter() || shouldFilter(totalMatrix)) {
                 paint.setFilterQuality(kLow_SkFilterQuality);
             }
             canvas->drawImage(layerImage.get(), 0, 0, &paint);
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 6ae5999..142bca9 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -97,7 +97,7 @@
     SkASSERT(mRenderThread.getGrContext() != nullptr);
     sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
             mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, colorType,
-            nullptr, &props));
+            mSurfaceColorSpace, &props));
 
     SkiaPipeline::updateLighting(lightGeometry, lightInfo);
     renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
@@ -176,6 +176,7 @@
     } else if (colorMode == ColorMode::WideColorGamut) {
         mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
     }
+    mSurfaceColorSpace = SkColorSpace::MakeSRGB();
 
     if (mEglSurface != EGL_NO_SURFACE) {
         const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 2dfe7c7..7a255c1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -169,7 +169,7 @@
     if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
         SkImageInfo info;
         info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),
-                                 kPremul_SkAlphaType);
+                                 kPremul_SkAlphaType, getSurfaceColorSpace());
         SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
         SkASSERT(mRenderThread.getGrContext() != nullptr);
         node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
@@ -204,8 +204,7 @@
     GrContext* context = thread.getGrContext();
     if (context) {
         ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
-        sk_sp<SkColorFilter> colorFilter;
-        auto image = bitmap->makeImage(&colorFilter);
+        auto image = bitmap->makeImage();
         if (image.get() && !bitmap->isHardware()) {
             SkImage_pinAsTexture(image.get(), context);
             SkImage_unpinAsTexture(image.get(), context);
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 45022e7..f5de1c8 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -179,9 +179,8 @@
 }
 
 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
+    sk_sp<SkImage> image = bitmap.makeImage();
+    mRecorder.drawImage(image, left, top, filterPaint(paint), bitmap.palette());
     // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
     // it is not safe to store a raw SkImage pointer, because the image object will be destroyed
     // when this function ends.
@@ -194,9 +193,8 @@
     SkAutoCanvasRestore acr(&mRecorder, true);
     concat(matrix);
 
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
+    sk_sp<SkImage> image = bitmap.makeImage();
+    mRecorder.drawImage(image, 0, 0, filterPaint(paint), bitmap.palette());
     if (!bitmap.isImmutable() && image.get() && !image->unique()) {
         mDisplayList->mMutableImages.push_back(image.get());
     }
@@ -208,9 +206,8 @@
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
+    sk_sp<SkImage> image = bitmap.makeImage();
+    mRecorder.drawImageRect(image, srcRect, dstRect, filterPaint(paint),
                             SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
         !dstRect.isEmpty()) {
@@ -247,10 +244,9 @@
     if (!filteredPaint || filteredPaint->getFilterQuality() != kLow_SkFilterQuality) {
         filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
     }
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+    sk_sp<SkImage> image = bitmap.makeImage();
     mRecorder.drawImageLattice(image, lattice, dst,
-                               filterBitmap(std::move(filteredPaint), std::move(colorFilter)),
+                               filterPaint(std::move(filteredPaint)),
                                bitmap.palette());
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
         mDisplayList->mMutableImages.push_back(image.get());
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
index 9ffccfb..15aec9f 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.cpp
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -22,6 +22,7 @@
 #include "renderthread/EglManager.h"
 #include "renderthread/RenderThread.h"
 #include "renderthread/VulkanManager.h"
+#include "utils/Color.h"
 
 // Macro for including the SurfaceTexture name in log messages
 #define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
@@ -44,13 +45,16 @@
     mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
 }
 
-void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer) {
-    if (!mImage.get()) {
+void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer,
+                                              android_dataspace dataspace) {
+    if (!mImage.get() || dataspace != mDataspace) {
         mImage = graphicBuffer.get()
                          ? SkImage::MakeFromAHardwareBuffer(
                                    reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()),
-                                   kPremul_SkAlphaType)
+                                   kPremul_SkAlphaType,
+                                   uirenderer::DataSpaceToColorSpace(dataspace))
                          : nullptr;
+        mDataspace = dataspace;
     }
 }
 
@@ -66,7 +70,7 @@
             int slot = st.mCurrentTexture;
             if (slot != BufferItem::INVALID_BUFFER_SLOT) {
                 *queueEmpty = true;
-                mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+                mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
                 return mImageSlots[slot].mImage;
             }
         }
@@ -145,7 +149,7 @@
     st.computeCurrentTransformMatrixLocked();
 
     *queueEmpty = false;
-    mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+    mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
     return mImageSlots[slot].mImage;
 }
 
diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h
index 31ee8db..5bab0ef5 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.h
+++ b/libs/hwui/surfacetexture/ImageConsumer.h
@@ -68,18 +68,21 @@
      * ImageConsumer maintains about a BufferQueue buffer slot.
      */
     struct ImageSlot {
-        ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+        ImageSlot() : mDataspace(HAL_DATASPACE_UNKNOWN), mEglFence(EGL_NO_SYNC_KHR) {}
 
         // mImage is the SkImage created from mGraphicBuffer.
         sk_sp<SkImage> mImage;
 
+        // the dataspace associated with the current image
+        android_dataspace mDataspace;
+
         /**
          * mEglFence is the EGL sync object that must signal before the buffer
          * associated with this buffer slot may be dequeued.
          */
         EGLSyncKHR mEglFence;
 
-        void createIfNeeded(sp<GraphicBuffer> graphicBuffer);
+        void createIfNeeded(sp<GraphicBuffer> graphicBuffer, android_dataspace dataspace);
     };
 
     /**
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp
index 4bff715..90f8912 100644
--- a/libs/hwui/surfacetexture/SurfaceTexture.cpp
+++ b/libs/hwui/surfacetexture/SurfaceTexture.cpp
@@ -470,8 +470,7 @@
     ConsumerBase::dumpLocked(result, prefix);
 }
 
-sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
-                                            bool* queueEmpty,
+sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty,
                                             uirenderer::RenderState& renderState) {
     Mutex::Autolock _l(mMutex);
 
@@ -488,7 +487,6 @@
     auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState);
     if (image.get()) {
         uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix);
-        dataSpace = mCurrentDataSpace;
     }
     return image;
 }
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h
index db392a9..96afd82 100644
--- a/libs/hwui/surfacetexture/SurfaceTexture.h
+++ b/libs/hwui/surfacetexture/SurfaceTexture.h
@@ -258,8 +258,8 @@
      */
     status_t attachToContext(uint32_t tex);
 
-    sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
-                                bool* queueEmpty, uirenderer::RenderState& renderState);
+    sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty,
+                                uirenderer::RenderState& renderState);
 
     /**
      * attachToView attaches a SurfaceTexture that is currently in the
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 66b9b85..8a1bc4d 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -72,9 +72,7 @@
     layerUpdater->setTransform(&transform);
 
     // updateLayer so it's ready to draw
-    SkMatrix identity;
-    identity.setIdentity();
-    layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr);
+    layerUpdater->updateLayer(true, SkMatrix::I(), nullptr);
     return layerUpdater;
 }
 
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index 15039b5..ad11a1d 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -44,8 +44,7 @@
                 });
 
         SkPaint paint;
-        sk_sp<SkColorFilter> colorFilter;
-        sk_sp<SkImage> image = hwuiBitmap->makeImage(&colorFilter);
+        sk_sp<SkImage> image = hwuiBitmap->makeImage();
         sk_sp<SkShader> repeatShader =
                 image->makeShader(SkShader::TileMode::kRepeat_TileMode,
                                   SkShader::TileMode::kRepeat_TileMode, nullptr);
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index f137562..448408d 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -72,8 +72,7 @@
     void doFrame(int frameNr) override {}
 
     sk_sp<SkShader> createBitmapShader(Bitmap& bitmap) {
-        sk_sp<SkColorFilter> colorFilter;
-        sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+        sk_sp<SkImage> image = bitmap.makeImage();
         return image->makeShader(SkShader::TileMode::kClamp_TileMode,
                                  SkShader::TileMode::kClamp_TileMode);
     }
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index c235715..210fced 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -54,8 +54,7 @@
     // create an image and pin it so that we have something with a unique key in the cache
     sk_sp<Bitmap> bitmap =
             Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(displayInfo.w, displayInfo.h));
-    sk_sp<SkColorFilter> filter;
-    sk_sp<SkImage> image = bitmap->makeImage(&filter);
+    sk_sp<SkImage> image = bitmap->makeImage();
     ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
 
     // attempt to trim all memory while we still hold strong refs
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index 6c8775b..a686979 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -43,7 +43,7 @@
     SkBitmap bitmap;
     bitmap.allocN32Pixels(16, 16);
     sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap);
-    layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage);
+    layerUpdater->updateLayer(true, scaledMatrix, layerImage);
 
     // the backing layer should now have all the properties applied.
     EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth());
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 634ceff..f3a7648 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -53,12 +53,12 @@
     adobeBitmap->getSkBitmap(&adobeSkBitmap);
     *adobeSkBitmap.getAddr32(0, 0) = 0xFF0000F0;  // Opaque, almost fully-red
 
-    SkImageInfo info = adobeInfo.makeColorSpace(nullptr);
+    SkImageInfo info = adobeInfo.makeColorSpace(SkColorSpace::MakeSRGB());
     sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
     SkBitmap skBitmap;
     bitmap->getSkBitmap(&skBitmap);
 
-    // Create a software canvas.
+    // Create a software sRGB canvas.
     SkiaCanvas canvas(skBitmap);
     canvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
     // The result should be fully red, since we convert to sRGB at draw time.
@@ -77,7 +77,7 @@
     picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
     sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
 
-    // Playback to an software canvas.  The result should be fully red.
+    // Playback to a software sRGB canvas.  The result should be fully red.
     canvas.asSkCanvas()->drawPicture(picture);
     ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
 }
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 5475898..cc8d83f 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -34,6 +34,10 @@
         return (value >= -NON_ZERO_EPSILON) && (value <= NON_ZERO_EPSILON);
     }
 
+    inline static bool isOne(float value) {
+        return areEqual(value, 1.0f);
+    }
+
     inline static bool isPositive(float value) { return value >= NON_ZERO_EPSILON; }
 
     /**
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index f60c338..8de2ab4 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -82,6 +82,11 @@
   STATS_LOG_VALUE_TYPE type;
 };
 
+struct WorkChain {
+  std::vector<int32_t> uids;
+  std::vector<std::string> tags;
+};
+
 // Represents a parcelable object. Only used to send data from Android OS to statsd.
 class StatsLogEventWrapper : public android::Parcelable {
  public:
@@ -99,7 +104,9 @@
 
   int64_t getWallClockTimeNs() const { return mWallClockTimeNs; }
 
-  std::vector<StatsLogValue> getElements() const { return mElements; }
+  const std::vector<StatsLogValue>& getElements() const { return mElements; }
+
+  const std::vector<WorkChain>& getWorkChains() const { return mWorkChains; }
 
  private:
   int mTagId;
@@ -109,6 +116,8 @@
   int64_t mWallClockTimeNs;
 
   std::vector<StatsLogValue> mElements;
+
+  std::vector<WorkChain> mWorkChains;
 };
 } // Namespace os
 } // Namespace android
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 04c4629..f6dfdef 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -58,6 +58,31 @@
     ALOGE("statsd could not read wall clock time from parcel");
     return res;
   }
+  int numWorkChain = 0;
+  if ((res = in->readInt32(&numWorkChain)) != OK) {
+    ALOGE("statsd could not read number of work chains from parcel");
+    return res;
+  }
+  if (numWorkChain > 0) {
+    for (int i = 0; i < numWorkChain; i++) {
+      int numNodes = 0;
+      if ((res = in->readInt32(&numNodes)) != OK) {
+        ALOGE(
+            "statsd could not read number of nodes in work chain from parcel");
+        return res;
+      }
+      if (numNodes == 0) {
+        ALOGE("empty work chain");
+        return BAD_VALUE;
+      }
+      WorkChain wc;
+      for (int j = 0; j < numNodes; j++) {
+        wc.uids.push_back(in->readInt32());
+        wc.tags.push_back(std::string(String8(in->readString16()).string()));
+      }
+      mWorkChains.push_back(wc);
+    }
+  }
   int dataSize = 0;
   if ((res = in->readInt32(&dataSize)) != OK) {
     ALOGE("statsd could not read data size from parcel");
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 7627cf6..180183e 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -26,7 +26,9 @@
 oneway interface ILocationListener
 {
     void onLocationChanged(in Location location);
-    void onStatusChanged(String provider, int status, in Bundle extras);
     void onProviderEnabled(String provider);
     void onProviderDisabled(String provider);
+
+    // --- deprecated ---
+    void onStatusChanged(String provider, int status, in Bundle extras);
 }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index b5d835a..ff2fad4 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -99,9 +99,10 @@
     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,
             String opPackageName);
-    void clearTestProviderStatus(String provider, String opPackageName);
 
     boolean sendExtraCommand(String provider, String command, inout Bundle extras);
 
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 88904c8..aa9dddc 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -44,29 +44,12 @@
     void onLocationChanged(Location location);
 
     /**
-     * Called when the provider status changes. This method is called when
-     * a provider is unable to fetch a location or if the provider has recently
-     * become available after a period of unavailability.
+     * This callback will never be invoked and providers can be considers as always in the
+     * {@link LocationProvider#AVAILABLE} state.
      *
-     * @param provider the name of the location provider associated with this
-     * update.
-     * @param status {@link LocationProvider#OUT_OF_SERVICE} if the
-     * provider is out of service, and this is not expected to change in the
-     * near future; {@link LocationProvider#TEMPORARILY_UNAVAILABLE} if
-     * the provider is temporarily unavailable but is expected to be available
-     * shortly; and {@link LocationProvider#AVAILABLE} if the
-     * provider is currently available.
-     * @param extras an optional Bundle which will contain provider specific
-     * status variables.
-     *
-     * <p> A number of common key/value pairs for the extras Bundle are listed
-     * below. Providers that use any of the keys on this list must
-     * provide the corresponding value as described below.
-     *
-     * <ul>
-     * <li> satellites - the number of satellites used to derive the fix
-     * </ul>
+     * @deprecated This callback will never be invoked.
      */
+    @Deprecated
     void onStatusChanged(String provider, int status, Bundle extras);
 
     /**
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 02680ab..b66ceef 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -146,9 +146,14 @@
     public static final String KEY_PROXIMITY_ENTERING = "entering";
 
     /**
+     * This key is no longer in use.
+     *
      * Key used for a Bundle extra holding an Integer status value
      * when a status change is broadcast using a PendingIntent.
+     *
+     * @deprecated Status changes are deprecated and no longer broadcast.
      */
+    @Deprecated
     public static final String KEY_STATUS_CHANGED = "status";
 
     /**
@@ -1581,8 +1586,7 @@
     }
 
     /**
-     * Sets mock status values for the given provider.  These values will be used in place
-     * of any actual values from the provider.
+     * This method has no effect as provider status has been deprecated and is no longer supported.
      *
      * @param provider the provider name
      * @param status the mock status
@@ -1593,7 +1597,10 @@
      * 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 method has no effect.
      */
+    @Deprecated
     public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
         try {
             mService.setTestProviderStatus(provider, status, extras, updateTime,
@@ -1604,21 +1611,19 @@
     }
 
     /**
-     * Removes any mock status values associated with the given provider.
+     * This method has no effect as provider status has been deprecated and is no longer supported.
      *
      * @param provider the provider name
-     *
      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
      * 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 method has no effect.
      */
+    @Deprecated
     public void clearTestProviderStatus(String provider) {
-        try {
-            mService.clearTestProviderStatus(provider, mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        setTestProviderStatus(provider, LocationProvider.AVAILABLE, null, 0L);
     }
 
     // --- GPS-specific support ---
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index c4fd097..b69a9d7 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -34,8 +34,23 @@
  * user-specified criteria.
  */
 public class LocationProvider {
+
+    /**
+     * @deprecated Location provider statuses are no longer supported.
+     */
+    @Deprecated
     public static final int OUT_OF_SERVICE = 0;
+
+    /**
+     * @deprecated Location provider statuses are no longer supported.
+     */
+    @Deprecated
     public static final int TEMPORARILY_UNAVAILABLE = 1;
+
+    /**
+     * @deprecated Location provider statuses are no longer supported.
+     */
+    @Deprecated
     public static final int AVAILABLE = 2;
 
     /**
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 1e69f16..d19559e 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -11,8 +11,8 @@
     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 abstract int onGetStatus(android.os.Bundle);
-    method public abstract long onGetStatusUpdateTime();
+    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);
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 30655f5..d45a4ba 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,14 +16,11 @@
 
 package com.android.location.provider;
 
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-
 import android.content.Context;
 import android.location.ILocationManager;
 import android.location.Location;
 import android.location.LocationManager;
+import android.location.LocationProvider;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -36,6 +33,10 @@
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.util.FastPrintWriter;
 
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+
 /**
  * Base class for location providers implemented as unbundled services.
  *
@@ -173,6 +174,8 @@
     }
 
     /**
+     * This method will no longer be invoked.
+     *
      * Returns a information on the status of this provider.
      * <p>{@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is
      * out of service, and this is not expected to change in the near
@@ -183,10 +186,17 @@
      *
      * <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.
      */
-    public abstract int onGetStatus(Bundle extras);
+    @Deprecated
+    public int onGetStatus(Bundle extras) {
+        return LocationProvider.AVAILABLE;
+    }
 
     /**
+     * This method will no longer be invoked.
+     *
      * Returns the time at which the status was last updated. It is the
      * responsibility of the provider to appropriately set this value using
      * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}.
@@ -195,8 +205,13 @@
      * the same status again.
      *
      * @return time of last status update in millis since last reboot
+     *
+     * @deprecated This method will no longer be invoked.
      */
-    public abstract long onGetStatusUpdateTime();
+    @Deprecated
+    public long onGetStatusUpdateTime() {
+        return 0;
+    }
 
     /**
      * Implements addditional location provider specific additional commands.
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 976d380..ff1bdd4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1911,7 +1911,7 @@
      *   system failed to generate a new session, a condition in which audio playback or recording
      *   will subsequently fail as well.
      */
-    public static int generateAudioSessionId() {
+    public int generateAudioSessionId() {
         int session = AudioSystem.newAudioSessionId();
         if (session > 0) {
             return session;
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 67cc456..082a375 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -75,6 +75,12 @@
      */
     public static final int NUM_STREAMS = 5;
 
+    /** Maximum value for AudioTrack channel count
+     * @hide public for MediaCode only, do not un-hide or change to a numeric literal
+     */
+    public static final int OUT_CHANNEL_COUNT_MAX = native_get_FCC_8();
+    private static native int native_get_FCC_8();
+
     // Expose only the getter method publicly so we can change it in the future
     private static final int NUM_STREAM_TYPES = 11;
     @UnsupportedAppUsage
@@ -918,6 +924,15 @@
 
     public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled);
 
+    /**
+     * Communicate UID of active assistant to audio policy service.
+     */
+    public static native int setAssistantUid(int uid);
+    /**
+     * Communicate UIDs of active accessibility services to audio policy service.
+     */
+    public static native int setA11yServicesUids(int[] uids);
+
     // Items shared with audio service
 
     /**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index d37f8ab..c226d49 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -93,11 +93,6 @@
      */
     private static final float GAIN_MAX = 1.0f;
 
-    /** Maximum value for AudioTrack channel count
-     * @hide public for MediaCode only, do not un-hide or change to a numeric literal
-     */
-    public static final int CHANNEL_COUNT_MAX = native_get_FCC_8();
-
     /** indicates AudioTrack state is stopped */
     public static final int PLAYSTATE_STOPPED = 1;  // matches SL_PLAYSTATE_STOPPED
     /** indicates AudioTrack state is paused */
@@ -1001,7 +996,8 @@
     }
 
     // mask of all the positional channels supported, however the allowed combinations
-    // are further restricted by the matching left/right rule and CHANNEL_COUNT_MAX
+    // are further restricted by the matching left/right rule and
+    // AudioSystem.OUT_CHANNEL_COUNT_MAX
     private static final int SUPPORTED_OUT_CHANNELS =
             AudioFormat.CHANNEL_OUT_FRONT_LEFT |
             AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
@@ -1124,7 +1120,7 @@
         mChannelIndexMask = channelIndexMask;
         if (mChannelIndexMask != 0) {
             // restrictive: indexMask could allow up to AUDIO_CHANNEL_BITS_LOG2
-            final int indexMask = (1 << CHANNEL_COUNT_MAX) - 1;
+            final int indexMask = (1 << AudioSystem.OUT_CHANNEL_COUNT_MAX) - 1;
             if ((channelIndexMask & ~indexMask) != 0) {
                 throw new IllegalArgumentException("Unsupported channel index configuration "
                         + channelIndexMask);
@@ -1169,9 +1165,9 @@
             return false;
         }
         final int channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
-        if (channelCount > CHANNEL_COUNT_MAX) {
+        if (channelCount > AudioSystem.OUT_CHANNEL_COUNT_MAX) {
             loge("Channel configuration contains too many channels " +
-                    channelCount + ">" + CHANNEL_COUNT_MAX);
+                    channelCount + ">" + AudioSystem.OUT_CHANNEL_COUNT_MAX);
             return false;
         }
         // check for unsupported multichannel combinations:
@@ -3418,7 +3414,6 @@
     private native final int native_getRoutedDeviceId();
     private native final void native_enableDeviceCallback();
     private native final void native_disableDeviceCallback();
-    static private native int native_get_FCC_8();
 
     private native int native_applyVolumeShaper(
             @NonNull VolumeShaper.Configuration configuration,
diff --git a/media/java/android/media/CallbackDataSourceDesc.java b/media/java/android/media/CallbackDataSourceDesc.java
index a7e168f..0e8e6ce 100644
--- a/media/java/android/media/CallbackDataSourceDesc.java
+++ b/media/java/android/media/CallbackDataSourceDesc.java
@@ -18,31 +18,31 @@
 
 import android.annotation.NonNull;
 
-import com.android.internal.util.Preconditions;
-
 /**
  * @hide
- * Structure for file data source descriptor.
+ * Structure of data source descriptor for sources using callback.
  *
- * Used by {@link MediaPlayer2#setDataSource(CallbackDataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
  * to set data source for playback.
  *
  * <p>Users should use {@link Builder} to create {@link CallbackDataSourceDesc}.
  *
  */
 public class CallbackDataSourceDesc extends DataSourceDesc {
-    private Media2DataSource mMedia2DataSource;
+    private DataSourceCallback mDataSourceCallback;
 
     private CallbackDataSourceDesc() {
     }
 
     /**
-     * Return the Media2DataSource of this data source.
+     * Return the DataSourceCallback of this data source.
      * It's meaningful only when {@code getType} returns {@link #TYPE_CALLBACK}.
-     * @return the Media2DataSource of this data source
+     * @return the DataSourceCallback of this data source
      */
-    public Media2DataSource getMedia2DataSource() {
-        return mMedia2DataSource;
+    public DataSourceCallback getDataSourceCallback() {
+        return mDataSourceCallback;
     }
 
     /**
@@ -60,7 +60,7 @@
      * </pre>
      */
     public static class Builder extends BuilderBase<Builder> {
-        private Media2DataSource mMedia2DataSource;
+        private DataSourceCallback mDataSourceCallback;
 
         /**
          * Constructs a new Builder with the defaults.
@@ -79,7 +79,7 @@
             if (dsd == null) {
                 return;  // use default
             }
-            mMedia2DataSource = dsd.mMedia2DataSource;
+            mDataSourceCallback = dsd.mDataSourceCallback;
         }
 
         /**
@@ -92,21 +92,21 @@
         public @NonNull CallbackDataSourceDesc build() {
             CallbackDataSourceDesc dsd = new CallbackDataSourceDesc();
             super.build(dsd);
-            dsd.mMedia2DataSource = mMedia2DataSource;
+            dsd.mDataSourceCallback = mDataSourceCallback;
 
             return dsd;
         }
 
         /**
-         * Sets the data source (Media2DataSource) to use.
+         * Sets the data source (DataSourceCallback) to use.
          *
-         * @param m2ds the Media2DataSource for the media to play
+         * @param dscb the DataSourceCallback for the media to play
          * @return the same Builder instance.
-         * @throws NullPointerException if m2ds is null.
+         * @throws NullPointerException if dscb is null.
          */
-        public @NonNull Builder setDataSource(@NonNull Media2DataSource m2ds) {
-            Preconditions.checkNotNull(m2ds);
-            mMedia2DataSource = m2ds;
+        public @NonNull Builder setDataSource(@NonNull DataSourceCallback dscb) {
+            Media2Utils.checkArgument(dscb != null, "data source cannot be null.");
+            mDataSourceCallback = dscb;
             return this;
         }
     }
diff --git a/media/java/android/media/Media2DataSource.java b/media/java/android/media/DataSourceCallback.java
similarity index 89%
rename from media/java/android/media/Media2DataSource.java
rename to media/java/android/media/DataSourceCallback.java
index 08df632..9b27baf 100644
--- a/media/java/android/media/Media2DataSource.java
+++ b/media/java/android/media/DataSourceCallback.java
@@ -27,12 +27,12 @@
  *
  * <p class="note">Methods of this interface may be called on multiple different
  * threads. There will be a thread synchronization point between each call to ensure that
- * modifications to the state of your Media2DataSource are visible to future calls. This means
+ * modifications to the state of your DataSourceCallback are visible to future calls. This means
  * you don't need to do your own synchronization unless you're modifying the
- * Media2DataSource from another thread while it's being used by the framework.</p>
+ * DataSourceCallback from another thread while it's being used by the framework.</p>
  *
  */
-public abstract class Media2DataSource implements Closeable {
+public abstract class DataSourceCallback implements Closeable {
     /**
      * Called to request data from the given position.
      *
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index aed3f84..702034e9 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -18,13 +18,13 @@
 
 import android.annotation.NonNull;
 
-import com.android.internal.util.Preconditions;
-
 /**
  * @hide
  * Base class of data source descriptor.
  *
- * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
  * to set data source for playback.
  *
  * <p>Users should use subclasses' builder to change {@link DataSourceDesc}.
@@ -32,7 +32,7 @@
  */
 public class DataSourceDesc {
     // intentionally less than long.MAX_VALUE
-    public static final long LONG_MAX = 0x7ffffffffffffffL;
+    static final long LONG_MAX = 0x7ffffffffffffffL;
 
     // keep consistent with native code
     public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000;
@@ -48,6 +48,19 @@
     }
 
     /**
+     * Releases the resources held by this {@code DataSourceDesc} object.
+     */
+    void close() {
+    }
+
+    // Have to declare protected for finalize() since it is protected
+    // in the base class Object.
+    @Override
+    protected void finalize() throws Throwable {
+        close();
+    }
+
+    /**
      * Return the media Id of data source.
      * @return the media Id of data source
      */
@@ -118,7 +131,7 @@
          * @return the same instance of subclass of {@link DataSourceDesc}
          */
         void build(@NonNull DataSourceDesc dsd) {
-            Preconditions.checkNotNull(dsd);
+            Media2Utils.checkArgument(dsd != null,  "dsd cannot be null.");
 
             if (mStartPositionMs > mEndPositionMs) {
                 throw new IllegalStateException("Illegal start/end position: "
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index b96a585..01a0cb6 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -16,7 +16,10 @@
 
 package android.media;
 
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
@@ -26,12 +29,14 @@
 import android.system.OsConstants;
 import android.util.Log;
 import android.util.Pair;
-import android.annotation.IntDef;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
 import java.io.DataInput;
+import java.io.DataInputStream;
 import java.io.EOFException;
 import java.io.File;
 import java.io.FileDescriptor;
@@ -42,14 +47,14 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
-import java.util.LinkedList;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -58,11 +63,6 @@
 import java.util.TimeZone;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
 
 /**
  * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
@@ -583,11 +583,19 @@
     private static class ExifAttribute {
         public final int format;
         public final int numberOfComponents;
+        public final long bytesOffset;
         public final byte[] bytes;
 
+        public static final long BYTES_OFFSET_UNKNOWN = -1;
+
         private ExifAttribute(int format, int numberOfComponents, byte[] bytes) {
+            this(format, numberOfComponents, BYTES_OFFSET_UNKNOWN, bytes);
+        }
+
+        private ExifAttribute(int format, int numberOfComponents, long bytesOffset, byte[] bytes) {
             this.format = format;
             this.numberOfComponents = numberOfComponents;
+            this.bytesOffset = bytesOffset;
             this.bytes = bytes;
         }
 
@@ -1318,6 +1326,7 @@
     private int mOrfThumbnailLength;
     private int mRw2JpgFromRawOffset;
     private boolean mIsSupportedFile;
+    private boolean mModified;
 
     // Pattern to check non zero timestamp
     private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
@@ -1328,7 +1337,14 @@
     /**
      * Reads Exif tags from the specified image file.
      */
-    public ExifInterface(String filename) throws IOException {
+    public ExifInterface(@NonNull File file) throws IOException {
+        this(file.getAbsolutePath());
+    }
+
+    /**
+     * Reads Exif tags from the specified image file.
+     */
+    public ExifInterface(@NonNull String filename) throws IOException {
         if (filename == null) {
             throw new IllegalArgumentException("filename cannot be null");
         }
@@ -1354,7 +1370,7 @@
      * for writable and seekable file descriptors only. This constructor will not rewind the offset
      * of the given file descriptor. Developers should close the file descriptor after use.
      */
-    public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
+    public ExifInterface(@NonNull FileDescriptor fileDescriptor) throws IOException {
         if (fileDescriptor == null) {
             throw new IllegalArgumentException("fileDescriptor cannot be null");
         }
@@ -1388,7 +1404,7 @@
      * for input streams. The given input stream will proceed its current position. Developers
      * should close the input stream after use.
      */
-    public ExifInterface(InputStream inputStream) throws IOException {
+    public ExifInterface(@NonNull InputStream inputStream) throws IOException {
         if (inputStream == null) {
             throw new IllegalArgumentException("inputStream cannot be null");
         }
@@ -1414,7 +1430,7 @@
      *
      * @param tag the name of the tag.
      */
-    private ExifAttribute getExifAttribute(String tag) {
+    private @Nullable ExifAttribute getExifAttribute(@NonNull String tag) {
         // Retrieves all tag groups. The value from primary image tag group has a higher priority
         // than the value from the thumbnail tag group if there are more than one candidates.
         for (int i = 0; i < EXIF_TAGS.length; ++i) {
@@ -1432,7 +1448,7 @@
      *
      * @param tag the name of the tag.
      */
-    public String getAttribute(String tag) {
+    public @Nullable String getAttribute(@NonNull String tag) {
         ExifAttribute attribute = getExifAttribute(tag);
         if (attribute != null) {
             if (!sTagSetForCompatibility.contains(tag)) {
@@ -1470,7 +1486,7 @@
      * @param tag the name of the tag.
      * @param defaultValue the value to return if the tag is not available.
      */
-    public int getAttributeInt(String tag, int defaultValue) {
+    public int getAttributeInt(@NonNull String tag, int defaultValue) {
         ExifAttribute exifAttribute = getExifAttribute(tag);
         if (exifAttribute == null) {
             return defaultValue;
@@ -1491,7 +1507,7 @@
      * @param tag the name of the tag.
      * @param defaultValue the value to return if the tag is not available.
      */
-    public double getAttributeDouble(String tag, double defaultValue) {
+    public double getAttributeDouble(@NonNull String tag, double defaultValue) {
         ExifAttribute exifAttribute = getExifAttribute(tag);
         if (exifAttribute == null) {
             return defaultValue;
@@ -1510,7 +1526,7 @@
      * @param tag the name of the tag.
      * @param value the value of the tag.
      */
-    public void setAttribute(String tag, String value) {
+    public void setAttribute(@NonNull String tag, @Nullable String value) {
         // Convert the given value to rational values for backwards compatibility.
         if (value != null && sTagSetForCompatibility.contains(tag)) {
             if (tag.equals(TAG_GPS_TIMESTAMP)) {
@@ -1772,12 +1788,18 @@
     }
 
     /**
-     * Save the tag data into the original image file. This is expensive because it involves
-     * copying all the data from one file to another and deleting the old file and renaming the
-     * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
-     * and make a single call rather than multiple calls for each attribute.
+     * Save the tag data into the original image file. This is expensive because
+     * it involves copying all the data from one file to another and deleting
+     * the old file and renaming the other. It's best to use
+     * {@link #setAttribute(String,String)} to set all attributes to write and
+     * make a single call rather than multiple calls for each attribute.
      * <p>
      * This method is only supported for JPEG files.
+     * <p class="note">
+     * Note: after calling this method, any attempts to obtain range information
+     * from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()}
+     * will throw {@link IllegalStateException}, since the offsets may have
+     * changed in the newly written file.
      * </p>
      */
     public void saveAttributes() throws IOException {
@@ -1789,6 +1811,10 @@
                     "ExifInterface does not support saving attributes for the current input.");
         }
 
+        // Remember the fact that we've changed the file on disk from what was
+        // originally parsed, meaning we can't answer range questions
+        mModified = true;
+
         // Keep the thumbnail in memory
         mThumbnailBytes = getThumbnail();
 
@@ -1849,6 +1875,15 @@
     }
 
     /**
+     * Returns true if the image file has the given attribute defined.
+     *
+     * @param tag the name of the tag.
+     */
+    public boolean hasAttribute(String tag) {
+        return (getExifAttribute(tag) != null);
+    }
+
+    /**
      * Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no
      * JPEG compressed thumbnail.
      * The returned data can be decoded using
@@ -1968,17 +2003,45 @@
      *
      * @return two-element array, the offset in the first value, and length in
      *         the second, or {@code null} if no thumbnail was found.
+     * @throws IllegalStateException if {@link #saveAttributes()} has been
+     *             called since the underlying file was initially parsed, since
+     *             that means offsets may have changed.
      */
-    public long[] getThumbnailRange() {
-        if (!mHasThumbnail) {
-            return null;
+    public @Nullable long[] getThumbnailRange() {
+        if (mModified) {
+            throw new IllegalStateException(
+                    "The underlying file has been modified since being parsed");
         }
 
-        long[] range = new long[2];
-        range[0] = mThumbnailOffset;
-        range[1] = mThumbnailLength;
+        if (mHasThumbnail) {
+            return new long[] { mThumbnailOffset, mThumbnailLength };
+        } else {
+            return null;
+        }
+    }
 
-        return range;
+    /**
+     * Returns the offset and length of the requested tag inside the image file,
+     * or {@code null} if the tag is not contained.
+     *
+     * @return two-element array, the offset in the first value, and length in
+     *         the second, or {@code null} if no tag was found.
+     * @throws IllegalStateException if {@link #saveAttributes()} has been
+     *             called since the underlying file was initially parsed, since
+     *             that means offsets may have changed.
+     */
+    public @Nullable long[] getAttributeRange(@NonNull String tag) {
+        if (mModified) {
+            throw new IllegalStateException(
+                    "The underlying file has been modified since being parsed");
+        }
+
+        final ExifAttribute attribute = getExifAttribute(tag);
+        if (attribute != null) {
+            return new long[] { attribute.bytesOffset, attribute.bytes.length };
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -2023,13 +2086,41 @@
     }
 
     /**
-     * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
-     * Returns -1 if the date time information if not available.
+     * Returns parsed {@code DateTime} value, or -1 if unavailable or invalid.
+     * 
      * @hide
      */
     @UnsupportedAppUsage
-    public long getDateTime() {
-        String dateTimeString = getAttribute(TAG_DATETIME);
+    public @CurrentTimeMillisLong long getDateTime() {
+        return parseDateTime(getAttribute(TAG_DATETIME),
+                getAttribute(TAG_SUBSEC_TIME));
+    }
+
+    /**
+     * Returns parsed {@code DateTimeDigitized} value, or -1 if unavailable or
+     * invalid.
+     *
+     * @hide
+     */
+    public @CurrentTimeMillisLong long getDateTimeDigitized() {
+        return parseDateTime(getAttribute(TAG_DATETIME_DIGITIZED),
+                getAttribute(TAG_SUBSEC_TIME_DIGITIZED));
+    }
+
+    /**
+     * Returns parsed {@code DateTimeOriginal} value, or -1 if unavailable or
+     * invalid.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public @CurrentTimeMillisLong long getDateTimeOriginal() {
+        return parseDateTime(getAttribute(TAG_DATETIME_ORIGINAL),
+                getAttribute(TAG_SUBSEC_TIME_ORIGINAL));
+    }
+
+    private static @CurrentTimeMillisLong long parseDateTime(@Nullable String dateTimeString,
+            @Nullable String subSecs) {
         if (dateTimeString == null
                 || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
 
@@ -2041,7 +2132,6 @@
             if (datetime == null) return -1;
             long msecs = datetime.getTime();
 
-            String subSecs = getAttribute(TAG_SUBSEC_TIME);
             if (subSecs != null) {
                 try {
                     long sub = Long.parseLong(subSecs);
@@ -3125,9 +3215,11 @@
                 continue;
             }
 
-            byte[] bytes = new byte[(int) byteCount];
+            final int bytesOffset = dataInputStream.peek();
+            final byte[] bytes = new byte[(int) byteCount];
             dataInputStream.readFully(bytes);
-            ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes);
+            ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents,
+                    bytesOffset, bytes);
             mAttributes[ifdType].put(tag.name, attribute);
 
             // DNG files have a DNG Version tag specifying the version of specifications that the
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index 5d8ff49..763a81f 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -17,22 +17,26 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
 
-import com.android.internal.util.Preconditions;
-
-import java.io.FileDescriptor;
+import java.io.IOException;
 
 /**
  * @hide
- * Structure for data source descriptor.
+ * Structure of data source descriptor for sources using file descriptor.
  *
- * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
  * to set data source for playback.
  *
  * <p>Users should use {@link Builder} to create {@link FileDataSourceDesc}.
  *
  */
 public class FileDataSourceDesc extends DataSourceDesc {
+    private static final String TAG = "FileDataSourceDesc";
+
     /**
      * Used when the length of file descriptor is unknown.
      *
@@ -40,34 +44,61 @@
      */
     public static final long FD_LENGTH_UNKNOWN = LONG_MAX;
 
-    private FileDescriptor mFD;
+    private ParcelFileDescriptor mPFD;
     private long mOffset = 0;
     private long mLength = FD_LENGTH_UNKNOWN;
 
     private FileDataSourceDesc() {
+        super();
     }
 
     /**
-     * Return the FileDescriptor of this data source.
-     * @return the FileDescriptor of this data source
+     * Releases the resources held by this {@code FileDataSourceDesc} object.
      */
-    public FileDescriptor getFileDescriptor() {
-        return mFD;
+    @Override
+    void close() {
+        super.close();
+        closeFD();
     }
 
     /**
-     * Return the offset associated with the FileDescriptor of this data source.
+     * Releases the file descriptor held by this {@code FileDataSourceDesc} object.
+     */
+    void closeFD() {
+        synchronized (this) {
+            if (mPFD != null) {
+                try {
+                    mPFD.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "failed to close pfd: " + e);
+                }
+
+                mPFD = null;
+            }
+        }
+    }
+
+    /**
+     * Return the ParcelFileDescriptor of this data source.
+     * @return the ParcelFileDescriptor of this data source
+     */
+    public ParcelFileDescriptor getParcelFileDescriptor() {
+        return mPFD;
+    }
+
+    /**
+     * Return the offset associated with the ParcelFileDescriptor of this data source.
      * It's meaningful only when it has been set by the {@link Builder}.
-     * @return the offset associated with the FileDescriptor of this data source
+     * @return the offset associated with the ParcelFileDescriptor of this data source
      */
     public long getOffset() {
         return mOffset;
     }
 
     /**
-     * Return the content length associated with the FileDescriptor of this data source.
+     * Return the content length associated with the ParcelFileDescriptor of this data source.
      * {@link #FD_LENGTH_UNKNOWN} means same as the length of source content.
-     * @return the content length associated with the FileDescriptor of this data source
+     * @return the content length associated with the ParcelFileDescriptor of this data source
      */
     public long getLength() {
         return mLength;
@@ -80,7 +111,7 @@
      *
      * <pre class="prettyprint">
      * FileDataSourceDesc newDSD = new FileDataSourceDesc.Builder()
-     *         .setDataSource(fd, 0, srcLength)
+     *         .setDataSource(pfd, 0, srcLength)
      *         .setStartPosition(1000)
      *         .setEndPosition(15000)
      *         .build();
@@ -88,7 +119,7 @@
      * </pre>
      */
     public static class Builder extends BuilderBase<Builder> {
-        private FileDescriptor mFD;
+        private ParcelFileDescriptor mPFD;
         private long mOffset = 0;
         private long mLength = FD_LENGTH_UNKNOWN;
 
@@ -109,7 +140,7 @@
             if (dsd == null) {
                 return;  // use default
             }
-            mFD = dsd.mFD;
+            mPFD = dsd.mPFD;
             mOffset = dsd.mOffset;
             mLength = dsd.mLength;
         }
@@ -124,7 +155,7 @@
         public @NonNull FileDataSourceDesc build() {
             FileDataSourceDesc dsd = new FileDataSourceDesc();
             super.build(dsd);
-            dsd.mFD = mFD;
+            dsd.mPFD = mPFD;
             dsd.mOffset = mOffset;
             dsd.mLength = mLength;
 
@@ -132,38 +163,46 @@
         }
 
         /**
-         * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
-         * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
-         * to close the file descriptor after the source has been used.
+         * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
+         * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
+         * created by this builder is passed to {@link MediaPlayer2} via
+         * {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+         * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or
+         * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will
+         * close the ParcelFileDescriptor.
          *
-         * @param fd the FileDescriptor for the file to play
+         * @param pfd the ParcelFileDescriptor for the file to play
          * @return the same Builder instance.
-         * @throws NullPointerException if fd is null.
+         * @throws NullPointerException if pfd is null.
          */
-        public @NonNull Builder setDataSource(@NonNull FileDescriptor fd) {
-            Preconditions.checkNotNull(fd);
+        public @NonNull Builder setDataSource(@NonNull ParcelFileDescriptor pfd) {
+            Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
             resetDataSource();
-            mFD = fd;
+            mPFD = pfd;
             return this;
         }
 
         /**
-         * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
-         * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
-         * to close the file descriptor after the source has been used.
+         * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
+         * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
+         * created by this builder is passed to {@link MediaPlayer2} via
+         * {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+         * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or
+         * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will
+         * close the ParcelFileDescriptor.
          *
          * Any negative number for offset is treated as 0.
          * Any negative number for length is treated as maximum length of the data source.
          *
-         * @param fd the FileDescriptor for the file to play
+         * @param pfd the ParcelFileDescriptor for the file to play
          * @param offset the offset into the file where the data to be played starts, in bytes
          * @param length the length in bytes of the data to be played
          * @return the same Builder instance.
-         * @throws NullPointerException if fd is null.
+         * @throws NullPointerException if pfd is null.
          */
         public @NonNull Builder setDataSource(
-                @NonNull FileDescriptor fd, long offset, long length) {
-            Preconditions.checkNotNull(fd);
+                @NonNull ParcelFileDescriptor pfd, long offset, long length) {
+            Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
             if (offset < 0) {
                 offset = 0;
             }
@@ -171,14 +210,14 @@
                 length = FD_LENGTH_UNKNOWN;
             }
             resetDataSource();
-            mFD = fd;
+            mPFD = pfd;
             mOffset = offset;
             mLength = length;
             return this;
         }
 
         private void resetDataSource() {
-            mFD = null;
+            mPFD = null;
             mOffset = 0;
             mLength = FD_LENGTH_UNKNOWN;
         }
diff --git a/media/java/android/media/Media2HTTPConnection.java b/media/java/android/media/Media2HTTPConnection.java
index 0d7825a..a369a62 100644
--- a/media/java/android/media/Media2HTTPConnection.java
+++ b/media/java/android/media/Media2HTTPConnection.java
@@ -16,27 +16,27 @@
 
 package android.media;
 
-import android.net.NetworkUtils;
+import static android.media.MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
+
 import android.os.StrictMode;
 import android.util.Log;
 
 import java.io.BufferedInputStream;
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.Proxy;
-import java.net.URL;
 import java.net.HttpURLConnection;
+import java.net.InetAddress;
 import java.net.MalformedURLException;
 import java.net.NoRouteToHostException;
 import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.UnknownHostException;
 import java.net.UnknownServiceException;
 import java.util.HashMap;
 import java.util.Map;
 
-import static android.media.MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
-
 /** @hide */
 public class Media2HTTPConnection {
     private static final String TAG = "Media2HTTPConnection";
@@ -161,10 +161,10 @@
             if (host.equalsIgnoreCase("localhost")) {
                 return true;
             }
-            if (NetworkUtils.numericToInetAddress(host).isLoopbackAddress()) {
+            if (InetAddress.getByName(host).isLoopbackAddress()) {
                 return true;
             }
-        } catch (IllegalArgumentException iex) {
+        } catch (IllegalArgumentException | UnknownHostException e) {
         }
         return false;
     }
diff --git a/media/java/android/media/Media2Utils.java b/media/java/android/media/Media2Utils.java
index 066233d..5fd6191 100644
--- a/media/java/android/media/Media2Utils.java
+++ b/media/java/android/media/Media2Utils.java
@@ -31,6 +31,20 @@
     private Media2Utils() {
     }
 
+    /**
+     * Ensures that an expression checking an argument is true.
+     *
+     * @param expression the expression to check
+     * @param errorMessage the exception message to use if the check fails; will
+     *     be converted to a string using {@link String#valueOf(Object)}
+     * @throws IllegalArgumentException if {@code expression} is false
+     */
+    public static void checkArgument(boolean expression, String errorMessage) {
+        if (!expression) {
+            throw new IllegalArgumentException(errorMessage);
+        }
+    }
+
     public static synchronized void storeCookies(List<HttpCookie> cookies) {
         CookieHandler cookieHandler = CookieHandler.getDefault();
         if (cookieHandler == null) {
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 995ebb2..6301993 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -16,6 +16,9 @@
 
 package android.media;
 
+import static android.media.Utils.intersectSortedDistinctRanges;
+import static android.media.Utils.sortDistinctRanges;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
@@ -32,9 +35,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import static android.media.Utils.intersectSortedDistinctRanges;
-import static android.media.Utils.sortDistinctRanges;
-
 /**
  * Provides information about a given media codec available on the device. You can
  * iterate through all codecs available by querying {@link MediaCodecList}. For example,
@@ -1117,7 +1117,7 @@
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
                 sampleRateRange = Range.create(1, 96000);
                 bitRates = Range.create(1, 10000000);
-                maxChannels = AudioTrack.CHANNEL_COUNT_MAX;
+                maxChannels = AudioSystem.OUT_CHANNEL_COUNT_MAX;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
                 sampleRateRange = Range.create(1, 655350);
                 // lossless codec, so bitrate is ignored
@@ -1135,6 +1135,10 @@
                 maxChannels = 6;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) {
                 maxChannels = 16;
+            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC4)) {
+                sampleRates = new int[] { 44100, 48000, 96000, 192000 };
+                bitRates = Range.create(16000, 2688000);
+                maxChannels = 24;
             } else {
                 Log.w(TAG, "Unsupported mime " + mime);
                 mParent.mError |= ERROR_UNSUPPORTED;
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index c203fa9..7785900 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -434,9 +434,12 @@
      */
     @NonNull
     public List<AudioPresentation> getAudioPresentations(int trackIndex) {
-        return new ArrayList<AudioPresentation>();
+        return native_getAudioPresentations(trackIndex);
     }
 
+    @NonNull
+    private native List<AudioPresentation> native_getAudioPresentations(int trackIndex);
+
     /**
      * Get the PSSH info if present.
      * @return a map of uuid-to-bytes, with the uuid specifying
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 5dee16e..284e422 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -138,6 +138,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_AC4 = "audio/ac4";
     public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
 
     /**
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 8288976..b047f8d 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -20,6 +20,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringDef;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningAppProcessInfo;
@@ -35,6 +36,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.util.Log;
@@ -411,6 +413,9 @@
             mHandlerThread = null;
         }
 
+        setCurrentSourceInfo(null);
+        clearNextSourceInfos();
+
         // Modular DRM clean up
         mOnDrmConfigHelper = null;
         synchronized (mDrmEventCbLock) {
@@ -456,10 +461,8 @@
         synchronized (mDrmEventCbLock) {
             mDrmEventCallbackRecords.clear();
         }
-        synchronized (mSrcLock) {
-            mCurrentSourceInfo = null;
-            mNextSourceInfos.clear();
-        }
+        setCurrentSourceInfo(null);
+        clearNextSourceInfos();
 
         synchronized (mTaskLock) {
             mPendingTasks.clear();
@@ -684,6 +687,8 @@
 
     /**
      * Sets the data source as described by a DataSourceDesc.
+     * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+     * in the {@link FileDataSourceDesc} will be closed by the player.
      *
      * @param dsd the descriptor of data source you want to play
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -693,15 +698,19 @@
         return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
             @Override
             void process() throws IOException {
-                checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+                Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                 int state = getState();
-                if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
-                    throw new IllegalStateException("called in wrong state " + state);
-                }
+                try {
+                    if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
+                        throw new IllegalStateException("called in wrong state " + state);
+                    }
 
-                synchronized (mSrcLock) {
-                    mCurrentSourceInfo = new SourceInfo(dsd);
-                    handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
+                    synchronized (mSrcLock) {
+                        setCurrentSourceInfo(new SourceInfo(dsd));
+                        handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
+                    }
+                } finally {
+                    dsd.close();
                 }
             }
         });
@@ -710,6 +719,8 @@
     /**
      * Sets a single data source as described by a DataSourceDesc which will be played
      * after current data source is finished.
+     * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+     * in the {@link FileDataSourceDesc} will be closed by the player.
      *
      * @param dsd the descriptor of data source you want to play after current one
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -719,9 +730,9 @@
         return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
             @Override
             void process() {
-                checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+                Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                 synchronized (mSrcLock) {
-                    mNextSourceInfos.clear();
+                    clearNextSourceInfos();
                     mNextSourceInfos.add(new SourceInfo(dsd));
                 }
                 prepareNextDataSource();
@@ -731,6 +742,8 @@
 
     /**
      * Sets a list of data sources to be played sequentially after current data source is done.
+     * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+     * in the {@link FileDataSourceDesc} will be closed by the player.
      *
      * @param dsds the list of data sources you want to play after current one
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -743,17 +756,15 @@
                 if (dsds == null || dsds.size() == 0) {
                     throw new IllegalArgumentException("data source list cannot be null or empty.");
                 }
-                for (DataSourceDesc dsd : dsds) {
-                    if (dsd == null) {
-                        throw new IllegalArgumentException(
-                                "DataSourceDesc in the source list cannot be null.");
-                    }
-                }
 
                 synchronized (mSrcLock) {
-                    mNextSourceInfos.clear();
+                    clearNextSourceInfos();
                     for (DataSourceDesc dsd : dsds) {
-                        mNextSourceInfos.add(new SourceInfo(dsd));
+                        if (dsd != null) {
+                            mNextSourceInfos.add(new SourceInfo(dsd));
+                        } else {
+                            Log.w(TAG, "DataSourceDesc in the source list shall not be null.");
+                        }
                     }
                 }
                 prepareNextDataSource();
@@ -770,7 +781,7 @@
         return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
             @Override
             void process() {
-                mNextSourceInfos.clear();
+                clearNextSourceInfos();
             }
         });
     }
@@ -788,20 +799,20 @@
 
     private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId)
             throws IOException {
-        checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+        Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
 
         if (dsd instanceof CallbackDataSourceDesc) {
             CallbackDataSourceDesc cbDSD = (CallbackDataSourceDesc) dsd;
             handleDataSource(isCurrent,
                              srcId,
-                             cbDSD.getMedia2DataSource(),
+                             cbDSD.getDataSourceCallback(),
                              cbDSD.getStartPosition(),
                              cbDSD.getEndPosition());
         } else if (dsd instanceof FileDataSourceDesc) {
             FileDataSourceDesc fileDSD = (FileDataSourceDesc) dsd;
             handleDataSource(isCurrent,
                              srcId,
-                             fileDSD.getFileDescriptor(),
+                             fileDSD.getParcelFileDescriptor(),
                              fileDSD.getOffset(),
                              fileDSD.getLength(),
                              fileDSD.getStartPosition(),
@@ -885,7 +896,7 @@
             if (afd.getDeclaredLength() < 0) {
                 handleDataSource(isCurrent,
                         srcId,
-                        afd.getFileDescriptor(),
+                        ParcelFileDescriptor.dup(afd.getFileDescriptor()),
                         0,
                         DataSourceDesc.LONG_MAX,
                         startPos,
@@ -893,7 +904,7 @@
             } else {
                 handleDataSource(isCurrent,
                         srcId,
-                        afd.getFileDescriptor(),
+                        ParcelFileDescriptor.dup(afd.getFileDescriptor()),
                         afd.getStartOffset(),
                         afd.getDeclaredLength(),
                         startPos,
@@ -959,7 +970,8 @@
         if (file.exists()) {
             FileInputStream is = new FileInputStream(file);
             FileDescriptor fd = is.getFD();
-            handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX, startPos, endPos);
+            handleDataSource(isCurrent, srcId, ParcelFileDescriptor.dup(fd),
+                    0, DataSourceDesc.LONG_MAX, startPos, endPos);
             is.close();
         } else {
             throw new IOException("handleDataSource failed.");
@@ -983,9 +995,10 @@
      */
     private void handleDataSource(
             boolean isCurrent, long srcId,
-            FileDescriptor fd, long offset, long length,
+            ParcelFileDescriptor pfd, long offset, long length,
             long startPos, long endPos) throws IOException {
-        nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length, startPos, endPos);
+        nativeHandleDataSourceFD(isCurrent, srcId, pfd.getFileDescriptor(), offset, length,
+                startPos, endPos);
     }
 
     private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId,
@@ -994,15 +1007,15 @@
 
     /**
      * @throws IllegalStateException if it is called in an invalid state
-     * @throws IllegalArgumentException if dataSource is not a valid Media2DataSource
+     * @throws IllegalArgumentException if dataSource is not a valid DataSourceCallback
      */
-    private void handleDataSource(boolean isCurrent, long srcId, Media2DataSource dataSource,
+    private void handleDataSource(boolean isCurrent, long srcId, DataSourceCallback dataSource,
             long startPos, long endPos) {
         nativeHandleDataSourceCallback(isCurrent, srcId, dataSource, startPos, endPos);
     }
 
     private native void nativeHandleDataSourceCallback(
-            boolean isCurrent, long srcId, Media2DataSource dataSource,
+            boolean isCurrent, long srcId, DataSourceCallback dataSource,
             long startPos, long endPos);
 
     // return true if there is a next data source, false otherwise.
@@ -1036,7 +1049,10 @@
                         MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null);
                 mTaskHandler.handleMessage(msg, nextSource.mId);
 
-                mNextSourceInfos.poll();
+                SourceInfo nextSourceInfo = mNextSourceInfos.poll();
+                if (nextSource != null) {
+                    nextSourceInfo.close();
+                }
                 return prepareNextDataSource();
             }
         }
@@ -1057,7 +1073,7 @@
                 SourceInfo nextSourceInfo = mNextSourceInfos.peek();
                 if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_PREPARED) {
                     // Switch to next source only when it has been prepared.
-                    mCurrentSourceInfo = mNextSourceInfos.poll();
+                    setCurrentSourceInfo(mNextSourceInfos.poll());
 
                     long srcId = mCurrentSourceInfo.mId;
                     try {
@@ -1513,7 +1529,7 @@
         return addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
             @Override
             void process() {
-                checkArgument(params != null, "the BufferingParams cannot be null");
+                Media2Utils.checkArgument(params != null, "the BufferingParams cannot be null");
                 native_setBufferingParams(params);
             }
         });
@@ -1536,7 +1552,7 @@
         return addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
             @Override
             void process() {
-                checkArgument(params != null, "the PlaybackParams cannot be null");
+                Media2Utils.checkArgument(params != null, "the PlaybackParams cannot be null");
                 native_setPlaybackParams(params);
             }
         });
@@ -1564,7 +1580,7 @@
         return addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
             @Override
             void process() {
-                checkArgument(params != null, "the SyncParams cannot be null");
+                Media2Utils.checkArgument(params != null, "the SyncParams cannot be null");
                 native_setSyncParams(params);
             }
         });
@@ -2690,12 +2706,6 @@
         }
     }
 
-    private static void checkArgument(boolean expression, String errorMessage) {
-        if (!expression) {
-            throw new IllegalArgumentException(errorMessage);
-        }
-    }
-
     private void sendEvent(final EventNotifier notifier) {
         synchronized (mEventCbLock) {
             try {
@@ -3321,6 +3331,25 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface PrepareDrmStatusCode {}
 
+    /** @hide */
+    @IntDef({
+        MediaDrm.KEY_TYPE_STREAMING,
+        MediaDrm.KEY_TYPE_OFFLINE,
+        MediaDrm.KEY_TYPE_RELEASE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaDrmKeyType {}
+
+    /** @hide */
+    @StringDef({
+        MediaDrm.PROPERTY_VENDOR,
+        MediaDrm.PROPERTY_VERSION,
+        MediaDrm.PROPERTY_DESCRIPTION,
+        MediaDrm.PROPERTY_ALGORITHMS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaDrmStringProperty {}
+
     /**
      * Retrieves the DRM Info associated with the given source
      *
@@ -3628,7 +3657,7 @@
     public MediaDrm.KeyRequest getDrmKeyRequest(
             @NonNull DataSourceDesc dsd,
             @Nullable byte[] keySetId, @Nullable byte[] initData,
-            @Nullable String mimeType, @MediaDrm.KeyType int keyType,
+            @Nullable String mimeType, @MediaDrmKeyType int keyType,
             @Nullable Map<String, String> optionalParameters)
             throws NoDrmSchemeException {
         // TODO: this implementation only works when dsd is the only data source
@@ -3786,7 +3815,7 @@
     @NonNull
     public String getDrmPropertyString(
             @NonNull DataSourceDesc dsd,
-            @NonNull @MediaDrm.StringProperty String propertyName)
+            @NonNull @MediaDrmStringProperty String propertyName)
             throws NoDrmSchemeException {
         // TODO: this implementation only works when dsd is the only data source
         Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName);
@@ -3829,7 +3858,7 @@
     // This is a synchronous call.
     public void setDrmPropertyString(
             @NonNull DataSourceDesc dsd,
-            @NonNull @MediaDrm.StringProperty String propertyName, @NonNull String value)
+            @NonNull @MediaDrmStringProperty String propertyName, @NonNull String value)
             throws NoDrmSchemeException {
         // TODO: this implementation only works when dsd is the only data source
         Log.v(TAG, "setDrmPropertyString: propertyName: " + propertyName + " value: " + value);
@@ -4476,6 +4505,7 @@
         final DataSourceDesc mDSD;
         final long mId = mSrcIdGenerator.getAndIncrement();
         AtomicInteger mBufferedPercentage = new AtomicInteger(0);
+        boolean mClosed = false;
 
         // m*AsNextSource (below) only applies to pending data sources in the playlist;
         // the meanings of mCurrentSourceInfo.{mStateAsNextSource,mPlayPendingAsNextSource}
@@ -4487,6 +4517,17 @@
             this.mDSD = dsd;
         }
 
+        void close() {
+            synchronized (this) {
+                if (!mClosed) {
+                    if (mDSD != null) {
+                        mDSD.close();
+                    }
+                    mClosed = true;
+                }
+            }
+        }
+
         @Override
         public String toString() {
             return String.format("%s(%d)", SourceInfo.class.getName(), mId);
@@ -4517,6 +4558,26 @@
         return nextSourceInfo != null && nextSourceInfo.mId == srcId;
     }
 
+    private void setCurrentSourceInfo(SourceInfo newSourceInfo) {
+        synchronized (mSrcLock) {
+            if (mCurrentSourceInfo != null) {
+                mCurrentSourceInfo.close();
+            }
+            mCurrentSourceInfo = newSourceInfo;
+        }
+    }
+
+    private void clearNextSourceInfos() {
+        synchronized (mSrcLock) {
+            for (SourceInfo sourceInfo : mNextSourceInfos) {
+                if (sourceInfo != null) {
+                    sourceInfo.close();
+                }
+            }
+            mNextSourceInfos.clear();
+        }
+    }
+
     public static final class MetricsConstants {
         private MetricsConstants() {}
 
diff --git a/media/java/android/media/UriDataSourceDesc.java b/media/java/android/media/UriDataSourceDesc.java
index e6f39e0..6a83dab 100644
--- a/media/java/android/media/UriDataSourceDesc.java
+++ b/media/java/android/media/UriDataSourceDesc.java
@@ -21,8 +21,6 @@
 import android.content.Context;
 import android.net.Uri;
 
-import com.android.internal.util.Preconditions;
-
 import java.net.CookieHandler;
 import java.net.CookieManager;
 import java.net.HttpCookie;
@@ -33,9 +31,11 @@
 
 /**
  * @hide
- * Structure for data source descriptor.
+ * Structure of data source descriptor for sources using URI.
  *
- * Used by {@link MediaPlayer2#setDataSource(UriDataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
  * to set data source for playback.
  *
  * <p>Users should use {@link Builder} to change {@link UriDataSourceDesc}.
@@ -158,8 +158,8 @@
          * @throws NullPointerException if context or uri is null.
          */
         public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri) {
-            Preconditions.checkNotNull(context, "context cannot be null");
-            Preconditions.checkNotNull(uri, "uri cannot be null");
+            Media2Utils.checkArgument(context != null, "context cannot be null");
+            Media2Utils.checkArgument(uri != null, "uri cannot be null");
             resetDataSource();
             mUri = uri;
             mContext = context;
@@ -195,8 +195,8 @@
          */
         public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri,
                 @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) {
-            Preconditions.checkNotNull(context, "context cannot be null");
-            Preconditions.checkNotNull(uri);
+            Media2Utils.checkArgument(context != null, "context cannot be null");
+            Media2Utils.checkArgument(uri != null, "uri cannot be null");
             if (cookies != null) {
                 CookieHandler cookieHandler = CookieHandler.getDefault();
                 if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index faf4301..e25e6a5 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -88,7 +88,7 @@
     name: "libmedia2_jni",
 
     srcs: [
-        "android_media_Media2DataSource.cpp",
+        "android_media_DataSourceCallback.cpp",
         "android_media_MediaMetricsJNI.cpp",
         "android_media_MediaPlayer2.cpp",
         "android_media_SyncParams.cpp",
diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
index 5306de6..a3adddd 100644
--- a/media/jni/android_media_AudioPresentation.h
+++ b/media/jni/android_media_AudioPresentation.h
@@ -14,173 +14,135 @@
  * limitations under the License.
  */
 
-#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
-#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+#ifndef _ANDROID_MEDIA_AUDIOPRESENTATION_H_
+#define _ANDROID_MEDIA_AUDIOPRESENTATION_H_
 
 #include "jni.h"
 
-#include <media/AudioPresentationInfo.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-
+#include <media/stagefright/foundation/ADebug.h>  // CHECK
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
 #include <nativehelper/ScopedLocalRef.h>
 
 namespace android {
 
 struct JAudioPresentationInfo {
     struct fields_t {
-        jclass      clazz;
+        jclass      clazz = NULL;
         jmethodID   constructID;
 
         // list parameters
-        jclass listclazz;
+        jclass listClazz = NULL;
         jmethodID listConstructId;
         jmethodID listAddId;
 
+        // hashmap parameters
+        jclass hashMapClazz = NULL;
+        jmethodID hashMapConstructID;
+        jmethodID hashMapPutID;
+
+        // ulocale parameters
+        jclass ulocaleClazz = NULL;
+        jmethodID ulocaleConstructID;
+
         void init(JNIEnv *env) {
             jclass lclazz = env->FindClass("android/media/AudioPresentation");
-            if (lclazz == NULL) {
-                return;
-            }
-
+            CHECK(lclazz != NULL);
             clazz = (jclass)env->NewGlobalRef(lclazz);
-            if (clazz == NULL) {
-                return;
-            }
-
+            CHECK(clazz != NULL);
             constructID = env->GetMethodID(clazz, "<init>",
-                                "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
-            env->DeleteLocalRef(lclazz);
+                    "(IILandroid/icu/util/ULocale;IZZZLjava/util/Map;)V");
+            CHECK(constructID != NULL);
 
             // list objects
-            jclass llistclazz = env->FindClass("java/util/ArrayList");
-            CHECK(llistclazz != NULL);
-            listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz));
-            CHECK(listclazz != NULL);
-            listConstructId = env->GetMethodID(listclazz, "<init>", "()V");
+            jclass llistClazz = env->FindClass("java/util/ArrayList");
+            CHECK(llistClazz != NULL);
+            listClazz = static_cast<jclass>(env->NewGlobalRef(llistClazz));
+            CHECK(listClazz != NULL);
+            listConstructId = env->GetMethodID(listClazz, "<init>", "()V");
             CHECK(listConstructId != NULL);
-            listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z");
+            listAddId = env->GetMethodID(listClazz, "add", "(Ljava/lang/Object;)Z");
             CHECK(listAddId != NULL);
-            env->DeleteLocalRef(llistclazz);
+
+            // hashmap objects
+            jclass lhashMapClazz = env->FindClass("java/util/HashMap");
+            CHECK(lhashMapClazz != NULL);
+            hashMapClazz = (jclass)env->NewGlobalRef(lhashMapClazz);
+            CHECK(hashMapClazz != NULL);
+            hashMapConstructID = env->GetMethodID(hashMapClazz, "<init>", "()V");
+            CHECK(hashMapConstructID != NULL);
+            hashMapPutID = env->GetMethodID(
+                    hashMapClazz,
+                    "put",
+                    "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+            CHECK(hashMapPutID != NULL);
+
+            jclass lulocaleClazz = env->FindClass("android/icu/util/ULocale");
+            CHECK(lulocaleClazz != NULL);
+            ulocaleClazz = (jclass)env->NewGlobalRef(lulocaleClazz);
+            CHECK(ulocaleClazz != NULL);
+            ulocaleConstructID = env->GetMethodID(ulocaleClazz, "<init>", "(Ljava/lang/String;)V");
+            CHECK(ulocaleConstructID != NULL);
         }
 
         void exit(JNIEnv *env) {
-            env->DeleteGlobalRef(clazz);
-            clazz = NULL;
-            env->DeleteGlobalRef(listclazz);
-            listclazz = NULL;
+            env->DeleteGlobalRef(clazz); clazz = NULL;
+            env->DeleteGlobalRef(listClazz); listClazz = NULL;
+            env->DeleteGlobalRef(hashMapClazz); hashMapClazz = NULL;
+            env->DeleteGlobalRef(ulocaleClazz); ulocaleClazz = NULL;
         }
     };
 
-    static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
-        ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap"));
-
-        if (hashMapClazz.get() == NULL) {
-            return -EINVAL;
-        }
-        jmethodID hashMapConstructID =
-            env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
-
-        if (hashMapConstructID == NULL) {
-            return -EINVAL;
-        }
-        jmethodID hashMapPutID =
-            env->GetMethodID(
-                    hashMapClazz.get(),
-                    "put",
-                    "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-
-        if (hashMapPutID == NULL) {
-            return -EINVAL;
-        }
-
-        jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
-
-        for (size_t i = 0; i < msg->countEntries(); ++i) {
-            AMessage::Type valueType;
-            const char *key = msg->getEntryNameAt(i, &valueType);
-
-            if (!strncmp(key, "android._", 9)) {
-                // don't expose private keys (starting with android._)
-                continue;
-            }
-            jobject valueObj = NULL;
-            AString val;
-            CHECK(msg->findString(key, &val));
-            valueObj = env->NewStringUTF(val.c_str());
-            if (valueObj != NULL) {
-                ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
-                if (localeClazz.get() == NULL) {
-                    return -EINVAL;
-                }
-                jmethodID localeConstructID =
-                        env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
-                if (localeConstructID == NULL) {
-                    return -EINVAL;
-                }
-                jstring jLanguage = env->NewStringUTF(key);
-                jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
-                env->CallObjectMethod(hashMap, hashMapPutID, jLocale, valueObj);
-                env->DeleteLocalRef(jLocale); jLocale = NULL;
-                env->DeleteLocalRef(valueObj); valueObj = NULL;
-                env->DeleteLocalRef(jLanguage); jLanguage = NULL;
-            }
-        }
-
-        *map = hashMap;
-
-        return OK;
+    static jobject asJobject(JNIEnv *env, const fields_t& fields) {
+        return env->NewObject(fields.listClazz, fields.listConstructId);
     }
 
-    jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) {
-        jobject list = env->NewObject(fields.listclazz, fields.listConstructId);
-
-        for (size_t i = 0; i < info.countPresentations(); ++i) {
-            const sp<AudioPresentation> &ap = info.getPresentation(i);
-            jobject jLabelObject;
-
-            sp<AMessage> labelMessage = new AMessage();
-            for (size_t i = 0; i < ap->mLabels.size(); ++i) {
-                labelMessage->setString(ap->mLabels.keyAt(i).string(),
-                                        ap->mLabels.valueAt(i).string());
+    static void addPresentations(JNIEnv *env, const fields_t& fields,
+                    const AudioPresentationCollection& presentations, jobject presentationsJObj) {
+        for (const auto& ap : presentations) {
+            ScopedLocalRef<jobject> jLabelObject = convertLabelsToMap(env, fields, ap.mLabels);
+            if (jLabelObject == nullptr) return;
+            ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(ap.mLanguage.c_str()));
+            if (jLanguage == nullptr) return;
+            ScopedLocalRef<jobject> jLocale(env, env->NewObject(
+                            fields.ulocaleClazz, fields.ulocaleConstructID, jLanguage.get()));
+            ScopedLocalRef<jobject> jValueObj(env, env->NewObject(fields.clazz, fields.constructID,
+                            static_cast<jint>(ap.mPresentationId),
+                            static_cast<jint>(ap.mProgramId),
+                            jLocale.get(),
+                            static_cast<jint>(ap.mMasteringIndication),
+                            static_cast<jboolean>((ap.mAudioDescriptionAvailable == 1) ? 1 : 0),
+                            static_cast<jboolean>((ap.mSpokenSubtitlesAvailable == 1) ? 1 : 0),
+                            static_cast<jboolean>((ap.mDialogueEnhancementAvailable == 1) ? 1 : 0),
+                            jLabelObject.get()));
+            if (jValueObj != nullptr) {
+                env->CallBooleanMethod(presentationsJObj, fields.listAddId, jValueObj.get());
             }
-            if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) {
-                return NULL;
-            }
-            ScopedLocalRef<jclass> localeClazz(env, env->FindClass("android/icu/util/ULocale"));
-            if (localeClazz.get() == NULL) {
-                return NULL;
-            }
-            jmethodID localeConstructID =
-                    env->GetMethodID(localeClazz.get(), "<init>", "(Ljava/lang/String;)V");
-            if (localeConstructID == NULL) {
-                return NULL;
-            }
-            jstring jLanguage = env->NewStringUTF(ap->mLanguage.c_str());
-            jobject jLocale = env->NewObject(localeClazz.get(), localeConstructID, jLanguage);
-            jobject jValueObj = env->NewObject(fields.clazz, fields.constructID,
-                                static_cast<jint>(ap->mPresentationId),
-                                static_cast<jint>(ap->mProgramId),
-                                jLocale,
-                                static_cast<jint>(ap->mMasteringIndication),
-                                static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ?
-                                    1 : 0),
-                                static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ?
-                                    1 : 0),
-                                static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ?
-                                    1 : 0),
-                                jLabelObject);
-            if (jValueObj == NULL) {
-                env->DeleteLocalRef(jLanguage); jLanguage = NULL;
-                return NULL;
-            }
-
-            env->CallBooleanMethod(list, fields.listAddId, jValueObj);
-            env->DeleteLocalRef(jLocale); jLocale = NULL;
-            env->DeleteLocalRef(jValueObj); jValueObj = NULL;
-            env->DeleteLocalRef(jLanguage); jLanguage = NULL;
         }
-        return list;
+    }
+
+  private:
+    static ScopedLocalRef<jobject> convertLabelsToMap(
+            JNIEnv *env, const fields_t& fields, const std::map<std::string, std::string> &labels) {
+        ScopedLocalRef<jobject> nullMap(env, nullptr);
+        ScopedLocalRef<jobject> hashMap(env, env->NewObject(
+                        fields.hashMapClazz, fields.hashMapConstructID));
+        if (hashMap == nullptr) {
+            return nullMap;
+        }
+
+        for (const auto& label : labels) {
+            ScopedLocalRef<jstring> jLanguage(env, env->NewStringUTF(label.first.c_str()));
+            if (jLanguage == nullptr) return nullMap;
+            ScopedLocalRef<jobject> jLocale(env, env->NewObject(
+                            fields.ulocaleClazz,
+                            fields.ulocaleConstructID,
+                            jLanguage.get()));
+            if (jLocale == nullptr) return nullMap;
+            ScopedLocalRef<jobject> jValue(env, env->NewStringUTF(label.second.c_str()));
+            if (jValue == nullptr) return nullMap;
+            env->CallObjectMethod(hashMap.get(), fields.hashMapPutID, jLocale.get(), jValue.get());
+        }
+        return hashMap;
     }
 };
 }  // namespace android
diff --git a/media/jni/android_media_Media2DataSource.cpp b/media/jni/android_media_DataSourceCallback.cpp
similarity index 79%
rename from media/jni/android_media_Media2DataSource.cpp
rename to media/jni/android_media_DataSourceCallback.cpp
index b3434e9..c91d409 100644
--- a/media/jni/android_media_Media2DataSource.cpp
+++ b/media/jni/android_media_DataSourceCallback.cpp
@@ -15,10 +15,10 @@
  */
 
 //#define LOG_NDEBUG 0
-#define LOG_TAG "JMedia2DataSource-JNI"
+#define LOG_TAG "JDataSourceCallback-JNI"
 #include <utils/Log.h>
 
-#include "android_media_Media2DataSource.h"
+#include "android_media_DataSourceCallback.h"
 
 #include "log/log.h"
 #include "jni.h"
@@ -33,14 +33,14 @@
 
 static const size_t kBufferSize = 64 * 1024;
 
-JMedia2DataSource::JMedia2DataSource(JNIEnv* env, jobject source)
+JDataSourceCallback::JDataSourceCallback(JNIEnv* env, jobject source)
     : mJavaObjStatus(OK),
       mSizeIsCached(false),
       mCachedSize(0) {
-    mMedia2DataSourceObj = env->NewGlobalRef(source);
-    CHECK(mMedia2DataSourceObj != NULL);
+    mDataSourceCallbackObj = env->NewGlobalRef(source);
+    CHECK(mDataSourceCallbackObj != NULL);
 
-    ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mMedia2DataSourceObj));
+    ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mDataSourceCallbackObj));
     CHECK(media2DataSourceClass.get() != NULL);
 
     mReadAtMethod = env->GetMethodID(media2DataSourceClass.get(), "readAt", "(J[BII)I");
@@ -55,17 +55,17 @@
     CHECK(mByteArrayObj != NULL);
 }
 
-JMedia2DataSource::~JMedia2DataSource() {
+JDataSourceCallback::~JDataSourceCallback() {
     JNIEnv* env = JavaVMHelper::getJNIEnv();
-    env->DeleteGlobalRef(mMedia2DataSourceObj);
+    env->DeleteGlobalRef(mDataSourceCallbackObj);
     env->DeleteGlobalRef(mByteArrayObj);
 }
 
-status_t JMedia2DataSource::initCheck() const {
+status_t JDataSourceCallback::initCheck() const {
     return OK;
 }
 
-ssize_t JMedia2DataSource::readAt(off64_t offset, void *data, size_t size) {
+ssize_t JDataSourceCallback::readAt(off64_t offset, void *data, size_t size) {
     Mutex::Autolock lock(mLock);
 
     if (mJavaObjStatus != OK) {
@@ -76,7 +76,7 @@
     }
 
     JNIEnv* env = JavaVMHelper::getJNIEnv();
-    jint numread = env->CallIntMethod(mMedia2DataSourceObj, mReadAtMethod,
+    jint numread = env->CallIntMethod(mDataSourceCallbackObj, mReadAtMethod,
             (jlong)offset, mByteArrayObj, (jint)0, (jint)size);
     if (env->ExceptionCheck()) {
         ALOGW("An exception occurred in readAt()");
@@ -106,7 +106,7 @@
     return numread;
 }
 
-status_t JMedia2DataSource::getSize(off64_t* size) {
+status_t JDataSourceCallback::getSize(off64_t* size) {
     Mutex::Autolock lock(mLock);
 
     if (mJavaObjStatus != OK) {
@@ -118,7 +118,7 @@
     }
 
     JNIEnv* env = JavaVMHelper::getJNIEnv();
-    *size = env->CallLongMethod(mMedia2DataSourceObj, mGetSizeMethod);
+    *size = env->CallLongMethod(mDataSourceCallbackObj, mGetSizeMethod);
     if (env->ExceptionCheck()) {
         ALOGW("An exception occurred in getSize()");
         jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
@@ -139,20 +139,20 @@
     return OK;
 }
 
-void JMedia2DataSource::close() {
+void JDataSourceCallback::close() {
     Mutex::Autolock lock(mLock);
 
     JNIEnv* env = JavaVMHelper::getJNIEnv();
-    env->CallVoidMethod(mMedia2DataSourceObj, mCloseMethod);
+    env->CallVoidMethod(mDataSourceCallbackObj, mCloseMethod);
     // The closed state is effectively the same as an error state.
     mJavaObjStatus = UNKNOWN_ERROR;
 }
 
-String8 JMedia2DataSource::toString() {
-    return String8::format("JMedia2DataSource(pid %d, uid %d)", getpid(), getuid());
+String8 JDataSourceCallback::toString() {
+    return String8::format("JDataSourceCallback(pid %d, uid %d)", getpid(), getuid());
 }
 
-String8 JMedia2DataSource::getMIMEType() const {
+String8 JDataSourceCallback::getMIMEType() const {
     return String8("application/octet-stream");
 }
 
diff --git a/media/jni/android_media_Media2DataSource.h b/media/jni/android_media_DataSourceCallback.h
similarity index 80%
rename from media/jni/android_media_Media2DataSource.h
rename to media/jni/android_media_DataSourceCallback.h
index dc085f3..5bde682 100644
--- a/media/jni/android_media_Media2DataSource.h
+++ b/media/jni/android_media_DataSourceCallback.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
-#define _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
+#ifndef _ANDROID_MEDIA_DATASOURCECALLBACK_H_
+#define _ANDROID_MEDIA_DATASOURCECALLBACK_H_
 
 #include "jni.h"
 
@@ -26,16 +26,16 @@
 
 namespace android {
 
-// The native counterpart to a Java android.media.Media2DataSource. It inherits from
+// The native counterpart to a Java android.media.DataSourceCallback. It inherits from
 // DataSource.
 //
 // If the java DataSource returns an error or throws an exception it
 // will be considered to be in a broken state, and the only further call this
 // will make is to close().
-class JMedia2DataSource : public DataSource {
+class JDataSourceCallback : public DataSource {
 public:
-    JMedia2DataSource(JNIEnv *env, jobject source);
-    virtual ~JMedia2DataSource();
+    JDataSourceCallback(JNIEnv *env, jobject source);
+    virtual ~JDataSourceCallback();
 
     virtual status_t initCheck() const override;
     virtual ssize_t readAt(off64_t offset, void *data, size_t size) override;
@@ -56,15 +56,15 @@
     bool mSizeIsCached;
     off64_t mCachedSize;
 
-    jobject mMedia2DataSourceObj;
+    jobject mDataSourceCallbackObj;
     jmethodID mReadAtMethod;
     jmethodID mGetSizeMethod;
     jmethodID mCloseMethod;
     jbyteArray mByteArrayObj;
 
-    DISALLOW_EVIL_CONSTRUCTORS(JMedia2DataSource);
+    DISALLOW_EVIL_CONSTRUCTORS(JDataSourceCallback);
 };
 
 }  // namespace android
 
-#endif  // _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
+#endif  // _ANDROID_MEDIA_DATASOURCECALLBACK_H_
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 15957c6..29238d3 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "MediaExtractor-JNI"
 #include <utils/Log.h>
 
+#include "android_media_AudioPresentation.h"
 #include "android_media_MediaDataSource.h"
 #include "android_media_MediaExtractor.h"
 #include "android_media_MediaMetricsJNI.h"
@@ -56,6 +57,7 @@
 };
 
 static fields_t gFields;
+static JAudioPresentationInfo::fields_t gAudioPresentationFields;
 
 JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
     : mClass(NULL),
@@ -289,6 +291,10 @@
     return mImpl->getCachedDuration(durationUs, eos);
 }
 
+status_t JMediaExtractor::getAudioPresentations(size_t trackIdx,
+        AudioPresentationCollection *presentations) const {
+    return mImpl->getAudioPresentations(trackIdx, presentations);
+}
 }  // namespace android
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -668,6 +674,28 @@
     return JNI_TRUE;
 }
 
+static jobject android_media_MediaExtractor_getAudioPresentations(
+        JNIEnv *env, jobject thiz, jint trackIdx) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+    jobject presentationsJObj = JAudioPresentationInfo::asJobject(env, gAudioPresentationFields);
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return presentationsJObj;
+    }
+    AudioPresentationCollection presentations;
+    status_t err = extractor->getAudioPresentations(trackIdx, &presentations);
+    if (err == ERROR_END_OF_STREAM || err == ERROR_UNSUPPORTED) {
+        return presentationsJObj;
+    } else if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return presentationsJObj;
+    }
+
+    JAudioPresentationInfo::addPresentations(
+            env, gAudioPresentationFields, presentations, presentationsJObj);
+    return presentationsJObj;
+}
+
 static void android_media_MediaExtractor_native_init(JNIEnv *env) {
     jclass clazz = env->FindClass("android/media/MediaExtractor");
     CHECK(clazz != NULL);
@@ -683,6 +711,8 @@
 
     gFields.cryptoInfoSetPatternID =
         env->GetMethodID(clazz, "setPattern", "(II)V");
+
+    gAudioPresentationFields.init(env);
 }
 
 static void android_media_MediaExtractor_native_setup(
@@ -963,6 +993,9 @@
 
     {"native_getMetrics",          "()Landroid/os/PersistableBundle;",
       (void *)android_media_MediaExtractor_native_getMetrics},
+
+    { "native_getAudioPresentations", "(I)Ljava/util/List;",
+      (void *)android_media_MediaExtractor_getAudioPresentations },
 };
 
 int register_android_media_MediaExtractor(JNIEnv *env) {
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index aaa8421..baa779c 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -18,6 +18,7 @@
 #define _ANDROID_MEDIA_MEDIAEXTRACTOR_H_
 
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AudioPresentationInfo.h>
 #include <media/MediaSource.h>
 #include <media/DataSource.h>
 #include <utils/Errors.h>
@@ -66,6 +67,8 @@
     status_t getMetrics(Parcel *reply) const;
 
     bool getCachedDuration(int64_t *durationUs, bool *eos) const;
+    status_t getAudioPresentations(size_t trackIdx,
+            AudioPresentationCollection *presentations) const;
 
 protected:
     virtual ~JMediaExtractor();
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index f7de2e7..4567492 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -46,7 +46,7 @@
 #include "utils/KeyedVector.h"
 #include "utils/String8.h"
 #include "android_media_BufferingParams.h"
-#include "android_media_Media2DataSource.h"
+#include "android_media_DataSourceCallback.h"
 #include "android_media_MediaMetricsJNI.h"
 #include "android_media_PlaybackParams.h"
 #include "android_media_SyncParams.h"
@@ -423,7 +423,7 @@
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
         return;
     }
-    sp<DataSource> callbackDataSource = new JMedia2DataSource(env, dataSource);
+    sp<DataSource> callbackDataSource = new JDataSourceCallback(env, dataSource);
     sp<DataSourceDesc> dsd = new DataSourceDesc();
     dsd->mId = srcId;
     dsd->mType = DataSourceDesc::TYPE_CALLBACK;
@@ -1390,7 +1390,7 @@
     },
     {
         "nativeHandleDataSourceCallback",
-        "(ZJLandroid/media/Media2DataSource;JJ)V",
+        "(ZJLandroid/media/DataSourceCallback;JJ)V",
         (void *)android_media_MediaPlayer2_handleDataSourceCallback
     },
     {"nativePlayNextDataSource", "(J)V",                        (void *)android_media_MediaPlayer2_playNextDataSource},
diff --git a/media/proto/Android.bp b/media/proto/Android.bp
index 50d44c3..74fd525 100644
--- a/media/proto/Android.bp
+++ b/media/proto/Android.bp
@@ -7,6 +7,7 @@
     srcs: ["mediaplayer2.proto"],
     no_framework_libs: true,
     jarjar_rules: "jarjar-rules.txt",
+    sdk_version: "28",
 }
 
 cc_library_static {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java
new file mode 100644
index 0000000..f578e46
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.ImageReader;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Surface;
+
+import junit.framework.Assert;
+
+public class SurfaceUtilsTest extends junit.framework.TestCase {
+
+    @SmallTest
+    public void testInvalidSurfaceException() {
+        ImageReader reader = ImageReader.newInstance(640, 480, ImageFormat.YUV_420_888, 1);
+        Surface surface = reader.getSurface();
+        surface.release();
+
+        try {
+            SurfaceUtils.isFlexibleConsumer(surface);
+            Assert.fail("unreachable");
+        } catch (UnsupportedOperationException e) {
+            // expected
+        }
+
+        reader.close();
+    }
+}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index e7e8384..207508e 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -144,6 +144,7 @@
     AHardwareBuffer_describe; # introduced=26
     AHardwareBuffer_fromHardwareBuffer; # introduced=26
     AHardwareBuffer_getNativeHandle; # introduced=26
+    AHardwareBuffer_isSupported; # introduced=29
     AHardwareBuffer_lock; # introduced=26
     AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26
     AHardwareBuffer_release; # introduced=26
diff --git a/native/webview/plat_support/draw_gl_functor.cpp b/native/webview/plat_support/draw_gl_functor.cpp
index 7cb49da..e3e52b1 100644
--- a/native/webview/plat_support/draw_gl_functor.cpp
+++ b/native/webview/plat_support/draw_gl_functor.cpp
@@ -42,10 +42,10 @@
 class DrawGLFunctor : public Functor {
  public:
   explicit DrawGLFunctor(jlong view_context) : view_context_(view_context) {}
-  virtual ~DrawGLFunctor() {}
+  ~DrawGLFunctor() override {}
 
   // Functor
-  virtual status_t operator ()(int what, void* data) {
+  status_t operator ()(int what, void* data) override {
     using uirenderer::DrawGlInfo;
     if (!g_aw_drawgl_function) {
       ALOGE("Cannot draw: no DrawGL Function installed");
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 8f13497..f244f9f 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -45,6 +45,7 @@
         "androidx.slice_slice-builders",
         "androidx.arch.core_core-runtime",
         "androidx.lifecycle_lifecycle-extensions",
+        "car-theme-lib-bp",
         "SystemUI-tags",
         "SystemUI-proto",
     ],
diff --git a/packages/CarSystemUI/res/layout/car_volume_dialog.xml b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
index c98740e..709797d 100644
--- a/packages/CarSystemUI/res/layout/car_volume_dialog.xml
+++ b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
@@ -20,11 +20,9 @@
     android:id="@+id/volume_list"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@android:color/black"
     android:minWidth="@dimen/volume_dialog_panel_width"
-    android:theme="@style/Theme.Car.DialogListView"
-    app:dividerEndMargin="@dimen/car_keyline_1"
-    app:dividerStartMargin="@dimen/car_keyline_1"
+    android:theme="@style/PagedListViewTheme"
     app:gutter="none"
     app:scrollBarEnabled="false"
+    app:listDividerColor="@color/list_divider_color"
     app:showPagedListViewDivider="true"/>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index df8f8db..c510ab6 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -51,4 +51,6 @@
     <color name="car_grey_900">#ff212121</color>
 
     <color name="keyguard_button_text_color">@android:color/black</color>
+
+    <color name="list_divider_color">@*android:color/car_list_divider_light</color>
 </resources>
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 7f4544a..0d95d30 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -41,15 +41,16 @@
         <item name="android:colorControlHighlight">@color/nav_bar_ripple_background_color</item>
     </style>
 
-    <style name="Theme.Car.DialogListView" parent="@style/Theme.Car.NoActionBar">
-        <item name="android:colorControlActivated">@color/car_accent</item>
-        <item name="listItemBackgroundColor">@android:color/black</item>
-    </style>
-
     <style name="NavigationBarButton">
         <item name="android:layout_height">96dp</item>
         <item name="android:layout_width">96dp</item>
         <item name="android:background">@drawable/nav_button_background</item>
     </style>
 
-</resources>
+    <style name="PagedListViewTheme" parent="@style/Theme.CarSupportWrapper.NoActionBar">
+        <item name="android:background">@*android:color/car_background</item>
+        <item name="listItemBackgroundColor">@*android:color/car_background</item>
+        <item name="dividerEndMargin">@dimen/car_keyline_1</item>
+        <item name="dividerStartMargin">@dimen/car_keyline_1</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 60153fc..4b212c2 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -355,6 +355,37 @@
     }
 
     @Override
+    public void onNotificationExpansionChanged(String key, boolean isUserAction,
+            boolean isExpanded) {
+        if (DEBUG) {
+            Log.i(TAG,
+                    "onNotificationExpansionChanged " + key + ", isUserAction =" + isUserAction
+                            + ", isExpanded = isExpanded");
+        }
+    }
+
+    @Override
+    public void onNotificationDirectReply(String key) {
+        if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key);
+    }
+
+    @Override
+    public void onSuggestedReplySent(String key, CharSequence reply, int source) {
+        if (DEBUG) {
+            Log.d(TAG, "onSuggestedReplySent() called with: key = [" + key + "], reply = [" + reply
+                    + "], source = [" + source + "]");
+        }
+    }
+
+    @Override
+    public void onActionClicked(String key, Notification.Action action, int source) {
+        if (DEBUG) {
+            Log.d(TAG, "onActionClicked() called with: key = [" + key + "], action = [" + action.title
+                    + "], source = [" + source + "]");
+        }
+    }
+
+    @Override
     public void onListenerConnected() {
         if (DEBUG) Log.i(TAG, "CONNECTED");
         try {
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 56feb47..87d6e4a 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -16,27 +16,24 @@
 
 package com.android.location.fused;
 
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.ProviderPropertiesUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
-
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.location.Criteria;
-import android.location.LocationProvider;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.UserHandle;
 import android.os.WorkSource;
 
+import com.android.location.provider.LocationProviderBase;
+import com.android.location.provider.ProviderPropertiesUnbundled;
+import com.android.location.provider.ProviderRequestUnbundled;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
     private static final String TAG = "FusedLocationProvider";
 
@@ -48,7 +45,6 @@
     private static final int MSG_DISABLE = 2;
     private static final int MSG_SET_REQUEST = 3;
 
-    private final Context mContext;
     private final FusionEngine mEngine;
 
     private static class RequestWrapper {
@@ -62,13 +58,12 @@
 
     public FusedLocationProvider(Context context) {
         super(TAG, PROPERTIES);
-        mContext = context;
         mEngine = new FusionEngine(context, Looper.myLooper());
 
         // listen for user change
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+        context.registerReceiverAsUser(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 String action = intent.getAction();
@@ -122,14 +117,4 @@
         // perform synchronously
         mEngine.dump(fd, pw, args);
     }
-
-    @Override
-    public int onGetStatus(Bundle extras) {
-        return LocationProvider.AVAILABLE;
-    }
-
-    @Override
-    public long onGetStatusUpdateTime() {
-        return 0;
-    }
 }
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index d1f140f..444e724 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -16,6 +16,7 @@
         "SettingsLibAppPreference",
         "SettingsLibSearchWidget",
         "SettingsLibSettingsSpinner",
+        "SettingsLayoutPreference",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/SearchWidget/res/values-as/strings.xml b/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
new file mode 100644
index 0000000..813e764
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="search_menu" msgid="1604061903696928905">"সন্ধান সম্পৰ্কীয় ছেটিংসমূহ"</string>
+</resources>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/Android.bp b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
new file mode 100644
index 0000000..489d360
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+    name: "SettingsLayoutPreference",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+          "androidx.preference_preference",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml b/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/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/SettingsLayoutPreference/res/layout/layout_preference_frame.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
new file mode 100644
index 0000000..ee4ce49
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
@@ -0,0 +1,20 @@
+<?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.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"/>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
new file mode 100644
index 0000000..0678263
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
@@ -0,0 +1,104 @@
+<?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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/entity_header"
+    style="@style/EntityHeader"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+
+    <LinearLayout
+        android:id="@+id/entity_header_content"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:gravity="center_horizontal"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/entity_header_icon"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:scaleType="fitXY"
+            android:antialias="true"/>
+
+        <TextView
+            android:id="@+id/entity_header_title"
+            style="@style/TextAppearance.EntityHeaderTitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="false"
+            android:ellipsize="marquee"
+            android:textDirection="locale"
+            android:layout_marginTop="8dp"/>
+
+        <TextView
+            android:id="@+id/install_type"
+            style="@style/TextAppearance.EntityHeaderSummary"
+            android:visibility="gone"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="2dp"/>
+
+        <TextView
+            android:id="@+id/entity_header_summary"
+            style="@style/TextAppearance.EntityHeaderSummary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="2dp"/>
+
+        <TextView
+            android:id="@+id/entity_header_second_summary"
+            style="@style/TextAppearance.EntityHeaderSummary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/entity_header_links"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_centerVertical="true"
+        android:layout_alignParentEnd="true"
+        android:orientation="vertical">
+
+        <ImageButton
+            android:id="@android:id/button1"
+            style="?android:attr/actionOverflowButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_weight="1"
+            android:layout_height="0dp"
+            android:minWidth="24dp"
+            android:src="@null"
+            android:tint="?android:attr/colorAccent"/>
+
+        <ImageButton
+            android:id="@android:id/button2"
+            style="?android:attr/actionOverflowButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_weight="1"
+            android:layout_height="0dp"
+            android:minWidth="24dp"
+            android:src="@null"
+            android:tint="?android:attr/colorAccent"/>
+
+    </LinearLayout>
+
+</RelativeLayout>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
new file mode 100644
index 0000000..805744b
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
@@ -0,0 +1,42 @@
+<?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="EntityHeader">
+        <item name="android:background">?android:attr/colorPrimaryDark</item>
+        <item name="android:paddingTop">24dp</item>
+        <item name="android:paddingBottom">16dp</item>
+        <item name="android:paddingEnd">16dp</item>
+    </style>
+
+    <style name="TextAppearance.EntityHeaderTitle"
+           parent="@android:style/TextAppearance.Material.Subhead">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">20sp</item>
+    </style>
+
+    <style name="TextAppearance.EntityHeaderSummary"
+           parent="@android:style/TextAppearance.Material.Body1">
+        <item name="android:textAlignment">viewStart</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:gravity">start</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
new file mode 100644
index 0000000..2a635b0
--- /dev/null
+++ b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
@@ -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.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.core.content.res.TypedArrayUtils;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * A preference can be simply customized a view by adding layout attribute in xml.
+ * User also can decide whether or not LayoutPreference allows above divider or below divider.
+ *
+ * For instances,
+ *
+ * <com.android.settingslib.widget.LayoutPreference
+ *      ...
+ *      android:layout="@layout/settings_entity_header"
+ *      xxxxxxx:allowDividerAbove="true"
+ *      xxxxxxx:allowDividerBelow="true"
+ *
+ */
+public class LayoutPreference extends Preference {
+
+    private final View.OnClickListener mClickListener = v -> performClick(v);
+    private boolean mAllowDividerAbove;
+    private boolean mAllowDividerBelow;
+    private View mRootView;
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme and the supplied
+     * attribute set.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     */
+    public LayoutPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context, attrs, 0 /* defStyleAttr */);
+    }
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme, the supplied
+     * attribute set, and default style attribute.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyleAttr An attribute in the current theme that contains a
+     *                     reference to a style resource that supplies default
+     *                     values for the view. Can be 0 to not look for
+     *                     defaults.
+     */
+    public LayoutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context, attrs, defStyleAttr);
+    }
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme and a customized view id.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param resource The view id which you expected to be inflated and show in preference.
+     */
+    public LayoutPreference(Context context, int resource) {
+        this(context, LayoutInflater.from(context).inflate(resource, null, false));
+    }
+
+    /**
+     * Constructs a new LayoutPreference with the given context's theme and a customized view.
+     *
+     * @param context The Context the view is running in, through which it can
+     *                access the current theme, resources, etc.
+     * @param view The view which you expected show in preference.
+     */
+    public LayoutPreference(Context context, View view) {
+        super(context);
+        setView(view);
+    }
+
+    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Preference);
+        mAllowDividerAbove = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerAbove,
+                R.styleable.Preference_allowDividerAbove, false);
+        mAllowDividerBelow = TypedArrayUtils.getBoolean(a, R.styleable.Preference_allowDividerBelow,
+                R.styleable.Preference_allowDividerBelow, false);
+        a.recycle();
+
+        a = context.obtainStyledAttributes(
+                attrs, R.styleable.Preference, defStyleAttr, 0);
+        int layoutResource = a.getResourceId(R.styleable.Preference_android_layout, 0);
+        if (layoutResource == 0) {
+            throw new IllegalArgumentException("LayoutPreference requires a layout to be defined");
+        }
+        a.recycle();
+
+        // Need to create view now so that findViewById can be called immediately.
+        final View view = LayoutInflater.from(getContext())
+                .inflate(layoutResource, null, false);
+        setView(view);
+    }
+
+    private void setView(View view) {
+        setLayoutResource(R.layout.layout_preference_frame);
+        mRootView = view;
+        setShouldDisableView(false);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        holder.itemView.setOnClickListener(mClickListener);
+
+        final boolean selectable = isSelectable();
+        holder.itemView.setFocusable(selectable);
+        holder.itemView.setClickable(selectable);
+        holder.setDividerAllowedAbove(mAllowDividerAbove);
+        holder.setDividerAllowedBelow(mAllowDividerBelow);
+
+        FrameLayout layout = (FrameLayout) holder.itemView;
+        layout.removeAllViews();
+        ViewGroup parent = (ViewGroup) mRootView.getParent();
+        if (parent != null) {
+            parent.removeView(mRootView);
+        }
+        layout.addView(mRootView);
+    }
+
+    /**
+     * Finds the view with the given ID.
+     *
+     * @param id the ID to search for
+     * @return a view with given ID if found, or {@code null} otherwise
+     */
+    public <T extends View> T findViewById(int id) {
+        return mRootView.findViewById(id);
+    }
+
+    /**
+     * LayoutPreference whether or not allows to set a below divider.
+     */
+    public void setAllowDividerBelow(boolean allowed) {
+        mAllowDividerBelow = allowed;
+    }
+
+    /**
+     * Return a value whether or not LayoutPreference allows to set a below divider.
+     */
+    public boolean isAllowDividerBelow() {
+        return mAllowDividerBelow;
+    }
+}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index d34820c..1dd7838 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan nie skandeer vir netwerke nie"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Gestoor"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Gedeaktiveer"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-opstelling het misluk"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sal op grond van jou gebruik waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Sal waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Sal waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Tot <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minder as <xliff:g id="THRESHOLD">%1$s</xliff:g> oor"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minder as <xliff:g id="THRESHOLD">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meer as <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vra elke keer"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Totdat jy dit afskakel"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sopas"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aansluitingprogram om opgedateerde grafikadrywer in ontwikkeling te gebruik"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Foonluidspreker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index b595e2b..2ce5f44 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ለአውታረመረቦች መቃኘት አይቻልም"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"የለም"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ተቀምጧል"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ተሰናክሏል"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"የአይ.ፒ. ውቅረት መሰናከል"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"በአጠቃቀምዎ መሠረት እስከ <xliff:g id="TIME">%1$s</xliff:g> ገደማ መቆየት አለበት"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"እስከ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ገደማ ድረስ መቆየት አለበት"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"እስከ <xliff:g id="TIME">%1$s</xliff:g> ገደማ መቆየት አለበት"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"እስከ <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"ከ<xliff:g id="THRESHOLD">%1$s</xliff:g> ያነሰ ይቀራል"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"ከ<xliff:g id="THRESHOLD">%1$s</xliff:g> ያነሰ ይቀራል (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"ከ<xliff:g id="TIME_REMAINING">%1$s</xliff:g> በላይ ይቀራል (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ሁልጊዜ ጠይቅ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"እስኪያጠፉት ድረስ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ልክ አሁን"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"በግንባታ ላይ የተዘመነ የግራፊክስ ነጂን ለመጠቀም መተግበሪያን መርጠው ያስገቡ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"የስልክ ድምጽ ማጉያ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index f8c2ba2..23f3a12 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"لا يمكن فحص الشبكات"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"بدون"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"تم الحفظ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"غير مفعّلة"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏تعذّرت تهيئة عنوان IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> حسب استخدامك."</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g>."</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"حتى <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"يتبقى أكثر من <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
@@ -451,4 +453,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"الطلب في كل مرة"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"إلى أن توقف الوضع يدويًا"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"للتو"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"فعِّل التطبيق لاستخدام برنامج تشغيل الرسومات المُحدَّث في تطوير البرامج."</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"مكبر صوت الهاتف"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 72122c2..86d1459 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"নেটৱৰ্ক বিচাৰি স্কেন কৰিব পৰা নাই"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"নাই"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ছেভ কৰি থোৱা নেটৱৰ্কসমূহ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"নিষ্ক্ৰিয় হৈ আছে"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP কনফিগাৰেশ্বন বিফল হৈছে"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> পৰ্যন্ত"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>তকৈও কম সময় বাকী আছে"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>তকৈও কম সময় বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>তকৈও বেছি সময় বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"প্ৰতিবাৰতে সোধক"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"আপুনি অফ নকৰা পর্যন্ত"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"এই মাত্ৰ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"বিকাশকাৰ্য চলি থকা আপডে\'টেড গ্ৰাফিক ড্ৰাইভাৰ ব্যৱহাৰ কৰিবলৈ এপ্ অপ্ট ইন কৰক"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ফ’নৰ স্পীকাৰ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 23008e2..17d0ca6 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Şəbəkə axtarmaq olmur"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Heç biri"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Yadda saxlanılan"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiv"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Konfiqurasiya Uğursuzluğu"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"İstifadəyə əsasən təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> olana qədər"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Qalan vaxt <xliff:g id="THRESHOLD">%1$s</xliff:g> və daha azdır"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Qalan vaxt <xliff:g id="THRESHOLD">%1$s</xliff:g> və daha azdır (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Qalan vaxt <xliff:g id="TIME_REMAINING">%1$s</xliff:g> və daha çoxdur (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Hər dəfə soruşun"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Deaktiv edənə qədər"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"İndicə"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Qrafik drayverdən istifadə etmək üçün tətbiq seçin"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon spikeri"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3ee8589..56052d2 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nije moguće skenirati mreže"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfiguracija je otkazala"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g> na osnovu korišćenja"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uvek pitaj"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Omogući aplikaciju za korišćenje upravljačkog programa grafičke katice u razvoju"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 64253bf..f2c2046 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не атрымлiваецца выканаць сканаванне для сетак"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Няма"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Захавана"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Адключана"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Збой канфігурацыі IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Зараду хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g> пры цяперашнім узроўні выкарыстання"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Зараду (<xliff:g id="LEVEL">%2$s</xliff:g>) хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Зараду хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Да <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Засталося менш за <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Узровень зараду батарэі: <xliff:g id="LEVEL">%2$s</xliff:g> (хопіць менш чым на <xliff:g id="THRESHOLD">%1$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Узровень зараду батарэі: <xliff:g id="LEVEL">%2$s</xliff:g> (хопіць больш чым на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Заўсёды пытацца"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Пакуль не выключыце"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Зараз"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Выбраная праграма, якая выкарыстоўвае абноўлены драйвер графічнай сістэмы (падчас распрацоўкі)"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Дынамік тэлефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 668aa2d..5b8d6b8 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да се сканира за мрежи"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Няма"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Запазено"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Деактивирани"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Неуспешно конфигуриране на IP адреса"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g> въз основа на използването"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Следва да издържи до около <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Остава/т по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Остава/т по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Остава/т повече от <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Да се пита винаги"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"До изключване"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Току-що"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Включване на приложението за използване на актуализирания графичен драйвер в разработка"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Високоговорител на телефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index c6eed2c..7efad99 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"নেটওয়ার্কগুলির জন্য স্ক্যান করা যাবে না"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"কোনো কিছুই নয়"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"সংরক্ষিত"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"অক্ষম হয়েছে"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP কনফিগারেশনের ব্যর্থতা"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"বর্তমান ব্যবহার অনুযায়ী আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> এর থেকেও কম বাকি আছে"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"আর <xliff:g id="THRESHOLD">%1$s</xliff:g>-এর কম চার্জ বাকি আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"আরও <xliff:g id="TIME_REMAINING">%1$s</xliff:g>-এর বেশি চলবে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"প্রতিবার জিজ্ঞেস করা হবে"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"এখনই"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ডেভলপমেন্টে আপডেট হওয়া গ্রাফিক্স ড্রাইভার ব্যবহার করতে অ্যাপ বেছে নিন"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ফেনের স্পিকার"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 16179fb..92435f7 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ne može skenirati mreže"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Greška u konfiguraciji IP-a"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Trebala bi trajati otprilike do <xliff:g id="TIME">%1$s</xliff:g> na osnovu vaše upotrebe"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Trebala bi trajati do otprilike <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Trebala bi trajati otprilike do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prijavi aplikaciju za korištenje ažuriranog grafičkog drajvera u razvoju"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index f906ea4..3182f16 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"No es poden cercar xarxes"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Cap"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Desat"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivat"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuració d\'IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> segons l\'ús que en fas"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Temps restant superior a <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pregunta sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Fins que no ho desactivis"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ara mateix"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aplicació activada per utilitzar el controlador de gràfics actualitzat en desenvolupament"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altaveu del telèfon"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index f9c8669..3785433 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nelze hledat sítě"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Žádné"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Uloženo"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Vypnuto"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Selhání konfigurace protokolu IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Při vašem obvyklém využití vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zbývá více než <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pokaždé se zeptat"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dokud tuto funkci nevypnete"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Právě teď"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Přihlaste aplikaci k použití vyvíjeného aktualizovaného grafického ovladače"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Reproduktor telefonu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 6b2bd98..eeeccee 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Der kan ikke søges efter netværk"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Gemt"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiveret"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfejl"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g> baseret på dit forbrug"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Indtil <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Der er mere end <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spørg hver gang"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Indtil du deaktiverer"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Lige nu"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Tilvælg en app, der skal bruge den opdaterede grafikdriver under udvikling"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonens højttaler"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index ad6f5f2..95f7416 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Netzwerkscan nicht möglich"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Keine"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Gespeichert"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiviert"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-Konfigurationsfehler"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sollte basierend auf deiner Nutzung etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Sollte etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Sollte etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Bis <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Weniger als <xliff:g id="THRESHOLD">%1$s</xliff:g> verbleibend"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Weniger als <xliff:g id="THRESHOLD">%1$s</xliff:g> verbleibend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mehr als <xliff:g id="TIME_REMAINING">%1$s</xliff:g> verbleibend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Jedes Mal fragen"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Bis zur Deaktivierung"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Gerade eben"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"App aktivieren, um den aktualisierten Grafiktreiber in der Entwicklung zu verwenden"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon-Lautsprecher"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 5bfbf2c..310b6cf 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Δεν είναι δυνατή η σάρωση για δίκτυα"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Καμία"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Αποθηκευμένο"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Απενεργοποιημένο"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Αποτυχία διαμόρφωσης διεύθυνσης IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου, ανάλογα με τη χρήση σας"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Έως τις <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Απομένει/ουν λιγότερo/α από <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Απομένει/ουν λιγότερo/α από <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Απομένουν περισσότερα/ες από <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Να ερωτώμαι κάθε φορά"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Μέχρι την απενεργοποίηση"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Μόλις τώρα"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Επιλέξτε μια εφαρμογή για τη χρήση του ενημερωμένου προγράμματος οδήγησης γραφικών σε ανάπτυξη"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Ηχείο τηλεφώνου"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 6927fda..d7ed49a 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎Can\'t scan for networks‎‏‎‎‏‎"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‎None‎‏‎‎‏‎"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎Saved‎‏‎‎‏‎"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‎‎Disabled‎‏‎‎‏‎"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‎‏‏‎‏‎‏‎‎‎IP Configuration Failure‎‏‎‎‏‎"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‎Should last until about ‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ based on your usage‎‏‎‎‏‎"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‎Should last until about ‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ (‎‏‎‎‏‏‎<xliff:g id="LEVEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‏‎Should last until about ‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‎Until ‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎Less than ‎‏‎‎‏‏‎<xliff:g id="THRESHOLD">%1$s</xliff:g>‎‏‎‎‏‏‏‎ remaining‎‏‎‎‏‎"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎Less than ‎‏‎‎‏‏‎<xliff:g id="THRESHOLD">%1$s</xliff:g>‎‏‎‎‏‏‏‎ remaining (‎‏‎‎‏‏‎<xliff:g id="LEVEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎More than ‎‏‎‎‏‏‎<xliff:g id="TIME_REMAINING">%1$s</xliff:g>‎‏‎‎‏‏‏‎ remaining (‎‏‎‎‏‏‎<xliff:g id="LEVEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎Ask every time‎‏‎‎‏‎"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎Until you turn off‎‏‎‎‏‎"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎Just now‎‏‎‎‏‎"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎Opt in app to use updated graphcis driver in developement‎‏‎‎‏‎"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎Phone speaker‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 675084f..ecec63c 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se pueden buscar las redes."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitada"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> según el uso"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tiempo restante: más de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hasta que lo desactives"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Recién"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Habilitar app para que use el controlador de gráficos actualizado en el desarrollo"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altavoz del teléfono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index b51b847..b8fdf30 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se puede buscar redes."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Guardado"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitado"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración de IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> según el uso"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hasta: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Queda menos del <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Queda más del <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hasta que se desactive"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Justo ahora"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Habilitar aplicación para usar controlador de gráficos actualizado en desarrollo"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altavoz del teléfono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index fb077be..1246626 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Võrke ei saa kontrollida"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Puudub"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Salvestatud"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Keelatud"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP seadistamise ebaõnnestumine"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Peaks teie kasutuse põhjal kestma kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Peaks kestma kuni <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Peaks kestma kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Jäänud on alla <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Jäänud on alla <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Jäänud on üle <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Küsi iga kord"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kuni välja lülitate"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Äsja"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Lubage rakendus, et kasutada arenduses olevat värskendatud graafikadraiverit"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefoni kõlar"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 3d3b8c3..68b4840 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ezin dira sareak bilatu"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Bat ere ez"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Gordeta"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desgaituta"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ezin izan da konfiguratu IP helbidea"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Erabileraren arabera, ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> arte"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> baino gutxiago gelditzen dira"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> baino gutxiago gelditzen da (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> baino gehiago gelditzen da (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Galdetu beti"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Desaktibatu arte"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Oraintxe"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Hautatu zein aplikaziorekin erabili nahi duzun garatze-prozesuan dagoen grafikoen kontrolatzaile eguneratua"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonoaren bozgorailua"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 0af118e..7882b056 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"اسکن شبکه‌ها امکان‌پذیر نیست"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"هیچ‌کدام"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ذخیره‌شده"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"غیرفعال شد"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏پیکربندی IP انجام نشد"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"باتوجه به میزان مصرفتان، باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> شارژ داشته باشید"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) شارژ داشته باشید"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> شارژ داشته باشید"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"تا <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"کمتر از <xliff:g id="THRESHOLD">%1$s</xliff:g> باقی مانده"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"کمتر از <xliff:g id="THRESHOLD">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"بیش از <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"هربار پرسیده شود"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"تا زمانی‌که آن را خاموش کنید"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"هم‌اکنون"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"برنامه انتخاب‌شده برای استفاده از درایور گرافیک به‌روزرسانی‌شده در برنامه‌نویس"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"بلندگوی تلفن"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index dde10d0d..1904c4d 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Verkkoja ei voi etsiä."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ei mitään"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Tallennettu"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Pois käytöstä"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-kokoonpanovirhe"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Varaus loppuu käyttösi perusteella noin <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Varaus loppuu noin <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Varaus loppuu noin <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> saakka"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Alle <xliff:g id="THRESHOLD">%1$s</xliff:g> jäljellä"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Alle <xliff:g id="THRESHOLD">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Yli <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Kysy aina"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kunnes poistat sen käytöstä"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Äsken"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Lisää sovellus käyttämään päivitettyä grafiikkaohjainta kehitysvaiheessa"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Puhelimen kaiutin"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 1257ea5..45b2872 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Aucune"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivés"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>, en fonction de votre usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Jusqu\'à <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Jusqu\'à la désactivation"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sélectionnez l\'application pour utiliser le pilote graphique mis à jour en mode de conception"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Haut-parleur du téléphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 444a52e..13361d7 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Aucune"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivé"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Temps restant estimé en fonction de votre utilisation : <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Jusqu\'à <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Jusqu\'à la désactivation"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sélectionner une application pour le développement de laquelle utiliser le pilote graphique mis à jour"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Haut-parleur du téléphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 8df2697..e8cf009 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Non se poden explorar redes"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ningunha"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Gardada"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivadas"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Erro na configuración de IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"En función do uso, debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Ata: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tempo restante inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tempo restante: máis de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Ata a desactivación"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Subscribirse á aplicación para utilizar o controlador de gráficos actualizado en desenvolvemento"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altofalante do teléfono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 4a92f2a..918920d 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"નેટવર્ક્સ માટે સ્કૅન કરી શકતા નથી"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"કોઈ નહીં"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"સાચવેલા"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"અક્ષમ કર્યો"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP કન્ફિગરેશન નિષ્ફળ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"તમારા વપરાશના આધારે લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> સુધી"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> કરતાં ઓછો સમય બાકી છે"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> કરતાં ઓછો સમય બાકી છે (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> કરતાં વધુ સમય બાકી છે (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"દર વખતે પૂછો"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"તમે બંધ ન કરો ત્યાં સુધી"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"હમણાં જ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"અપડેટ કરેલ ગ્રાફિક્સ ડ્રાઇવરનો ઉપયોગ કરવા માટે અ‍ૅપ પસંદ કરો"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ફોન સ્પીકર"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 6d88c35..890d036 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्क के लिए स्‍कैन नहीं कर सकता"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"कोई नहीं"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"सेव किया गया"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्‍फ़िगरेशन की विफलता"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"आपके इस्तेमाल के हिसाब से बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> तक"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम समय बचा है"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम बैटरी बची है (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> से ज़्यादा चलने लायक बैटरी बची है (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"हर बार पूछें"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"जब तक आप इसे बंद नहीं करते"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"अभी-अभी"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"डेवलपमेंट में अपडेट किए गए ग्राफ़िक्स ड्राइवर का इस्तेमाल करने के लिए ऐप्लिकेशन में ऑप्ट इन करें"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फ़ोन स्पीकर"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 32c9a62..1464b91 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Skeniranje mreža nije moguće"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Spremljeno"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-a nije uspjela"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g> na temelju vaše upotrebe"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo sad"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Uključi aplikaciju za upotrebu ažuriranog upravljačkog programa u razvoju"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index ed19267..c9fdd45 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nem lehet beolvasni a hálózatokat"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nincs"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Mentve"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Letiltva"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurációs hiba"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"A használat alapján nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Eddig: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Kevesebb mint <xliff:g id="TIME_REMAINING">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Mindig kérdezzen rá"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kikapcsolásig"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Az imént"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"A frissített, fejlesztés alatt álló grafikus drivert használja a választott alkalmazás"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon hangszórója"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 6d516ea..3b13ed1 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Հնարավոր չէ սկանավորել ցանցերը"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ոչ մեկը"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Պահված է"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Անջատված"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP կարգավորման ձախողում"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Լիցքը պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>՝ կախված օգտագործման եղանակից"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Լիցքը (<xliff:g id="LEVEL">%2$s</xliff:g>) պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Լիցքը պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից պակաս"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից պակաս (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Մնացել է ավելի քան <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Հարցնել ամեն անգամ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Մինչև չանջատեք"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Հենց նոր"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ընտրված հավելվածը, որը պետք է օգտագործի թարմացված գրաֆիկական սարքավարը մշակման ժամանակ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Հեռախոսի բարձրախոս"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index a5f2317..4d1b2df 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak dapat memindai jaringan"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Tidak ada"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Nonaktif"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g> berdasarkan penggunaan Anda"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hingga <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tersisa lebih dari <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Selalu tanya"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Sampai Anda menonaktifkannya"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Baru saja"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ikut sertakan aplikasi untuk menggunakan driver grafis yang diupdate dalam pengembangan"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Speaker ponsel"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 6cf09a6..600ffa4 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ekki er hægt að leita að netum"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ekkert"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Vistað"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Óvirkt"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-stillingarvilla"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g> miðað við notkun þína"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Til klukkan <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minna en <xliff:g id="THRESHOLD">%1$s</xliff:g> eftir"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minna en <xliff:g id="THRESHOLD">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meira en <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spyrja í hvert skipti"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Þar til þú slekkur"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Rétt í þessu"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Velja að nota uppfærðan myndefnisrekil í þróun í forriti"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Símahátalari"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index a1b9b82..068aa91 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossibile cercare reti"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nessuna"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Salvata"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disattivata"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Errore configurazione IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Tempo stimato rimanente in base al tuo utilizzo: <xliff:g id="TIME">%1$s</xliff:g> circa"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Tempo stimato rimanente: <xliff:g id="TIME">%1$s</xliff:g> circa (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Tempo stimato rimanente: <xliff:g id="TIME">%1$s</xliff:g> circa"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Fino alle ore <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tempo rimanente: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tempo rimanente: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tempo rimanente: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Chiedi ogni volta"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Fino alla disattivazione"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Adesso"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Attiva l\'app per utilizzare il driver grafico aggiornato nella versione di sviluppo"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altoparlante telefono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 1a50622..fa38f77 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"לא ניתן לסרוק לאיתור רשתות"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ללא"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"נשמר"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"מושבת"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏כשל בתצורת IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g> על סמך השימוש במכשיר"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"עד <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"נותרו פחות מ-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"נותרו פחות מ-<xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"נותרו יותר מ-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"שאל בכל פעם"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"עד הכיבוי"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"הרגע"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"האפליקציה שנבחרה לשימוש במנהל ההתקן המעודכן לגרפיקה שבפיתוח"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"רמקול של טלפון"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 4c544af..9927654 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ネットワークをスキャンできません"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"なし"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"保存済み"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"無効"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP設定エラー"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>(使用状況に基づく)"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> まで"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>未満"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>未満(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"残り時間: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>以上(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"毎回確認"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"OFF にするまで"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"たった今"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"更新したグラフィックス ドライバを開発に使用するオプトイン アプリ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"スマートフォンのスピーカー"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 92f3049..7ba0153 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ქსელების სკანირება არა არის შესაძლებელი"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"არცერთი"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"დამახსოვრებულია"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"გამორთულია"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP კონფიგურაციის შეფერხება"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>, მოხმარების გათვალისწინებით"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g>-მდე"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"დარჩენილია <xliff:g id="THRESHOLD">%1$s</xliff:g>-ზე ნაკლები"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"დარჩენილია <xliff:g id="THRESHOLD">%1$s</xliff:g>-ზე ნაკლები დრო (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"დარჩენილია <xliff:g id="TIME_REMAINING">%1$s</xliff:g>-ზე მეტი დრო (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ყოველთვის მკითხეთ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"გამორთვამდე"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ახლახან"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"გააქტიურების აპი, რომელიც გამოიყენებს შემუშავების პროცესში მყოფ, განახლებულ გრაფიკულ დრაივერს"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ტელეფონის დინამიკი"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index b56c6fd..d5b4441 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Желілерді шолу мүмкін емес"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ешқандай"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Сақталды"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Өшірілген"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясының қатесі"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Пайдалануға байланысты шамамен <xliff:g id="TIME">%1$s</xliff:g> уақытқа жетеді"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Шамамен <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) уақытқа жетеді"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Шамамен <xliff:g id="TIME">%1$s</xliff:g> уақытқа жетеді"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> дейін"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> шамасынан аз қалды"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> шамасынан аз қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> шамасынан көп уақыт қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Әрдайым сұрау"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Өшірілгенге дейін"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Дәл қазір"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Әзірлеу барысында қолданба жаңартылған графика драйверін пайдаланады"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефон динамигі"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 5860473..d1d1c76 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"មិន​អាច​វិភាគ​រក​បណ្ដាញ"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"គ្មាន"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"បាន​រក្សាទុក"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"បាន​បិទ"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ការ​កំណត់​រចនាសម្ព័ន្ធ IP បរាជ័យ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"គួរ​តែ​អាច​ប្រើបាន​រហូតដល់​ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g> ដោយផ្អែក​លើការ​ប្រើប្រាស់​របស់អ្នក"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"គួរ​តែ​អាច​ប្រើបាន​រហូតដល់​ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"គួរ​តែ​អាច​ប្រើបាន​រហូតដល់​ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"រហូតដល់​ម៉ោង <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"នៅ​សល់​តិច​ជាង <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"នៅសល់​តិចជាង <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"នៅសល់​ច្រើនជាង <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"សួរគ្រប់ពេល"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"រហូតទាល់តែ​អ្នកបិទ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"អម្បាញ់មិញ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ភ្ជាប់កម្មវិធី​ ដើម្បី​ប្រើដ្រាយវើ​ក្រាហ្វិក​ដែលបាន​ដំឡើងជំនាន់​សម្រាប់​ការ​អភិវឌ្ឍ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ឧបករណ៍​បំពង​សំឡេង​ទូរសព្ទ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 87e18e6..46759f5 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ನೆಟ್‌ವರ್ಕ್‌ಗಳಿಗಾಗಿ ಸ್ಕ್ಯಾನ್‌ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ಯಾವುದೂ ಇಲ್ಲ"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ಉಳಿಸಲಾಗಿದೆ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ಕಾನ್ಫಿಗರೇಶನ್ ವಿಫಲತೆ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ನಿಮ್ಮ ಬಳಕೆ ಆಧರಿಸಿ <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯದವರೆಗೆ ಫೋನ್‌ ರನ್‌ ಆಗಬೇಕು"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ಸಮಯದವರೆಗೆ ಫೋನ್‌ ರನ್‌ ಆಗಬೇಕು"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> ಸಮಯದವರೆಗೆ ಫೋನ್‌ ರನ್‌ ಆಗಬೇಕು"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ರವರೆಗೆ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ನಿಮಿಷಕ್ಕಿಂತ ಕಡಿಮೆ ಸಮಯ ಉಳಿದಿದೆ"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ಕ್ಕಿಂತ ಕಡಿಮೆ (<xliff:g id="LEVEL">%2$s</xliff:g>) ಬಾಕಿ"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು (<xliff:g id="LEVEL">%2$s</xliff:g>) ಬಾಕಿ"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ಇದೀಗ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ಅಭಿವೃದ್ಧಿಯಲ್ಲಿ ಅಪ್‌ಡೇಟ್ ಮಾಡಲಾದ ಗ್ರಾಫಿಕ್‌ಗಳ ಡ್ರೈವರ್ ಬಳಸಲು ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ಫೋನ್ ಸ್ಪೀಕರ್"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 3333c0d..067175b 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"네트워크를 검색할 수 없습니다."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"없음"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"저장됨"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"사용 중지됨"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 설정 실패"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"사용량을 기준으로 약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g>까지"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> 이상 남음(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -435,7 +437,7 @@
     <string name="cancel" msgid="6859253417269739139">"취소"</string>
     <string name="okay" msgid="1997666393121016642">"확인"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"켜기"</string>
-    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"알림 일시중지 사용 설정"</string>
+    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"방해 금지 모드 사용 설정"</string>
     <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"사용 안함"</string>
     <string name="zen_interruption_level_priority" msgid="2078370238113347720">"중요 알림만"</string>
     <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"항상 확인"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"사용 중지할 때까지"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"조금 전"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"개발 중인 업데이트된 그래픽 드라이버를 사용할 앱을 선택하세요."</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"휴대전화 스피커"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index cbbc47c..8e994da 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Түйүндөрдү издөө мүмкүн эмес"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Жок"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Сакталды"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Өчүрүлгөн"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясы бузулду"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Колдонгонуңузга караганда болжол менен <xliff:g id="TIME">%1$s</xliff:g> кийин өчөт"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Болжол менен <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) кийин өчөт"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Болжол менен <xliff:g id="TIME">%1$s</xliff:g> кийин өчөт"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> чейин"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> жетпеген убакыт калды"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> жетпеген убакыт калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ашыгыраак убакыт калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ар дайым суралсын"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Бул функция өчүрүлгөнгө чейин"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Азыр эле"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Иштеп чыгууда жаңыртылган графикалык драйверлерди пайдалануу үчүн колдонмону кошуңуз"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефондун динамиги"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 21e4679..7bf46c0 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ບໍ່ສາມາດກວດຫາເຄືອຂ່າຍໄດ້"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ບໍ່ໃຊ້"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ບັນ​ທຶກແລ້ວ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ປິດການນຳໃຊ້"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ການ​ຕັ້ງ​ຄ່າ IP ລົ້ມ​ເຫຼວ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"ຈົນກວ່າຈະຮອດ <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"ຍັງເຫຼືອໜ້ອຍກວ່າ <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"ຍັງເຫຼືອໜ້ອຍກວ່າ <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"ຍັງເຫຼືອຫຼາຍກວ່າ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ຖາມທຸກເທື່ອ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ຈົນກວ່າທ່ານຈະປິດ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ຕອນນີ້"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ເຂົ້າຮ່ວມແອັບເພື່ອໃຊ້ໄດຣເວີກຣາຟິກທີ່ອັບເດດແລ້ວໃນການພັດທະນາ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ລຳໂພງໂທລະສັບ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index cbff9e7..9da3d52 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nepavyksta nuskaityti tinklų"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nėra"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Išsaugotas"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Neleidžiama"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigūracijos triktis"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Pagal tai, kaip naudojama, turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Iki <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Liko mažiau nei <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Liko mažiau nei <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Liko daugiau nei <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Klausti kaskart"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kol išjungsite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ką tik"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Pasirinkti programą, kuri bus naudojama su atnaujinta kuriama grafikos tvarkykle"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefono garsiakalbis"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index d200828..2233468 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nevar skenēt tīklus"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nav"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saglabāts"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Atspējots"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigurācijas kļūme"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Ņemot vērā lietojumu, darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Atlikušais laiks — mazāk nekā <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Atlicis mazāk nekā <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Atlicis vairāk nekā <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vaicāt katru reizi"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Līdz brīdim, kad izslēgsiet"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Tikko"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Izvēlēties izmantot atjaunināto grafikas dzini šīs lietotnes izstrādē"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Tālruņa skaļrunis"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index db16847..534d154 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да скенира за мрежи"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ниедна"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Зачувано"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Оневозможено"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Конфигурирањето ИП не успеа"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g> според вашето користење"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Уште помалку од <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Уште помалку од <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Уште повеќе од <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Секогаш прашувај"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Додека не го исклучите"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Неодамнешни"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Прифатете ја апликацијата за да се користи ажурираниот драјвер за графика во програмирање"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефонски звучник"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index c3af968..7a335cd 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"നെ‌റ്റ്‌വർക്കുകൾക്കായി സ്കാൻ ചെയ്യാനായില്ല"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ഒന്നുമില്ല"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"സംരക്ഷിച്ചു"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP കോൺഫിഗറേഷൻ പരാജയം"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"നിങ്ങളുടെ ഉപയോഗത്തെ അടിസ്ഥാനമാക്കി ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ്"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ് (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ്"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> വരെ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-ൽ കുറവ് സമയം ശേഷിക്കുന്നു"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-ൽ കുറവ് സമയം ശേഷിക്കുന്നു (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-ൽ കൂടുതൽ സമയം ശേഷിക്കുന്നു (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"എപ്പോഴും ചോദിക്കുക"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ഇപ്പോൾ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"വികസനത്തിൽ, അപ്ഡേറ്റ് ചെയ്‌ത ഗ്രാഫിക്‌സ് ഡ്രൈവർ ഉപയോഗിക്കാൻ ഓപ്റ്റ് ഇൻ ചെയ്യുക"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ഫോൺ സ്‌പീക്കർ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 8627e1b..4816643 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Сүлжээнүүдийг скан хийх боломжгүй"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Байхгүй"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Хадгалагдсан"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Идэвхгүйжүүлсэн"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP тохируулга амжилтгүй"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Таны хэрэглээнд тулгуурлан ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> хүртэл"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-с бага хугацаа үлдсэн"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-с бага хугацаа үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-с их хугацаа үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Тухай бүрт асуух"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Таныг унтраах хүртэл"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Дөнгөж сая"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Хөгжүүлэлтэд буй шинэчилсэн график драйверийг ашиглахын тулд аппад нэгдэх"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Утасны чанга яригч"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index cd7f175..8ebb182 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्कसाठी स्कॅन करू शकत नाही"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"काहीही नाही"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"सेव्ह केले"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्फिगरेशन अयशस्वी"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"तुमच्या वापरावर अवलंबून सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकावी"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकेल (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकावी"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> पेक्षा कमी शिल्लक आहे"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> पेक्षा कमी वेळ शिल्लक आहे (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> पेक्षा जास्त वेळ शिल्लक आहे (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"प्रत्येक वेळी विचारा"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"तुम्ही बंद करेपर्यंत"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"आत्ताच"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"अपडेट केलेले ग्राफिक ड्राइव्हर डेव्हलमेंटमध्ये वापरण्यासाठी अॅप निवडा"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फोनचा स्पीकर"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index d0b2e12..3593882 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak boleh mengimbas untuk rangkaian"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Tiada"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Dinyahdayakan"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g> berdasarkan penggunaan anda"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hingga <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tinggal kurang daripada <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Kurang daripada <xliff:g id="THRESHOLD">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Lebih daripada <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Tanya setiap kali"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Sehingga anda matikan"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sebentar tadi"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sertakan apl untuk menggunakan pemacu grafik yang dikemas kini dalam pembangunan"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Pembesar suara telefon"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 5f5957c..7ac3742 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ကွန်ယက်များကို စကင်မလုပ်နိုင်ပါ"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"တစ်ခုမျှ မဟုတ်ပါ"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"သိမ်းဆည်းပြီး"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ပိတ်ထားသည်"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ပြုပြင်ခြင်း မအောင်မြင်ပါ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"သင်၏ အသုံးပြုမှုအပေါ် အခြေခံ၍ <xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည်"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည်"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> အထိ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ခန့်သာ ကျန်တော့သည်"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> အောက်သာ ကျန်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ကျော် ကျန်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"အမြဲမေးပါ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"သင်ပိတ်လိုက်သည် အထိ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ယခုလေးတင်"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ဆော့ဖ်ဝဲရေးဆွဲမှုအတွင်း အပ်ဒိတ်လုပ်ထားသော ဂရပ်ဖစ်ဒရိုင်ဗာကို အသုံးပြုရန် အက်ပ်ကို ရွေးချယ်ပါ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ဖုန်းစပီကာ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 3c240f8..ae9c5f2 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan ikke søke etter nettverk"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Lagret"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Slått av"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurasjonsfeil"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g>, basert på bruken din"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Til <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Mindre enn <xliff:g id="THRESHOLD">%1$s</xliff:g> gjenstår"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mindre enn <xliff:g id="THRESHOLD">%1$s</xliff:g> gjenstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mer enn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> gjenstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spør hver gang"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Til du slår av"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Nå nettopp"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Velg app for å bruke en oppdatert grafikkdriver som er under utvikling"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonhøyttaler"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 1699870..0b4510fd 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"सञ्जालका लागि स्क्यान गर्न सक्दैन"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"कुनै पनि होइन"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"सुरक्षित गरियो"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"असक्षम पारियो"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP विन्यास असफल"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"तपाईंको प्रयोगका आधारमा लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"ब्याट्री लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> सम्म"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> भन्दा कम समय बाँकी छ"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> भन्दा कम समय बाँकी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> भन्दा बढी समय बाँकी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"प्रत्येक पटक सोध्नुहोस्"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"तपाईंले निष्क्रिय नपार्दासम्म"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"अहिले भर्खरै"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"विकासको क्रममा अद्यावधिक गरिएको ग्राफिक ड्राइभर प्रयोग गर्न अप्ट इन गर्नुहोस्"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फोनको स्पिकर"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 738df94..f227e1c 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan niet zoeken naar netwerken"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Opgeslagen"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Uitgeschakeld"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-configuratie mislukt"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g> op basis van je gebruik"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Tot <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minder dan <xliff:g id="THRESHOLD">%1$s</xliff:g> resterend"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minder dan <xliff:g id="THRESHOLD">%1$s</xliff:g> resterend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meer dan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> resterend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Altijd vragen"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Totdat je uitschakelt"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Zojuist"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Meld een app aan om het geüpdatete grafische stuurprogramma in ontwikkeling te gebruiken"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefoonluidspreker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 2d39cc6..bf8493a 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ନେଟ୍‌ୱର୍କଗୁଡ଼ିକୁ ଖୋଜିପାରୁନାହିଁ"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"କିଛି ନାହିଁ"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ସେଭ୍‌ ହୋଇଗଲା"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ଅକ୍ଷମ ହୋଇଛି"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP କନଫିଗରେଶନ ବିଫଳ ହୋଇଛି"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ଆପଣଙ୍କର ବ୍ୟବହାରକୁ ଆଧାର କରି ବ୍ୟାଟେରୀ <xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ ଚାଲିବ"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"ବ୍ୟାଟେରୀ ପାଖାପାଖି <xliff:g id="TIME">%1$s</xliff:g> ଚାଲିବ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"ବ୍ୟାଟେରୀ <xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ ଚାଲିବ"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ରୁ କମ୍ ସମୟ ବଳକା ଅଛି"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ରୁ କମ୍ ସମୟ ବଳକା ଅଛି (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>ରୁ ଅଧିକ ସମୟ ବଳକା ଅଛି(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ DND ଅନ୍‌ ରହିବ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ଏହିକ୍ଷଣି"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ଡେଭଲପ୍‍‍‍‍‍‍‍‍ମେଣ୍ଟରେ ଅପ୍‍‍‍‍‍‍‍ଡେଟ୍‍ ଗ୍ରାଫିକ୍ସ ଡ୍ରାଇଭର୍‍ ବ୍ୟବହାର କରିବାକୁ ଆପ୍ଟ ଇ‍ନ୍‍ ଅପ୍ଲିକେସନ୍‍"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ଫୋନ୍‍ ସ୍ପିକର୍‍"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 61f0447..f948a7e 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ਨੈਟਵਰਕਾਂ ਲਈ ਸਕੈਨ ਨਹੀਂ ਕਰ ਸਕਦਾ"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ਕੋਈ ਨਹੀਂ"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ਰੱਖਿਅਤ ਕੀਤਾ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ਅਯੋਗ ਬਣਾਇਆ"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ਕੌਂਫਿਗਰੇਸ਼ਨ ਅਸਫਲਤਾ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ਤੁਹਾਡੀ ਵਰਤੋਂ ਦੇ ਆਧਾਰ \'ਤੇ ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ਤੱਕ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ਤੋਂ ਘੱਟ ਸਮਾਂ ਬਾਕੀ"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ਤੋਂ ਘੱਟ ਸਮਾਂ ਬਾਕੀ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਤੋਂ ਵੱਧ ਸਮਾਂ ਬਾਕੀ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ਹੁਣੇ ਹੀ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ਅੱਪਡੇਟ ਕੀਤੇ ਵਿਕਾਸ-ਅਧੀਨ ਗ੍ਰਾਫਿਕਸ ਡਰਾਈਵਰ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਐਪ ਦੀ ਚੋਣ ਕਰੋ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ਫ਼ੋਨ ਦਾ ਸਪੀਕਰ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 519f82c..c75a894 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nie można wyszukać sieci."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Brak"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Zapisana"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Wyłączona"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Błąd konfiguracji IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Na podstawie Twojego sposobu korzystania jeszcze około <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Powinno wystarczyć do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Powinno wystarczyć do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Pozostało ponad: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Zawsze pytaj"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dopóki nie wyłączysz"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Przed chwilą"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Wybierz aplikację, która ma używać opracowywanego sterownika grafiki"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Głośnik telefonu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 16844a2..c273f59 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> com base no seu uso"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s)"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Até você desativar"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ative o app para usar o driver gráfico atualizado no desenvolvimento"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Alto-falante do smartphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index f01ddfa..26e4729 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar redes"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g> com base na sua utilização."</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g>."</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até à(s) <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Resta(m) menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Resta(m) menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Resta(m) mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Até ser desativado"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Optar pela aplicação para utilizar a placa gráfica atualizada em desenvolvimento"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altifalante do telemóvel"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 16844a2..c273f59 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> com base no seu uso"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s)"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Até você desativar"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ative o app para usar o driver gráfico atualizado no desenvolvimento"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Alto-falante do smartphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 8a7b440..94f4842 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nu se poate scana pentru rețele"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Niciuna"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Salvată"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Dezactivată"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Eroare de configurație IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"În baza utilizării, ar trebui să reziste până la aproximativ <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Ar trebui să reziste până la <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Ar trebui să reziste până la <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Până la <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"a mai rămas mai puțin de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"A mai rămas mai puțin de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"A mai rămas mai mult de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Întreabă de fiecare dată"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Până când dezactivați"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Chiar acum"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aplicația pentru înscriere pentru a folosi driverul actualizat al plăcii grafice este în dezvoltare"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Difuzorul telefonului"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 31274d1..1c331d8 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не удалось начать поиск сетей."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Нет"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Сохранено"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Отключено"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ошибка IP-конфигурации"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"При текущем уровне использования заряда хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Заряда (<xliff:g id="LEVEL">%2$s</xliff:g>) хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Заряда хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Осталось менее <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Уровень заряда батареи: <xliff:g id="LEVEL">%2$s</xliff:g> (хватит менее чем на <xliff:g id="THRESHOLD">%1$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Уровень заряда батареи: <xliff:g id="LEVEL">%2$s</xliff:g> (хватит более чем на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Всегда спрашивать"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Пока вы не отключите"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Только что"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Приложение будет использовать обновленный драйвер графической системы (на стадии разработки)"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Встроенный динамик"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index b22e068..608ff7f 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ජාල සඳහා පරිලෝකනය කළ නොහැක"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"කිසිවක් නැත"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"සුරකින ලදි"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"අබලයි"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP වින්‍යාස කිරීම අසාර්ථකයි"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ඔබේ භාවිතය මත පදනම්ව <xliff:g id="TIME">%1$s</xliff:g> පමන වන තෙක් තිබිය යුතුය"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) පමණ වන තෙක් තිබිය යුතුය"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> පමණ වන තෙක් තිබිය යුතුය"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> දක්වා"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ට වඩා අඩුවෙන් ඉතිරිව ඇත"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ට වඩා අඩුවෙන් ඉතිරිය (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>ට වඩා වැඩියෙන් ඉතිරිය (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"සෑම විටම ඉල්ලන්න"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ඔබ ක්‍රියාවිරහිත කරන තුරු"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"මේ දැන්"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"වැඩිදියුණු වෙමින් ඇති යාවත්කාලීන කළ චිත්‍රක ධාවකය භාවිත කිරීමට යෙදුමට ඇතුළු වන්න"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"දුරකථන ස්පීකරය"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 43923b8..2f76ef9 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Siete sa nedajú vyhľadávať"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Žiadne"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Uložené"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Vypnuté"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Zlyhanie konfigurácie adresy IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Mal by vydržať približne do <xliff:g id="TIME">%1$s</xliff:g> v závislosti od využitia"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Mal by vydržať približne <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Mal by vydržať približne do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zostáva viac ako <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vždy sa opýtať"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dokiaľ túto funkciu nevypnete"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Teraz"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prihlásiť aplikáciu, ktorá má používať aktualizovaný ovládač grafickej karty vo vývoji"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Reproduktor telefónu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 6d392fe..64124cb 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ni mogoče iskati omrežij"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Brez"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Shranjeno"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogočeno"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-ja ni uspela"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Naprava bi morala glede na način uporabe delovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Naprava bi morala delovati do približno <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Naprava bi morala delovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostanek: manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostali čas delovanja: manj kot <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vedno vprašaj"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dokler ne izklopite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Pravkar"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Omogočena aplikacija za uporabo posodobljenega grafičnega gonilnika pri razvoju"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvočnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index e05a019..25d575e 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nuk mund të skanojë për rrjete"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Asnjë"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"U ruajt"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Të çaktivizuara"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Dështim në konfigurimin e IP-së"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> bazuar në përdorimin tënd"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Deri në <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> të mbetura"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mbeten më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mbeten më shumë se <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pyet çdo herë"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Deri sa ta çaktivizosh"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Pikërisht tani"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prano aplikacionin për të përdorur drejtuesin e përditësuar të grafikës që është në zhvillim"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altoparlanti i telefonit"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 371b909..b58b47c 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Није могуће скенирати мреже"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Нема"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Сачувано"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Онемогућено"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурација је отказала"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g> на основу коришћења"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Преостало је мање од <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Преостало је мање од <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Преостало је више од <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Увек питај"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Док не искључите"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Управо"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Омогући апликацију за коришћење управљачког програма графичке катице у развоју"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Звучник телефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index e6872bb..7198b84 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Det går inte att söka efter nätverk"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Sparat"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Inaktiverad"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfel"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g> utifrån din användning"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Till kl. <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Mindre än <xliff:g id="THRESHOLD">%1$s</xliff:g> återstår"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mindre än <xliff:g id="THRESHOLD">%1$s</xliff:g> återstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mer än <xliff:g id="TIME_REMAINING">%1$s</xliff:g> återstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Fråga varje gång"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Tills du inaktiverar funktionen"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Nyss"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Välj om appen ska använda den uppdaterade grafikdrivrutinen under utveckling"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Mobilens högtalare"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 23efb91..657b54f 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Haiwezi kutambaza mitandao"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Hamna"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Imehifadhiwa"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Imezimwa"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Haikuweza Kusanidi IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Inapaswa kudumu hadi <xliff:g id="TIME">%1$s</xliff:g> kulingana na jinsi unavyoitumia"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Inapaswa kudumu kwa takribani <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Inapaswa kudumu hadi <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hadi <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zimesalia chini ya <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zimesalia chini ya <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zimesalia zaidi ya <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uliza kila wakati"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hadi utakapoizima"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sasa hivi"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Chagua programu itakayotumia kiendeshaji cha michoro kilichosasishwa katika hatua ya kusanidi"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Spika ya simu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 53ba738..59b42d8 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"நெட்வொர்க்குகளுக்கு ஸ்கேன் செய்யப்படவில்லை"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ஏதுமில்லை"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"சேமிக்கப்பட்டது"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"முடக்கப்பட்டது"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP உள்ளமைவில் தோல்வி"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"நீங்கள் பயன்படுத்துவதன் அடிப்படையில் <xliff:g id="TIME">%1$s</xliff:g> வரை உபயோகிக்க முடியும்"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> வரை பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> வரை பயன்படுத்த முடியும்"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> வரை"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>க்கும் குறைவாகவே பயன்படுத்த முடியும்"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>க்கும் குறைவாகவே பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>க்கும் மேல் பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ஒவ்வொரு முறையும் கேள்"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ஆஃப் செய்யும் வரை"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"சற்றுமுன்"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"உருவாக்கத்திலுள்ள புதுப்பிக்கப்பட்ட கிராஃபிக்ஸ் டிரைவரைப் பயன்படுத்த ஆப்ஸைத் தேர்ந்தெடுக்கவும்"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"மொபைல் ஸ்பீக்கர்"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index c8c6b4c..af233da 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"నెట్‌వర్క్‌ల కోసం స్కాన్ చేయడం సాధ్యపడదు"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ఏదీ లేదు"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"సేవ్ చేయబడింది"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"నిలిపివేయబడింది"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP కాన్ఫిగరేషన్ వైఫల్యం"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"మీ వినియోగం ఆధారంగా దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> వరకు"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> కంటే ఎక్కువ సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ప్రతిసారి అడుగు"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"మీరు ఆఫ్‌ చేసే వరకు"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ఇప్పుడే"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"అభివృద్దిలో అప్‌డేట్‌ చేసిన గ్రాఫిక్ డ్రైవర్‌ను ఉపయోగించడానికి యాప్‌ని ప్రారంభించండి"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ఫోన్ స్పీకర్"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index d293d59..636c3f2 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ไม่สามารถสแกนหาเครือข่าย"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ไม่มี"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"บันทึกแล้ว"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ปิดอยู่"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"การกำหนดค่า IP ล้มเหลว"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g> เมื่อดูจากการใช้งานของคุณ"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"จนถึง <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"เหลืออีกไม่ถึง <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"เหลือเวลาอีกไม่ถึง <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"เหลือเวลามากกว่า <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ถามทุกครั้ง"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"จนกว่าคุณจะปิด"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"เมื่อสักครู่"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"เลือกใช้แอปเพื่อใช้ไดรเวอร์กราฟิกที่อัปเดตในการพัฒนา"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ลำโพงโทรศัพท์"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 9da4561..1bcc36a 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Hindi makapag-scan ng mga network"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Wala"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Na-save"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Naka-disable"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Pagkabigo ng Configuration ng IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> batay sa iyong paggamit"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Tatagal hanggang mga <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hanggang <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mahigit <xliff:g id="TIME_REMAINING">%1$s</xliff:g> pa ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Magtanong palagi"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hanggang sa i-off mo"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ngayon lang"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"App sa pag-opt in para magamit ang na-update na graphics driver na ginagawa"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Speaker ng telepono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 2d5cd7f..ff669f6 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ağlar taranamıyor"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Yok"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Kaydedildi"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Devre dışı"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Yapılandırması Hatası"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Kullanımınıza göre saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Şu saate kadar: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"En fazla <xliff:g id="THRESHOLD">%1$s</xliff:g> kaldı"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"En çok <xliff:g id="THRESHOLD">%1$s</xliff:g> kaldı (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"En az <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Her zaman sor"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Siz kapatana kadar"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Az önce"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Güncellenmiş grafik sürücüsünü geliştirme ortamında kullanmak için uygulamayı kaydedin"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon hoparlörü"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 512ea86..2ded8c1 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Неможливо здійснити сканування мереж"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Немає"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Збережено"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Вимкнено"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Помилка конфігурації IP-адреси"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"На основі даних про використання, вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Залишилося менше ніж <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Залишилося менше ніж <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Залишилося понад <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Запитувати щоразу"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Доки ви не вимкнете"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Щойно"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Вибраний додаток, який використовуватиме оновлений графічний драйвер під час розробки"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Динамік телефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 84ad3ed..7dc4690 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"نیٹ ورکس کیلئے اسکین نہيں کر سکتے ہیں"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"کوئی نہیں"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"محفوظ کردیا گیا"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"غیر فعال"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏IP کنفیگریشن کی ناکامی"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"آپ کے استعمال کی بنیاد پر تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> تک"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> سے زیادہ باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ہر بار پوچھیں"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"یہاں تک کہ آپ آف کر دیں"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ابھی ابھی"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ڈیولپمنٹ میں اپ ڈیٹ کردہ گرافکس ڈرائیور کو استعمال کرنے کے لیے ایپ آپٹ ان کریں"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"فون اسپیکر"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index af2ada5..5f92c9a 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tarmoqlarni tekshirib chiqishni iloji bo‘lmadi"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Hech qanday"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saqlandi"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"O‘chiq"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP manzilini sozlab bo‘lmadi"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Joriy holatda taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> gacha"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>dan kamroq vaqt qoldi"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>dan kamroq vaqt qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>dan ko‘proq vaqt qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Har safar so‘ralsin"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Bekor qilinmaguncha"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Hozir"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ilova yangilangan grafik drayverdan (hali ishlov jarayonida) foydalanadi"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon karnayi"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index f454127..eb10fb1 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Không thể dò tìm mạng"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Không"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Đã lưu"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Đã tắt"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Lỗi cấu hình IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g> dựa trên mức sử dụng của bạn"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Cho đến <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Còn lại không đến <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Còn lại không đến <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Còn lại hơn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Luôn hỏi"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Cho đến khi bạn tắt"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Vừa xong"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Chọn ứng dụng để sử dụng trình điều khiển đồ họa được cập nhật trong giai đoạn phát triển"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Loa điện thoại"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 10a20be..7b14138 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"无法扫描网络"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"无"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"已保存"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 配置失败"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根据您的使用情况,估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"目前电量为 <xliff:g id="LEVEL">%2$s</xliff:g>,估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"直到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"剩余电池续航时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"电量剩余使用时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"电量剩余使用时间超过 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都询问"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"直到您将其关闭"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"刚刚"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"为应用启用更新后的显卡驱动,以在开发过程中使用"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手机扬声器"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 1510545..48b6959 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃瞄網絡"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根據您的使用情況,電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"還可用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"剩餘電量時間少於 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"還有少於 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"還有超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都詢問"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"直至您關閉為止"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"剛剛"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"請選取應用程式,以在開發階段使用更新的顯示卡驅動程式"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手機喇叭"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 90c0d80..684569b 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃描網路"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根據你的使用情形,預估可持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"目前電量 <xliff:g id="LEVEL">%2$s</xliff:g>,預估還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"預估還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"電池可用時間超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都詢問"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"直到你關閉為止"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"剛剛"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"選取要在開發階段使用最新版繪圖驅動程式的應用程式"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手機喇叭"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index e4f9f4d7..d107a5a 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ayikwazi ukuhlola amanethiwekhi"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Lutho"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Kulondoloziwe"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Akusebenzi"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ukwehluleka kokulungiswa kwe-IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g> kusukela ekusetshenzisweni kwakho"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Kuze kube ngu-<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Kusele okungaphansi kunokungu-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Ngaphansi kuka-<xliff:g id="THRESHOLD">%1$s</xliff:g> osele (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Ngaphezu kuka-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Buza njalo"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Uze uvale isikrini"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Khona manje"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Uhlelo lokusebenza lokukhetha ukungena olungasebenzisa idrayivu yamagrafikhi ekuthuthukiseni"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Isipikha sefoni"</string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 45a3bb0..a97e054 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -54,18 +54,17 @@
     private final Context mContext;
     private final BluetoothAdapter mLocalAdapter;
     private final LocalBluetoothProfileManager mProfileManager;
+    private final Object mProfileLock = new Object();
     BluetoothDevice mDevice;
     private long mHiSyncId;
     // Need this since there is no method for getting RSSI
     short mRssi;
     // mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is
     // because current sub device is only for HearingAid and its profile is the same.
-    private final List<LocalBluetoothProfile> mProfiles =
-            Collections.synchronizedList(new ArrayList<>());
+    private final List<LocalBluetoothProfile> mProfiles = new ArrayList<>();
 
     // List of profiles that were previously in mProfiles, but have been removed
-    private final List<LocalBluetoothProfile> mRemovedProfiles =
-            Collections.synchronizedList(new ArrayList<>());
+    private final List<LocalBluetoothProfile> mRemovedProfiles = new ArrayList<>();
 
     // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
     private boolean mLocalNapRoleConnected;
@@ -74,7 +73,7 @@
 
     private int mMessageRejectionCount;
 
-    private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
+    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;
@@ -134,36 +133,42 @@
             }
             return;
         }
-        if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
-            if (profile instanceof MapProfile) {
-                profile.setPreferred(mDevice, true);
-            }
-            if (!mProfiles.contains(profile)) {
-                mRemovedProfiles.remove(profile);
-                mProfiles.add(profile);
-                if (profile instanceof PanProfile &&
-                        ((PanProfile) profile).isLocalRoleNap(mDevice)) {
-                    // Device doesn't support NAP, so remove PanProfile on disconnect
-                    mLocalNapRoleConnected = true;
+
+        synchronized (mProfileLock) {
+            if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
+                if (profile instanceof MapProfile) {
+                    profile.setPreferred(mDevice, true);
                 }
+                if (!mProfiles.contains(profile)) {
+                    mRemovedProfiles.remove(profile);
+                    mProfiles.add(profile);
+                    if (profile instanceof PanProfile
+                            && ((PanProfile) profile).isLocalRoleNap(mDevice)) {
+                        // Device doesn't support NAP, so remove PanProfile on disconnect
+                        mLocalNapRoleConnected = true;
+                    }
+                }
+            } else if (profile instanceof MapProfile
+                    && newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+                profile.setPreferred(mDevice, false);
+            } else if (mLocalNapRoleConnected && profile instanceof PanProfile
+                    && ((PanProfile) profile).isLocalRoleNap(mDevice)
+                    && newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+                Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
+                mProfiles.remove(profile);
+                mRemovedProfiles.add(profile);
+                mLocalNapRoleConnected = false;
             }
-        } else if (profile instanceof MapProfile &&
-                newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
-            profile.setPreferred(mDevice, false);
-        } else if (mLocalNapRoleConnected && profile instanceof PanProfile &&
-                ((PanProfile) profile).isLocalRoleNap(mDevice) &&
-                newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
-            Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
-            mProfiles.remove(profile);
-            mRemovedProfiles.add(profile);
-            mLocalNapRoleConnected = false;
         }
+
         fetchActiveDevices();
     }
 
     public void disconnect() {
-        for (LocalBluetoothProfile profile : mProfiles) {
-            disconnect(profile);
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : mProfiles) {
+                disconnect(profile);
+            }
         }
         // Disconnect  PBAP server in case its connected
         // This is to ensure all the profiles are disconnected as some CK/Hs do not
@@ -210,32 +215,35 @@
     }
 
     private void connectWithoutResettingTimer(boolean connectAllProfiles) {
-        // Try to initialize the profiles if they were not.
-        if (mProfiles.isEmpty()) {
-            // if mProfiles is empty, then do not invoke updateProfiles. This causes a race
-            // condition with carkits during pairing, wherein RemoteDevice.UUIDs have been updated
-            // from bluetooth stack but ACTION.uuid is not sent yet.
-            // Eventually ACTION.uuid will be received which shall trigger the connection of the
-            // various profiles
-            // If UUIDs are not available yet, connect will be happen
-            // upon arrival of the ACTION_UUID intent.
-            Log.d(TAG, "No profiles. Maybe we will connect later");
-            return;
-        }
+        synchronized (mProfileLock) {
+            // Try to initialize the profiles if they were not.
+            if (mProfiles.isEmpty()) {
+                // if mProfiles is empty, then do not invoke updateProfiles. This causes a race
+                // condition with carkits during pairing, wherein RemoteDevice.UUIDs have been
+                // updated from bluetooth stack but ACTION.uuid is not sent yet.
+                // Eventually ACTION.uuid will be received which shall trigger the connection of the
+                // various profiles
+                // If UUIDs are not available yet, connect will be happen
+                // upon arrival of the ACTION_UUID intent.
+                Log.d(TAG, "No profiles. Maybe we will connect later");
+                return;
+            }
 
-        int preferredProfiles = 0;
-        for (LocalBluetoothProfile profile : mProfiles) {
-            if (connectAllProfiles ? profile.accessProfileEnabled() : profile.isAutoConnectable()) {
-                if (profile.isPreferred(mDevice)) {
-                    ++preferredProfiles;
-                    connectInt(profile);
+            int preferredProfiles = 0;
+            for (LocalBluetoothProfile profile : mProfiles) {
+                if (connectAllProfiles ? profile.accessProfileEnabled()
+                        : profile.isAutoConnectable()) {
+                    if (profile.isPreferred(mDevice)) {
+                        ++preferredProfiles;
+                        connectInt(profile);
+                    }
                 }
             }
-        }
-        if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
+            if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
 
-        if (preferredProfiles == 0) {
-            connectAutoConnectableProfiles();
+            if (preferredProfiles == 0) {
+                connectAutoConnectableProfiles();
+            }
         }
     }
 
@@ -244,10 +252,12 @@
             return;
         }
 
-        for (LocalBluetoothProfile profile : mProfiles) {
-            if (profile.isAutoConnectable()) {
-                profile.setPreferred(mDevice, true);
-                connectInt(profile);
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : mProfiles) {
+                if (profile.isAutoConnectable()) {
+                    profile.setPreferred(mDevice, true);
+                    connectInt(profile);
+                }
             }
         }
     }
@@ -515,14 +525,16 @@
      * @return Whether it is connected.
      */
     public boolean isConnected() {
-        for (LocalBluetoothProfile profile : mProfiles) {
-            int status = getProfileConnectionState(profile);
-            if (status == BluetoothProfile.STATE_CONNECTED) {
-                return true;
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : mProfiles) {
+                int status = getProfileConnectionState(profile);
+                if (status == BluetoothProfile.STATE_CONNECTED) {
+                    return true;
+                }
             }
-        }
 
-        return false;
+            return false;
+        }
     }
 
     public boolean isConnectedProfile(LocalBluetoothProfile profile) {
@@ -532,14 +544,16 @@
     }
 
     public boolean isBusy() {
-        for (LocalBluetoothProfile profile : mProfiles) {
-            int status = getProfileConnectionState(profile);
-            if (status == BluetoothProfile.STATE_CONNECTING
-                    || status == BluetoothProfile.STATE_DISCONNECTING) {
-                return true;
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : mProfiles) {
+                int status = getProfileConnectionState(profile);
+                if (status == BluetoothProfile.STATE_CONNECTING
+                        || status == BluetoothProfile.STATE_DISCONNECTING) {
+                    return true;
+                }
             }
+            return getBondState() == BluetoothDevice.BOND_BONDING;
         }
-        return getBondState() == BluetoothDevice.BOND_BONDING;
     }
 
     private boolean updateProfiles() {
@@ -554,8 +568,10 @@
          */
         processPhonebookAccess();
 
-        mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles,
-                                       mLocalNapRoleConnected, mDevice);
+        synchronized (mProfileLock) {
+            mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles,
+                    mLocalNapRoleConnected, mDevice);
+        }
 
         if (BluetoothUtils.D) {
             Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
@@ -616,7 +632,9 @@
 
     void onBondingStateChanged(int bondState) {
         if (bondState == BluetoothDevice.BOND_NONE) {
-            mProfiles.clear();
+            synchronized (mProfileLock) {
+                mProfiles.clear();
+            }
             mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
             mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
             mDevice.setSimAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
@@ -646,9 +664,11 @@
     public List<LocalBluetoothProfile> getConnectableProfiles() {
         List<LocalBluetoothProfile> connectableProfiles =
                 new ArrayList<LocalBluetoothProfile>();
-        for (LocalBluetoothProfile profile : mProfiles) {
-            if (profile.accessProfileEnabled()) {
-                connectableProfiles.add(profile);
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : mProfiles) {
+                if (profile.accessProfileEnabled()) {
+                    connectableProfiles.add(profile);
+                }
             }
         }
         return connectableProfiles;
@@ -823,10 +843,12 @@
 
     public int getMaxConnectionState() {
         int maxState = BluetoothProfile.STATE_DISCONNECTED;
-        for (LocalBluetoothProfile profile : getProfiles()) {
-            int connectionStatus = getProfileConnectionState(profile);
-            if (connectionStatus > maxState) {
-                maxState = connectionStatus;
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : getProfiles()) {
+                int connectionStatus = getProfileConnectionState(profile);
+                if (connectionStatus > maxState) {
+                    maxState = connectionStatus;
+                }
             }
         }
         return maxState;
@@ -843,32 +865,34 @@
         boolean hfpConnected = true;         // HFP is connected
         boolean hearingAidConnected = true;  // Hearing Aid is connected
 
-        for (LocalBluetoothProfile profile : getProfiles()) {
-            int connectionStatus = getProfileConnectionState(profile);
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : getProfiles()) {
+                int connectionStatus = getProfileConnectionState(profile);
 
-            switch (connectionStatus) {
-                case BluetoothProfile.STATE_CONNECTING:
-                case BluetoothProfile.STATE_DISCONNECTING:
-                    return mContext.getString(
-                            BluetoothUtils.getConnectionStateSummary(connectionStatus));
+                switch (connectionStatus) {
+                    case BluetoothProfile.STATE_CONNECTING:
+                    case BluetoothProfile.STATE_DISCONNECTING:
+                        return mContext.getString(
+                                BluetoothUtils.getConnectionStateSummary(connectionStatus));
 
-                case BluetoothProfile.STATE_CONNECTED:
-                    profileConnected = true;
-                    break;
+                    case BluetoothProfile.STATE_CONNECTED:
+                        profileConnected = true;
+                        break;
 
-                case BluetoothProfile.STATE_DISCONNECTED:
-                    if (profile.isProfileReady()) {
-                        if ((profile instanceof A2dpProfile) ||
-                                (profile instanceof A2dpSinkProfile)) {
-                            a2dpConnected = false;
-                        } else if ((profile instanceof HeadsetProfile) ||
-                                (profile instanceof HfpClientProfile)) {
-                            hfpConnected = false;
-                        } else if (profile instanceof HearingAidProfile) {
-                            hearingAidConnected = false;
+                    case BluetoothProfile.STATE_DISCONNECTED:
+                        if (profile.isProfileReady()) {
+                            if (profile instanceof A2dpProfile
+                                    || profile instanceof A2dpSinkProfile) {
+                                a2dpConnected = false;
+                            } else if (profile instanceof HeadsetProfile
+                                    || profile instanceof HfpClientProfile) {
+                                hfpConnected = false;
+                            } else if (profile instanceof HearingAidProfile) {
+                                hearingAidConnected = false;
+                            }
                         }
-                    }
-                    break;
+                        break;
+                }
             }
         }
 
@@ -924,32 +948,34 @@
         boolean hfpNotConnected = false;        // HFP is preferred but not connected
         boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
 
-        for (LocalBluetoothProfile profile : getProfiles()) {
-            int connectionStatus = getProfileConnectionState(profile);
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : getProfiles()) {
+                int connectionStatus = getProfileConnectionState(profile);
 
-            switch (connectionStatus) {
-                case BluetoothProfile.STATE_CONNECTING:
-                case BluetoothProfile.STATE_DISCONNECTING:
-                    return mContext.getString(
-                            BluetoothUtils.getConnectionStateSummary(connectionStatus));
+                switch (connectionStatus) {
+                    case BluetoothProfile.STATE_CONNECTING:
+                    case BluetoothProfile.STATE_DISCONNECTING:
+                        return mContext.getString(
+                                BluetoothUtils.getConnectionStateSummary(connectionStatus));
 
-                case BluetoothProfile.STATE_CONNECTED:
-                    profileConnected = true;
-                    break;
+                    case BluetoothProfile.STATE_CONNECTED:
+                        profileConnected = true;
+                        break;
 
-                case BluetoothProfile.STATE_DISCONNECTED:
-                    if (profile.isProfileReady()) {
-                        if ((profile instanceof A2dpProfile) ||
-                                (profile instanceof A2dpSinkProfile)){
-                            a2dpNotConnected = true;
-                        } else if ((profile instanceof HeadsetProfile) ||
-                                (profile instanceof HfpClientProfile)) {
-                            hfpNotConnected = true;
-                        } else if (profile instanceof  HearingAidProfile) {
-                            hearingAidNotConnected = true;
+                    case BluetoothProfile.STATE_DISCONNECTED:
+                        if (profile.isProfileReady()) {
+                            if (profile instanceof A2dpProfile
+                                    || profile instanceof A2dpSinkProfile) {
+                                a2dpNotConnected = true;
+                            } else if (profile instanceof HeadsetProfile
+                                    || profile instanceof HfpClientProfile) {
+                                hfpNotConnected = true;
+                            } else if (profile instanceof HearingAidProfile) {
+                                hearingAidNotConnected = true;
+                            }
                         }
-                    }
-                    break;
+                        break;
+                }
             }
         }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index f7f6589..3a62838 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -50,7 +50,7 @@
     }
 
     public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
-        return new ArrayList<CachedBluetoothDevice>(mCachedDevices);
+        return new ArrayList<>(mCachedDevices);
     }
 
     public static boolean onDeviceDisappeared(CachedBluetoothDevice cachedDevice) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 86928fc..4f81daf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -154,8 +154,10 @@
     static final String KEY_CARRIER_NAME = "key_carrier_name";
     static final AtomicInteger sLastId = new AtomicInteger(0);
 
-    /**
-     * These values are matched in string arrays -- changes must be kept in sync
+    /*
+     * NOTE: These constants for security and PSK types are saved to the bundle in saveWifiState,
+     * and sent across IPC. The numeric values should remain stable, otherwise the changes will need
+     * to be synced with other unbundled users of this library.
      */
     public static final int SECURITY_NONE = 0;
     public static final int SECURITY_WEP = 1;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index ede248b..8757eed 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -55,6 +55,8 @@
                     paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/AppPreference/res"));
                     paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res"));
                     paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res"));
+                    paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/"
+                            + "SettingsLayoutPreference/res"));
                     paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res"));
                     paths.add(resourcePath("file:frameworks/base/core/res/res"));
                     paths.add(resourcePath("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.appcompat_appcompat-nodeps/android_common/aar/res/"));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
new file mode 100644
index 0000000..427a611
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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 static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class LayoutPreferenceTest {
+
+    private LayoutPreference mPreference;
+    private PreferenceViewHolder mHolder;
+
+    @Before
+    public void setUp() {
+        final Context mContext = RuntimeEnvironment.application;
+        mPreference = new LayoutPreference(mContext, R.layout.settings_entity_header);
+        mHolder = PreferenceViewHolder.createInstanceForTests(LayoutInflater.from(mContext)
+                .inflate(R.layout.layout_preference_frame, null, false));
+    }
+
+    @Test
+    public void setOnPreferenceClickListener_layoutPreferenceShouldListenClickEvent() {
+        final OnPreferenceClickListener listener = mock(OnPreferenceClickListener.class);
+
+        mPreference.setOnPreferenceClickListener(listener);
+        mPreference.onBindViewHolder(mHolder);
+
+        mHolder.itemView.callOnClick();
+
+        verify(listener).onPreferenceClick(mPreference);
+        assertThat(mHolder.itemView.isFocusable()).isTrue();
+        assertThat(mHolder.itemView.isClickable()).isTrue();
+    }
+
+    @Test
+    public void setNonSelectable_viewShouldNotBeSelectable() {
+        mPreference.setSelectable(false);
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mHolder.itemView.isFocusable()).isFalse();
+        assertThat(mHolder.itemView.isClickable()).isFalse();
+    }
+
+    @Test
+    public void disableSomeView_shouldMaintainStateAfterBind() {
+        mPreference.findViewById(android.R.id.button1).setEnabled(false);
+        mPreference.findViewById(android.R.id.button2).setEnabled(true);
+
+        mPreference.onBindViewHolder(mHolder);
+
+        assertThat(mPreference.findViewById(android.R.id.button1).isEnabled()).isFalse();
+        assertThat(mPreference.findViewById(android.R.id.button2).isEnabled()).isTrue();
+    }
+
+    @Test
+    public void allowDividerBelow_shouldSaveCorrectDividerStatus() {
+        mPreference.setAllowDividerBelow(true);
+
+        assertThat(mPreference.isAllowDividerBelow()).isTrue();
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index cbb6e82..b2c12b2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -855,6 +855,10 @@
                 GlobalSettingsProto.MultiSim.SMS_PROMPT);
         p.end(multiSimToken);
 
+        dumpSetting(s, p,
+                Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
+                GlobalSettingsProto.NATIVE_FLAGS_HEALTH_CHECK_ENABLED);
+
         final long netstatsToken = p.start(GlobalSettingsProto.NETSTATS);
         dumpSetting(s, p,
                 Settings.Global.NETSTATS_ENABLED,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2227642..00ea45c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -186,6 +186,7 @@
             Settings.NameValueTable.VALUE, null);
 
     public static final String RESULT_ROWS_DELETED = "result_rows_deleted";
+    public static final String RESULT_SETTINGS_LIST = "result_settings_list";
 
     // Overlay specified settings whitelisted for Instant Apps
     private static final Set<String> OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS = new ArraySet<>();
@@ -483,6 +484,27 @@
                 return result;
             }
 
+            case Settings.CALL_METHOD_LIST_SYSTEM: {
+                Bundle result = new Bundle();
+                result.putStringArrayList(RESULT_SETTINGS_LIST,
+                        buildSettingsList(getAllSystemSettings(requestingUserId, null)));
+                return result;
+            }
+
+            case Settings.CALL_METHOD_LIST_SECURE: {
+                Bundle result = new Bundle();
+                result.putStringArrayList(RESULT_SETTINGS_LIST,
+                        buildSettingsList(getAllSecureSettings(requestingUserId, null)));
+                return result;
+            }
+
+            case Settings.CALL_METHOD_LIST_GLOBAL: {
+                Bundle result = new Bundle();
+                result.putStringArrayList(RESULT_SETTINGS_LIST,
+                        buildSettingsList(getAllGlobalSettings(null)));
+                return result;
+            }
+
             default: {
                 Slog.w(LOG_TAG, "call() with invalid method: " + method);
             } break;
@@ -552,6 +574,20 @@
         }
     }
 
+    private ArrayList<String> buildSettingsList(Cursor cursor) {
+        final ArrayList<String> lines = new ArrayList<String>();
+        try {
+            while (cursor != null && cursor.moveToNext()) {
+                lines.add(cursor.getString(1) + "=" + cursor.getString(2));
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return lines;
+    }
+
     @Override
     public Uri insert(Uri uri, ContentValues values) {
         if (DEBUG) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index f8445fd..13537c4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -19,8 +19,6 @@
 import android.app.ActivityManager;
 import android.content.IContentProvider;
 import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Process;
@@ -265,9 +263,6 @@
             }
             if (mUser < 0) {
                 mUser = UserHandle.USER_SYSTEM;
-            } else if (mVerb == CommandVerb.LIST) {
-                perr.println("--user not supported for list.");
-                return -1;
             }
             UserManager userManager = UserManager.get(mProvider.getContext());
             if (userManager.getUserInfo(mUser) == null) {
@@ -304,27 +299,22 @@
             return 0;
         }
 
-        private List<String> listForUser(IContentProvider provider, int userHandle, String table) {
-            final Uri uri = "system".equals(table) ? Settings.System.CONTENT_URI
-                    : "secure".equals(table) ? Settings.Secure.CONTENT_URI
-                    : "global".equals(table) ? Settings.Global.CONTENT_URI
-                    : null;
-            final ArrayList<String> lines = new ArrayList<String>();
-            if (uri == null) {
-                return lines;
+        List<String> listForUser(IContentProvider provider, int userHandle, String table) {
+            final String callListCommand;
+            if ("system".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_SYSTEM;
+            else if ("secure".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_SECURE;
+            else if ("global".equals(table)) callListCommand = Settings.CALL_METHOD_LIST_GLOBAL;
+            else {
+                getErrPrintWriter().println("Invalid table; no list performed");
+                throw new IllegalArgumentException("Invalid table " + table);
             }
+            final ArrayList<String> lines = new ArrayList<String>();
             try {
-                final Cursor cursor = provider.query(resolveCallingPackage(), uri, null, null,
-                        null);
-                try {
-                    while (cursor != null && cursor.moveToNext()) {
-                        lines.add(cursor.getString(1) + "=" + cursor.getString(2));
-                    }
-                } finally {
-                    if (cursor != null) {
-                        cursor.close();
-                    }
-                }
+                Bundle arg = new Bundle();
+                arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
+                Bundle result =
+                        provider.call(resolveCallingPackage(), callListCommand, null, arg);
+                lines.addAll(result.getStringArrayList(SettingsProvider.RESULT_SETTINGS_LIST));
                 Collections.sort(lines);
             } catch (RemoteException e) {
                 throw new RuntimeException("Failed in IPC", e);
@@ -483,7 +473,7 @@
                 pw.println("  reset [--user <USER_ID> | current] NAMESPACE {PACKAGE_NAME | RESET_MODE}");
                 pw.println("      Reset the global/secure table for a package with mode.");
                 pw.println("      RESET_MODE is one of {untrusted_defaults, untrusted_clear, trusted_defaults}, case-insensitive");
-                pw.println("  list NAMESPACE");
+                pw.println("  list [--user <USER_ID> | current] NAMESPACE");
                 pw.println("      Print all defined keys.");
                 pw.println("      NAMESPACE is one of {system, secure, global}, case-insensitive");
             }
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index f8b61cd..44354bc1 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -302,11 +302,13 @@
     }
 
     /**
-     * Returns whether there is a soft nav bar.
+     * Returns whether there is a soft nav bar on specified display.
+     *
+     * @param displayId the id of display to check if there is a software navigation bar.
      */
-    public boolean hasSoftNavigationBar() {
+    public boolean hasSoftNavigationBar(int displayId) {
         try {
-            return mIwm.hasNavigationBar();
+            return mIwm.hasNavigationBar(displayId);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
index dfa38ba..8723fb9 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
@@ -631,7 +631,8 @@
                     }
                 };
                 WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
-                        future, animStartedListener, getHandler(), true /* scaleUp */);
+                        future, animStartedListener, getHandler(), true /* scaleUp */,
+                        getContext().getDisplayId());
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
                         event.task.getTopComponent().flattenToShortString());
             } else {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index f492208..4891e50 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -30,6 +30,12 @@
     int VERSION = 1;
 
     void startPendingIntentDismissingKeyguard(PendingIntent intent);
+
+    /**
+     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but
+     * allow you to specify the callback that is executed after the intent is sent.
+     */
+    void startPendingIntentDismissingKeyguard(PendingIntent intent, Runnable intentSentCallback);
     void startActivity(Intent intent, boolean dismissShade);
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
     void startActivity(Intent intent, boolean dismissShade, Callback callback);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
index ba4eb5f..88b8dd8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
@@ -52,7 +52,9 @@
     }
 
     class Sensor {
-        public static int TYPE_WAKE_LOCK_SCREEN = 1;
+        public static final int TYPE_WAKE_LOCK_SCREEN = 1;
+        public static final int TYPE_WAKE_DISPLAY = 2;
+        public static final int TYPE_SWIPE = 3;
 
         int mType;
 
@@ -68,6 +70,7 @@
     class TriggerEvent {
         Sensor mSensor;
         int mVendorType;
+        float[] mValues;
 
         /**
          * Creates a trigger event
@@ -76,14 +79,30 @@
          *                   e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
          */
         public TriggerEvent(Sensor sensor, int vendorType) {
+            this(sensor, vendorType, null);
+        }
+
+        /**
+         * Creates a trigger event
+         * @param sensor The type of sensor, e.g. TYPE_WAKE_LOCK_SCREEN
+         * @param vendorType The vendor type, which should be unique for each type of sensor,
+         *                   e.g. SINGLE_TAP = 1, DOUBLE_TAP = 2, etc.
+         * @param values Values captured by the sensor.
+         */
+        public TriggerEvent(Sensor sensor, int vendorType, float[] values) {
             mSensor = sensor;
             mVendorType = vendorType;
+            mValues = values;
         }
 
         public Sensor getSensor() {
             return mSensor;
         }
 
+        public float[] getValues() {
+            return mValues;
+        }
+
         public int getVendorType() {
             return mVendorType;
         }
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index c86ebe7..eb3f70a 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -51,7 +51,7 @@
             android:layout_centerVertical="true"
             android:layout_toEndOf="@id/pkgicon" />
         <TextView
-            android:id="@+id/pkg_group_divider"
+            android:id="@+id/pkg_divider"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -61,7 +61,7 @@
             android:layout_centerVertical="true"
             android:layout_toEndOf="@id/pkgname" />
         <TextView
-            android:id="@+id/group_name"
+            android:id="@+id/delegate_name"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info"
@@ -70,7 +70,7 @@
             android:ellipsize="end"
             android:maxLines="1"
             android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/pkg_group_divider" />
+            android:layout_toEndOf="@id/pkg_divider" />
         <!-- 24 dp icon with 16 dp padding all around to mirror notification content margins -->
         <ImageButton
             android:id="@+id/info"
@@ -101,13 +101,39 @@
             android:layout_marginStart="@*android:dimen/notification_content_margin_start"
             android:layout_marginEnd="@*android:dimen/notification_content_margin_start"
             android:orientation="vertical">
-            <!-- Channel Name -->
-            <TextView
-                android:id="@+id/channel_name"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                style="@android:style/TextAppearance.Material.Notification.Title" />
+            <RelativeLayout
+                android:id="@+id/names"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+                <TextView
+                    android:id="@+id/group_name"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+                    android:layout_marginStart="2dp"
+                    android:layout_marginEnd="2dp"
+                    android:ellipsize="end"
+                    android:maxLines="1"
+                    android:layout_centerVertical="true" />
+                <TextView
+                    android:id="@+id/pkg_group_divider"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+                    android:layout_marginStart="2dp"
+                    android:layout_marginEnd="2dp"
+                    android:text="@*android:string/notification_header_divider_symbol"
+                    android:layout_centerVertical="true"
+                    android:layout_toEndOf="@id/group_name" />
+                <!-- Channel Name -->
+                <TextView
+                    android:id="@+id/channel_name"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    style="@android:style/TextAppearance.Material.Notification.Title"
+                    android:layout_toEndOf="@id/pkg_group_divider"/>
+            </RelativeLayout>
             <!-- Question prompt -->
             <TextView
                 android:id="@+id/block_prompt"
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
index 2f7d486..bc15f2c4 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
@@ -26,9 +26,7 @@
         android:id="@+id/dialog_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:orientation="vertical"
-        android:padding="@dimen/ongoing_appops_dialog_content_padding">
-
+        android:orientation="vertical" >
         <TextView
             android:id="@+id/title"
             android:layout_width="match_parent"
@@ -37,21 +35,29 @@
             android:textDirection="locale"
             android:textAppearance="@style/TextAppearance.AppOpsDialog.Title"
             android:textColor="@*android:color/text_color_primary"
-            android:paddingStart="@dimen/ongoing_appops_dialog_title_padding"
-            android:paddingEnd="@dimen/ongoing_appops_dialog_title_padding"
-            android:paddingBottom="@dimen/ongoing_appops_dialog_sep"
+            android:layout_marginStart="@dimen/ongoing_appops_dialog_title_margin_sides"
+            android:layout_marginEnd="@dimen/ongoing_appops_dialog_title_margin_sides"
+            android:layout_marginBottom="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
+            android:layout_marginTop="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
         />
 
         <LinearLayout
-            android:id="@+id/items_container"
+            android:orientation="vertical"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:gravity="start"
-        />
+            android:layout_marginBottom="@dimen/ongoing_appops_dialog_items_bottom_margin" >
 
-        <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item"
-                 android:visibility="gone" />
+            <LinearLayout
+                android:id="@+id/items_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:gravity="start"
+            />
+
+            <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item"
+                     android:visibility="gone" />
+        </LinearLayout>
 
     </LinearLayout>
 
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
index f05f7ba..ecfbfb4 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
@@ -17,37 +17,39 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="@dimen/ongoing_appops_dialog_line_height"
+    android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
+    android:layout_marginEnd="@dimen/ongoing_appops_dialog_text_padding"
     android:fillViewport="true"
     android:orientation="horizontal"
-    android:layout_marginTop="@dimen/ongoing_appops_dialog_text_margin"
-    android:focusable="true" >
+    android:focusable="true"
+    android:layout_gravity="center_vertical">
 
     <ImageView
         android:id="@+id/app_icon"
-        android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
-        android:layout_width="@dimen/ongoing_appops_dialog_icon_height"
+        android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
+        android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
+        android:layout_gravity="start|center_vertical"
     />
 
     <TextView
         android:id="@+id/app_name"
-        android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+        android:layout_height="match_parent"
         android:layout_width="0dp"
         android:layout_weight="1"
-        android:gravity="bottom|start"
+        android:gravity="start|center_vertical"
         android:textDirection="locale"
         android:textAppearance="@style/TextAppearance.AppOpsDialog.Item"
         android:textColor="@*android:color/text_color_primary"
-        android:paddingStart="@dimen/ongoing_appops_dialog_text_padding"
-        android:paddingEnd="@dimen/ongoing_appops_dialog_text_padding"
-
+        android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
     />
 
     <LinearLayout
         android:id="@+id/icons"
-        android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+        android:layout_height="match_parent"
         android:layout_width="wrap_content"
         android:gravity="end"
+        android:layout_gravity="end|center_vertical"
         android:visibility="gone"
     />
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d8648fa..9e97cd8 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -464,4 +464,6 @@
     <string-array name="config_pluginWhitelist" translatable="false">
         <item>com.android.systemui</item>
     </string-array>
+
+    <integer name="ongoing_appops_dialog_max_apps">5</integer>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0997c5b1..b0a519c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -945,14 +945,20 @@
     <dimen name="ongoing_appops_dialog_sep">16dp</dimen>
     <!--Padding around text items in Ongoing App Ops dialog -->
     <dimen name="ongoing_appops_dialog_text_padding">16dp</dimen>
-    <!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
-    <dimen name="ongoing_appops_dialog_icon_height">28dp</dimen>
-    <!-- Margin between text lines in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
-    <!-- Side padding of title in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_title_padding">10dp</dimen>
-    <!-- Padding around Ongoing App Ops dialog content -->
-    <dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
+    <!-- Height and width of App Opp icons in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_icon_size">24dp</dimen>
+    <!-- Left margin of App Opp icons in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_icon_margin">8dp</dimen>
+    <!-- Height and width of Application icons in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_app_icon_size">32dp</dimen>
+    <!-- Height of line in Ongoing App Ops dialog-->
+    <dimen name="ongoing_appops_dialog_line_height">48dp</dimen>
+    <!-- Side margin of title in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_title_margin_sides">24dp</dimen>
+    <!-- Bottom margin of items in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_items_bottom_margin">24dp</dimen>
+    <!-- Top and bottom margin of title in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_title_margin_top_bottom">18dp</dimen>
     <!-- Side margins around the Ongoing App Ops chip-->
     <dimen name="ongoing_appops_chip_margin">12dp</dimen>
     <!-- Top and bottom margins around the Ongoing App Ops chip -->
@@ -968,9 +974,9 @@
     <!-- Radius of Ongoing App Ops chip corners -->
     <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
     <!-- Text size for Ongoing App Ops dialog title -->
-    <dimen name="ongoing_appops_dialog_title_size">24sp</dimen>
+    <dimen name="ongoing_appops_dialog_title_size">20sp</dimen>
     <!-- Text size for Ongoing App Ops dialog items -->
-    <dimen name="ongoing_appops_dialog_item_size">20sp</dimen>
+    <dimen name="ongoing_appops_dialog_item_size">16sp</dimen>
 
     <!-- How much a bubble is elevated -->
     <dimen name="bubble_elevation">8dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4a0bc9b..c5654f0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1574,6 +1574,9 @@
     <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
     <string name="notification_unblockable_desc">These notifications can\'t be turned off</string>
 
+    <!-- Notification: Control panel: Label for the app that posted this notification, if it's not the package that the notification was posted for -->
+    <string name="notification_delegate_header">via <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+
     <!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
     <string name="appops_camera">This app is using the camera.</string>
     <!-- Notification Inline controls: describes what the app is doing in the background [CHAR_LIMIT=NONE] -->
@@ -2251,7 +2254,10 @@
     <string name="heap_dump_tile_name">Dump SysUI Heap</string>
 
     <!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] -->
-    <string name="ongoing_privacy_chip_multiple_apps"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</string>
+    <plurals name="ongoing_privacy_chip_multiple_apps">
+        <item quantity="few"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
+        <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
+    </plurals>
 
     <!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
     <string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
@@ -2260,12 +2266,15 @@
     <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
 
     <!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]-->
-    <string name="ongoing_privacy_chip_content_multiple_apps_single_op"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</string>
+    <plurals name="ongoing_privacy_chip_content_multiple_apps_single_op">
+        <item quantity="few"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
+        <item quantity="other"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
+    </plurals>
 
     <!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
     <string name="ongoing_privacy_dialog_cancel">Cancel</string>
 
-    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=15]-->
+    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]-->
     <string name="ongoing_privacy_dialog_open_settings">View details</string>
 
     <!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
@@ -2292,6 +2301,6 @@
     <!-- Text for indicating extra apps using app ops [CHAR LIMIT=NONE] -->
     <plurals name="ongoing_privacy_dialog_overflow_text">
         <item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> other app</item>
-        <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other app</item>
+        <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other apps</item>
     </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e9aa1b6..fede934 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -262,7 +262,7 @@
 
     <style name="TextAppearance.AppOpsDialog.Item">
         <item name="android:textSize">@dimen/ongoing_appops_dialog_item_size</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:fontFamily">sans-serif</item>
     </style>
 
     <style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index 70258c2..2aba3fa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -20,7 +20,6 @@
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.view.Surface;
-import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 
 public class TransactionCompat {
@@ -53,7 +52,7 @@
     }
 
     public TransactionCompat setSize(SurfaceControlCompat surfaceControl, int w, int h) {
-        mTransaction.setSize(surfaceControl.mSurfaceControl, w, h);
+        mTransaction.setBufferSize(surfaceControl.mSurfaceControl, w, h);
         return this;
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 42e60aa..ff9e84c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -100,23 +100,23 @@
      * Overrides a pending app transition.
      */
     public void overridePendingAppTransitionMultiThumbFuture(
-            AppTransitionAnimationSpecsFuture animationSpecFuture,
-            Runnable animStartedCallback, Handler animStartedCallbackHandler, boolean scaleUp) {
+            AppTransitionAnimationSpecsFuture animationSpecFuture, Runnable animStartedCallback,
+            Handler animStartedCallbackHandler, boolean scaleUp, int displayId) {
         try {
             WindowManagerGlobal.getWindowManagerService()
                     .overridePendingAppTransitionMultiThumbFuture(animationSpecFuture.getFuture(),
                             RecentsTransition.wrapStartedListener(animStartedCallbackHandler,
-                                    animStartedCallback), scaleUp);
+                                    animStartedCallback), scaleUp, displayId);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ", e);
         }
     }
 
     public void overridePendingAppTransitionRemote(
-            RemoteAnimationAdapterCompat remoteAnimationAdapter) {
+            RemoteAnimationAdapterCompat remoteAnimationAdapter, int displayId) {
         try {
             WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionRemote(
-                    remoteAnimationAdapter.getWrapped());
+                    remoteAnimationAdapter.getWrapped(), displayId);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to override pending app transition (remote): ", e);
         }
@@ -159,11 +159,13 @@
     }
 
     /**
-     * @return whether there is a soft nav bar.
+     * @param displayId the id of display to check if there is a software navigation bar.
+     *
+     * @return whether there is a soft nav bar on specific display.
      */
-    public boolean hasSoftNavigationBar() {
+    public boolean hasSoftNavigationBar(int displayId) {
         try {
-            return WindowManagerGlobal.getWindowManagerService().hasNavigationBar();
+            return WindowManagerGlobal.getWindowManagerService().hasNavigationBar(displayId);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 6b0a7a9..b55aa5c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1531,10 +1531,11 @@
         }
         mHandler.removeCallbacks(mRetryFingerprintAuthentication);
         boolean shouldListenForFingerprint = shouldListenForFingerprint();
-        if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFingerprint) {
+        boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
+                || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
+        if (runningOrRestarting && !shouldListenForFingerprint) {
             stopListeningForFingerprint();
-        } else if (mFingerprintRunningState != BIOMETRIC_STATE_RUNNING
-                && shouldListenForFingerprint) {
+        } else if (!runningOrRestarting && shouldListenForFingerprint) {
             startListeningForFingerprint();
         }
     }
@@ -1589,6 +1590,10 @@
             setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
             return;
         }
+        if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+            // Waiting for restart via handleFingerprintError().
+            return;
+        }
         if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
         int userId = ActivityManager.getCurrentUser();
         if (isUnlockWithFingerprintPossible(userId)) {
@@ -2418,6 +2423,8 @@
                     + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
             pw.println("    disabled(DPM)=" + isFingerprintDisabled(userId));
             pw.println("    possible=" + isUnlockWithFingerprintPossible(userId));
+            pw.println("    listening: actual=" + mFingerprintRunningState
+                    + " expected=" + (shouldListenForFingerprint() ? 1 : 0));
             pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
             pw.println("    trustManaged=" + getUserTrustIsManaged(userId));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index e1b8dc8..9e7c5ba 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -36,6 +36,15 @@
     }
 
     @Override
+    public void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentCallback) {
+        if (mActualStarter == null) {
+            return;
+        }
+        mActualStarter.startPendingIntentDismissingKeyguard(intent, intentSentCallback);
+    }
+
+    @Override
     public void startActivity(Intent intent, boolean dismissShade) {
         if (mActualStarter == null) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e868f96..416cc59 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -23,10 +23,10 @@
 import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP;
 
 import android.app.Notification;
-import android.app.NotificationManager;
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -57,6 +57,11 @@
     // When a bubble is dismissed, recreate it as a notification
     public static final boolean DEBUG_DEMOTE_TO_NOTIF = false;
 
+    // Secure settings
+    private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
+    private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
+    private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
+
     private Context mContext;
     private BubbleDismissListener mDismissListener;
     private BubbleStateChangeListener mStateChangeListener;
@@ -268,7 +273,7 @@
         for (BubbleView bv : mBubbles.values()) {
             NotificationData.Entry entry = bv.getEntry();
             if (entry != null) {
-                if (entry.row.isRemoved() || entry.isBubbleDismissed() || entry.row.isDismissed()) {
+                if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
                     viewsToRemove.add(bv);
                 }
             }
@@ -318,11 +323,15 @@
     /**
      * Whether the notification should bubble or not.
      */
-    public static boolean shouldAutoBubble(NotificationData.Entry entry, int priority,
-            boolean canAppOverlay) {
-        if (!DEBUG_ENABLE_AUTO_BUBBLE || entry.isBubbleDismissed()) {
+    public static boolean shouldAutoBubble(Context context, NotificationData.Entry entry) {
+        if (entry.isBubbleDismissed()) {
             return false;
         }
+
+        boolean autoBubbleMessages = shouldAutoBubbleMessages(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+        boolean autoBubbleOngoing = shouldAutoBubbleOngoing(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+        boolean autoBubbleAll = shouldAutoBubbleAll(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+
         StatusBarNotification n = entry.notification;
         boolean hasRemoteInput = false;
         if (n.getNotification().actions != null) {
@@ -333,12 +342,28 @@
                 }
             }
         }
+
         Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle();
-        boolean shouldBubble = priority >= NotificationManager.IMPORTANCE_HIGH
-                || Notification.MessagingStyle.class.equals(style)
+        boolean isMessageType = Notification.MessagingStyle.class.equals(style)
                 || Notification.CATEGORY_MESSAGE.equals(n.getNotification().category)
-                || hasRemoteInput
-                || canAppOverlay;
-        return shouldBubble && !entry.isBubbleDismissed();
+                || hasRemoteInput;
+        return (isMessageType && autoBubbleMessages)
+                || (n.isOngoing() && autoBubbleOngoing)
+                || autoBubbleAll;
+    }
+
+    private static boolean shouldAutoBubbleMessages(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_AUTO_BUBBLE_MESSAGES, 0) != 0;
+    }
+
+    private static boolean shouldAutoBubbleOngoing(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_AUTO_BUBBLE_ONGOING, 0) != 0;
+    }
+
+    private static boolean shouldAutoBubbleAll(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index a79e047..6c47aac 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -120,7 +120,7 @@
      * @return the view to display when the bubble is expanded.
      */
     public ExpandableNotificationRow getRowView() {
-        return mEntry.row;
+        return mEntry.getRow();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 21b21d9..eda3c59 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -177,9 +177,22 @@
         log("state " + state);
     }
 
-    public static void traceWakeLockScreenWakeUp() {
+    /**
+     * Appends lock screen wake up event to the logs.
+     * @param wake if we're waking up or sleeping.
+     */
+    public static void traceLockScreenWakeUp(boolean wake) {
         if (!ENABLED) return;
-        log("wakeLockScreenWakeUp");
+        log("wakeLockScreenWakeUp " + wake);
+    }
+
+    /**
+     * Appends wake-display event to the logs.
+     * @param wake if we're waking up or sleeping.
+     */
+    public static void traceWakeDisplay(boolean wake) {
+        if (!ENABLED) return;
+        log("wakeLockScreenWakeUp " + wake);
     }
 
     public static void traceProximityResult(Context context, boolean near, long millis,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 01a2345..1dd31010 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -24,20 +24,21 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
-import android.os.Build;
 import android.os.Handler;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
 
 /**
  * Controls the screen brightness when dozing.
  */
 public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part,
         SensorEventListener {
+    private static final boolean DEBUG_AOD_BRIGHTNESS = SystemProperties
+            .getBoolean("debug.aod_brightness", false);
     protected static final String ACTION_AOD_BRIGHTNESS =
             "com.android.systemui.doze.AOD_BRIGHTNESS";
     protected static final String BRIGHTNESS_BUCKET = "brightness_bucket";
@@ -83,11 +84,9 @@
         mSensorToScrimOpacity = sensorToScrimOpacity;
 
         if (mDebuggable) {
-            Dependency.get(Dependency.BG_HANDLER).post(()-> {
-                IntentFilter filter = new IntentFilter();
-                filter.addAction(ACTION_AOD_BRIGHTNESS);
-                mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler);
-            });
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(ACTION_AOD_BRIGHTNESS);
+            mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler);
         }
     }
 
@@ -97,7 +96,7 @@
         this(context, service, sensorManager, lightSensor, host, handler,
                 context.getResources().getInteger(
                         com.android.internal.R.integer.config_screenBrightnessDoze),
-                policy.screenBrightnessArray, policy.dimmingScrimArray, Build.IS_DEBUGGABLE);
+                policy.screenBrightnessArray, policy.dimmingScrimArray, DEBUG_AOD_BRIGHTNESS);
     }
 
     @Override
@@ -126,9 +125,7 @@
     private void onDestroy() {
         setLightSensorEnabled(false);
         if (mDebuggable) {
-            Dependency.get(Dependency.BG_HANDLER).post(()-> {
-                mContext.unregisterReceiver(this);
-            });
+            mContext.unregisterReceiver(this);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 77f7ad4f..bf8e04d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.doze;
 
+import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY;
 import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
 
 import android.annotation.AnyThread;
@@ -67,7 +68,6 @@
     private final AmbientDisplayConfiguration mConfig;
     private final WakeLock mWakeLock;
     private final Consumer<Boolean> mProxCallback;
-    private final Consumer<Boolean> mWakeScreenCallback;
     private final Callback mCallback;
 
     private final Handler mHandler = new Handler();
@@ -76,8 +76,7 @@
 
     public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
             DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
-            Callback callback, Consumer<Boolean> proxCallback,
-            Consumer<Boolean> wakeScreenCallback, AlwaysOnDisplayPolicy policy) {
+            Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
         mContext = context;
         mAlarmManager = alarmManager;
         mSensorManager = sensorManager;
@@ -85,7 +84,6 @@
         mConfig = config;
         mWakeLock = wakeLock;
         mProxCallback = proxCallback;
-        mWakeScreenCallback = wakeScreenCallback;
         mResolver = mContext.getContentResolver();
 
         mSensors = new TriggerSensor[] {
@@ -123,7 +121,13 @@
                         DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
                         false /* reports touch coordinates */,
                         false /* touchscreen */),
-                new WakeScreenSensor(),
+                new PluginTriggerSensor(
+                        new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
+                        Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
+                        true /* configured */,
+                        DozeLog.REASON_SENSOR_WAKE_UP,
+                        false /* reports touch coordinates */,
+                        false /* touchscreen */),
         };
 
         mProxSensor = new ProxSensor(policy);
@@ -395,7 +399,8 @@
                     screenX = event.values[0];
                     screenY = event.values[1];
                 }
-                mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY);
+                mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY,
+                        event.values);
                 updateListener();  // reregister, this sensor only fires once
             }));
         }
@@ -429,7 +434,14 @@
 
         private final SensorManagerPlugin.Sensor mPluginSensor;
         private final SensorManagerPlugin.TriggerEventListener mTriggerEventListener = (event) -> {
-            onTrigger(null);
+            DozeLog.traceSensor(mContext, mPulseReason);
+            mHandler.post(mWakeLock.wrap(() -> {
+                if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
+                mRegistered = false;
+                mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
+                        event.getValues());
+                updateListener();  // reregister, this sensor only fires once
+            }));
         };
 
         PluginTriggerSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
@@ -463,28 +475,19 @@
                     .append(", mSensor=").append(mPluginSensor).append("}").toString();
         }
 
-    }
-
-    private class WakeScreenSensor extends TriggerSensor {
-
-        WakeScreenSensor() {
-            super(findSensorWithType(mConfig.wakeScreenSensorType()),
-                    Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, true /* configured */,
-                    DozeLog.REASON_SENSOR_WAKE_UP, false /* reportsTouchCoordinates */,
-                    false /* requiresTouchscreen */);
+        private String triggerEventToString(SensorManagerPlugin.TriggerEvent event) {
+            if (event == null) return null;
+            final StringBuilder sb = new StringBuilder("PluginTriggerEvent[")
+                    .append(event.getSensor()).append(',')
+                    .append(event.getVendorType());
+            if (event.getValues() != null) {
+                for (int i = 0; i < event.getValues().length; i++) {
+                    sb.append(',').append(event.getValues()[i]);
+                }
+            }
+            return sb.append(']').toString();
         }
 
-        @Override
-        @AnyThread
-        public void onTrigger(TriggerEvent event) {
-            DozeLog.traceSensor(mContext, mPulseReason);
-            mHandler.post(mWakeLock.wrap(() -> {
-                if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
-                mRegistered = false;
-                mWakeScreenCallback.accept(event.values[0] > 0);
-                updateListener();  // reregister, this sensor only fires once
-            }));
-        }
     }
 
     public interface Callback {
@@ -494,11 +497,11 @@
          * @param pulseReason Requesting sensor, e.g. {@link DozeLog#PULSE_REASON_SENSOR_PICKUP}
          * @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity.
          * @param screenX the location on the screen where the sensor fired or -1
-         *                if the sensor doesn't support reporting screen locations.
+ *                if the sensor doesn't support reporting screen locations.
          * @param screenY the location on the screen where the sensor fired or -1
-         *                if the sensor doesn't support reporting screen locations.
+         * @param rawValues raw values array from the event.
          */
         void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck,
-                float screenX, float screenY);
+                float screenX, float screenY, float[] rawValues);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index d69b1bf..bad0148 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -84,7 +84,7 @@
         mWakeLock = wakeLock;
         mAllowPulseTriggers = allowPulseTriggers;
         mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
-                config, wakeLock, this::onSensor, this::onProximityFar, this::onWakeScreen,
+                config, wakeLock, this::onSensor, this::onProximityFar,
                 dozeParameters.getPolicy());
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
     }
@@ -124,13 +124,17 @@
     }
 
     private void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
-            float screenX, float screenY) {
+            float screenX, float screenY, float[] rawValues) {
         boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
         boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
         boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+        boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
+        boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
 
-        if (isLongPress) {
+        if (isWakeDisplay) {
+            onWakeScreen(wakeEvent);
+        } else if (isLongPress) {
             requestPulse(pulseReason, sensorPerformedProxCheck);
         } else {
             proximityCheckThenCall((result) -> {
@@ -141,7 +145,15 @@
                 if (isDoubleTap) {
                     mDozeHost.onDoubleTap(screenX, screenY);
                     mMachine.wakeUp();
-                } else if (isPickup || isWakeLockScreen) {
+                } else if (isWakeLockScreen) {
+                    if (wakeEvent) {
+                        mDozeHost.setPassiveInterrupt(true);
+                        mMachine.wakeUp();
+                        DozeLog.traceLockScreenWakeUp(wakeEvent);
+                    } else {
+                        if (DEBUG) Log.d(TAG, "Unpulsing");
+                    }
+                } else if (isPickup) {
                     mDozeHost.setPassiveInterrupt(true);
                     mMachine.wakeUp();
                 } else {
@@ -157,8 +169,6 @@
             final boolean withinVibrationThreshold =
                     timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
             DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold);
-        } else if (isWakeLockScreen) {
-            DozeLog.traceWakeLockScreenWakeUp();
         }
     }
 
@@ -184,6 +194,7 @@
     }
 
     private void onWakeScreen(boolean wake) {
+        DozeLog.traceWakeDisplay(wake);
         DozeMachine.State state = mMachine.getState();
         boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
         boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index e78951a..201c7e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager;
 import android.app.AlarmManager;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -175,7 +174,7 @@
      * @param builder The slice builder.
      */
     protected void addZenMode(ListBuilder builder) {
-        if (!isDndSuppressingNotifications()) {
+        if (!isDndOn()) {
             return;
         }
         RowBuilder dndBuilder = new RowBuilder(mDndUri)
@@ -187,13 +186,10 @@
     }
 
     /**
-     * Return true if DND is enabled suppressing notifications.
+     * Return true if DND is enabled.
      */
-    protected boolean isDndSuppressingNotifications() {
-        boolean suppressingNotifications = (mZenModeController.getConfig().suppressedVisualEffects
-                & NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
-        return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF
-                && suppressingNotifications;
+    protected boolean isDndOn() {
+        return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF;
     }
 
     @Override
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 e447def..8495fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -19,6 +19,8 @@
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
 
 import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS;
 import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT;
@@ -65,6 +67,7 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.WindowManager.LayoutParams;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -90,8 +93,8 @@
     public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
     public static final int MESSAGE_ANIMATION_ENDED = 6;
 
-    private static final long INITIAL_DISMISS_DELAY = 3500;
-    private static final long POST_INTERACTION_DISMISS_DELAY = 2000;
+    private static final int INITIAL_DISMISS_DELAY = 3500;
+    private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
     private static final long MENU_FADE_DURATION = 125;
 
     private static final float MENU_BACKGROUND_ALPHA = 0.3f;
@@ -105,6 +108,7 @@
 
     private final List<RemoteAction> mActions = new ArrayList<>();
 
+    private AccessibilityManager mAccessibilityManager;
     private View mViewRoot;
     private Drawable mBackgroundDrawable;
     private View mMenuContainer;
@@ -194,6 +198,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.pip_menu_activity);
 
+        mAccessibilityManager = getSystemService(AccessibilityManager.class);
         mBackgroundDrawable = new ColorDrawable(Color.BLACK);
         mBackgroundDrawable.setAlpha(0);
         mViewRoot = findViewById(R.id.background);
@@ -639,8 +644,10 @@
         mHandler.removeCallbacks(mFinishRunnable);
     }
 
-    private void repostDelayedFinish(long delay) {
+    private void repostDelayedFinish(int delay) {
+        int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
+                FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
         mHandler.removeCallbacks(mFinishRunnable);
-        mHandler.postDelayed(mFinishRunnable, delay);
+        mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 03a573e..6a9f24c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -368,7 +368,7 @@
 
     private void onAccessibilityShowMenu() {
         mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
-                mMovementBounds, false /* allowMenuTimeout */, willResizeMenu());
+                mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
     }
 
     private boolean handleTouchEvent(MotionEvent ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 5d33ffd..f054345 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -411,13 +411,8 @@
 
         setNextLogTime();
 
-        // This initialization method may be called on a configuration change. Only one set of
-        // ongoing callbacks should be occurring, so remove any now. updateTemperatureWarning will
-        // schedule an ongoing callback.
-        mHandler.removeCallbacks(mUpdateTempCallback);
-
         // We have passed all of the checks, start checking the temp
-        updateTemperatureWarning();
+        mHandler.post(mUpdateTempCallback);
     }
 
     private void showThermalShutdownDialog() {
@@ -448,6 +443,8 @@
 
         logTemperatureStats();
 
+        // Remove any pending callbacks as we only want to enable one
+        mHandler.removeCallbacks(mUpdateTempCallback);
         mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
     }
 
@@ -553,11 +550,7 @@
     // Thermal event received from vendor thermal management subsystem
     private final class ThermalEventListener extends IThermalEventListener.Stub {
         @Override public void notifyThrottling(Temperature temp) {
-            // Trigger an update of the temperature warning.  Only one
-            // callback can be enabled at a time, so remove any existing
-            // callback; updateTemperatureWarning will schedule another one.
-            mHandler.removeCallbacks(mUpdateTempCallback);
-            updateTemperatureWarning();
+            mHandler.post(mUpdateTempCallback);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index d3715d0..65ed889 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -78,8 +78,9 @@
                 if (builder.app != null) {
                     text.setText(builder.app?.applicationName)
                 } else {
-                    text.text = context.getString(R.string.ongoing_privacy_chip_multiple_apps,
-                            builder.appsAndTypes.size)
+                    text.text = context.resources.getQuantityString(
+                            R.plurals.ongoing_privacy_chip_multiple_apps,
+                            builder.appsAndTypes.size, builder.appsAndTypes.size)
                 }
             }
         } else {
@@ -100,9 +101,9 @@
                         context.getString(R.string.ongoing_privacy_chip_content_single_app,
                                 builder.app?.applicationName, typesText)
             } else {
-                contentDescription = context.getString(
-                        R.string.ongoing_privacy_chip_content_multiple_apps_single_op,
-                        builder.appsAndTypes.size, typesText)
+                contentDescription = context.resources.getQuantityString(
+                        R.plurals.ongoing_privacy_chip_content_multiple_apps_single_op,
+                        builder.appsAndTypes.size, builder.appsAndTypes.size, typesText)
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index f6a95af..bbdae29 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -20,6 +20,7 @@
 import android.content.DialogInterface
 import android.content.Intent
 import android.content.res.ColorStateList
+import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.View
 import android.widget.ImageView
@@ -34,13 +35,13 @@
     val dialogBuilder: PrivacyDialogBuilder
 ) {
 
-    val iconSize = context.resources.getDimensionPixelSize(
-            R.dimen.ongoing_appops_dialog_icon_height)
-    val iconColor = context.resources.getColor(
+    private val iconSize = context.resources.getDimensionPixelSize(
+            R.dimen.ongoing_appops_dialog_icon_size)
+    private val iconColor = context.resources.getColor(
             com.android.internal.R.color.text_color_primary, context.theme)
-    companion object {
-        private const val MAX_ITEMS = 10
-    }
+    private val iconMargin = context.resources.getDimensionPixelSize(
+            R.dimen.ongoing_appops_dialog_icon_margin)
+    private val MAX_ITEMS = context.resources.getInteger(R.integer.ongoing_appops_dialog_max_apps)
 
     fun createDialog(): Dialog {
         val builder = AlertDialog.Builder(context).apply {
@@ -105,6 +106,11 @@
         val appName = item.findViewById(R.id.app_name) as TextView
         val icons = item.findViewById(R.id.icons) as LinearLayout
 
+        var lp = LinearLayout.LayoutParams(iconSize, iconSize).apply {
+            gravity = Gravity.CENTER_VERTICAL
+            marginStart = iconMargin
+        }
+
         app.icon?.let {
             appIcon.setImageDrawable(it)
         }
@@ -117,7 +123,7 @@
                     imageTintList = ColorStateList.valueOf(iconColor)
                     setImageDrawable(it)
                 }
-                icons.addView(image, iconSize, LinearLayout.LayoutParams.WRAP_CONTENT)
+                icons.addView(image, lp)
             }
             icons.visibility = View.VISIBLE
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 3fa3e8e..268462e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -18,10 +18,14 @@
 
 import android.app.ActivityManager
 import android.app.AppOpsManager
+import android.content.BroadcastReceiver
 import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
 import android.os.Handler
 import android.os.UserHandle
 import android.os.UserManager
+import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.Dependency
 import com.android.systemui.appops.AppOpItem
 import com.android.systemui.appops.AppOpsController
@@ -33,25 +37,29 @@
                 AppOpsManager.OP_RECORD_AUDIO,
                 AppOpsManager.OP_COARSE_LOCATION,
                 AppOpsManager.OP_FINE_LOCATION)
+        val intents = listOf(Intent.ACTION_USER_FOREGROUND,
+                Intent.ACTION_MANAGED_PROFILE_ADDED,
+                Intent.ACTION_MANAGED_PROFILE_REMOVED)
+        const val TAG = "PrivacyItemController"
     }
 
     private var privacyList = emptyList<PrivacyItem>()
     private val appOpsController = Dependency.get(AppOpsController::class.java)
     private val userManager = context.getSystemService(UserManager::class.java)
-    private val currentUser = ActivityManager.getCurrentUser()
-    private val currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+    private var currentUserIds = emptyList<Int>()
     private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
     private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
+    private var listening = false
+
     private val notifyChanges = Runnable {
         callback.privacyChanged(privacyList)
     }
+
     private val updateListAndNotifyChanges = Runnable {
         updatePrivacyList()
         uiHandler.post(notifyChanges)
     }
 
-    private var listening = false
-
     private val cb = object : AppOpsController.Callback {
         override fun onActiveStateChanged(
             code: Int,
@@ -61,12 +69,36 @@
         ) {
             val userId = UserHandle.getUserId(uid)
             if (userId in currentUserIds) {
-                update()
+                update(false)
             }
         }
     }
 
-    private fun update() {
+    @VisibleForTesting
+    internal var userSwitcherReceiver = Receiver()
+        set(value) {
+            context.unregisterReceiver(field)
+            field = value
+            registerReceiver()
+        }
+
+    init {
+        registerReceiver()
+    }
+
+    private fun registerReceiver() {
+        context.registerReceiverAsUser(userSwitcherReceiver, UserHandle.ALL, IntentFilter().apply {
+            intents.forEach {
+                addAction(it)
+            }
+        }, null, null)
+    }
+
+    private fun update(updateUsers: Boolean) {
+        if (updateUsers) {
+            val currentUser = ActivityManager.getCurrentUser()
+            currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+        }
         bgHandler.post(updateListAndNotifyChanges)
     }
 
@@ -75,7 +107,7 @@
         listening = listen
         if (listening) {
             appOpsController.addCallback(OPS, cb)
-            update()
+            update(true)
         } else {
             appOpsController.removeCallback(OPS, cb)
         }
@@ -102,4 +134,12 @@
     interface Callback {
         fun privacyChanged(privacyItems: List<PrivacyItem>)
     }
+
+    internal inner class Receiver : BroadcastReceiver() {
+        override fun onReceive(context: Context?, intent: Intent?) {
+            if (intent?.action in intents) {
+                update(true)
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 216b940..f796793 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -219,7 +219,7 @@
             mLayout.findViewById(R.id.screen_pinning_text_area)
                     .setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
             View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
-            if (WindowManagerWrapper.getInstance().hasSoftNavigationBar()) {
+            if (WindowManagerWrapper.getInstance().hasSoftNavigationBar(mContext.getDisplayId())) {
                 buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
                 swapChildrenIfRtlAndVertical(buttons);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 3da6d2e..bc38169 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -112,8 +112,7 @@
             return;
         }
 
-        alertEntry.mEntry.row.sendAccessibilityEvent(
-                AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        alertEntry.mEntry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         if (alert) {
             alertEntry.updateEntry(true /* updatePostTime */);
         }
@@ -186,7 +185,7 @@
         alertEntry.setEntry(entry);
         mAlertEntries.put(entry.key, alertEntry);
         onAlertEntryAdded(alertEntry);
-        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
     }
 
     /**
@@ -207,7 +206,7 @@
         Entry entry = alertEntry.mEntry;
         mAlertEntries.remove(key);
         onAlertEntryRemoved(alertEntry);
-        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         alertEntry.reset();
         if (mExtendedLifetimeAlertEntries.contains(entry)) {
             if (mNotificationLifetimeFinishedCallback != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index f1c0304..a5e7f04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -79,7 +79,7 @@
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setAmbientPulsing(true);
+        entry.setAmbientPulsing(true);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, true);
         }
@@ -88,11 +88,11 @@
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setAmbientPulsing(false);
+        entry.setAmbientPulsing(false);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, false);
         }
-        entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+        entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
index 3b611a31..2f26109 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
@@ -22,8 +22,11 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.Display;
+import android.view.IWindowManager;
 import android.view.View;
 import android.view.WindowManagerGlobal;
 
@@ -34,6 +37,8 @@
  */
 public class DisplayNavigationBarController implements DisplayListener {
 
+    private static final String TAG = DisplayNavigationBarController.class.getName();
+
     private final Context mContext;
     private final Handler mHandler;
     private final DisplayManager mDisplayManager;
@@ -112,14 +117,23 @@
         }
 
         final int displayId = display.getDisplayId();
+        final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+
+        try {
+            if (!wms.hasNavigationBar(displayId)) {
+                return;
+            }
+        } catch (RemoteException e) {
+            // Cannot get wms, just return with warning message.
+            Log.w(TAG, "Cannot get WindowManager.");
+            return;
+        }
         final Context externalDisplayContext = mContext.createDisplayContext(display);
-        NavigationBarFragment.create(externalDisplayContext,
-                (tag, fragment) -> {
-                    final NavigationBarFragment navBar = (NavigationBarFragment) fragment;
-                    // TODO(b/115978725): handle external nav bars sysuiVisibility
-                    navBar.setCurrentSysuiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
-                    mExternalNavigationBarMap.append(displayId, navBar);
-                }
-        );
+        NavigationBarFragment.create(externalDisplayContext, (tag, fragment) -> {
+            final NavigationBarFragment navBar = (NavigationBarFragment) fragment;
+            // TODO(b/115978725): handle external nav bars sysuiVisibility
+            navBar.setCurrentSysuiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+            mExternalNavigationBarMap.append(displayId, navBar);
+        });
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 745b2f9..b9684fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -27,7 +27,6 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
 
 public class EmptyShadeView extends StackScrollerDecorView {
 
@@ -74,7 +73,7 @@
     }
 
     @Override
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+    public ExpandableViewState createExpandableViewState() {
         return new EmptyShadeViewState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index fc1e94a..f045548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -199,7 +199,7 @@
             for (int i = 0; i < N; i++) {
                 final NotificationData.Entry entry = activeNotifications.get(i);
 
-                if (isMediaNotification(entry)) {
+                if (entry.isMediaNotification()) {
                     final MediaSession.Token token =
                             entry.notification.getNotification().extras.getParcelable(
                                     Notification.EXTRA_MEDIA_SESSION);
@@ -336,13 +336,6 @@
         return PlaybackState.STATE_NONE;
     }
 
-    private boolean isMediaNotification(NotificationData.Entry entry) {
-        // TODO: confirm that there's a valid media key
-        return entry.row.getExpandedContentView() != null
-                && entry.row.getExpandedContentView().findViewById(
-                        com.android.internal.R.id.media_actions) != null;
-    }
-
     private void clearCurrentMediaNotificationSession() {
         mMediaMetadata = null;
         if (mMediaController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 8c53cc2..7be5461 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -150,34 +150,42 @@
         }
 
         private void logActionClick(View view) {
+            Integer actionIndex = (Integer)
+                    view.getTag(com.android.internal.R.id.notification_action_index_tag);
+            if (actionIndex == null) {
+                Log.e(TAG, "Couldn't retrieve the actionIndex from the clicked button");
+                return;
+            }
             ViewParent parent = view.getParent();
-            String key = getNotificationKeyForParent(parent);
-            if (key == null) {
+            StatusBarNotification statusBarNotification = getNotificationForParent(parent);
+            if (statusBarNotification == null) {
                 Log.w(TAG, "Couldn't determine notification for click.");
                 return;
             }
-            int index = -1;
+            String key = statusBarNotification.getKey();
+            int buttonIndex = -1;
             // If this is a default template, determine the index of the button.
             if (view.getId() == com.android.internal.R.id.action0 &&
                     parent != null && parent instanceof ViewGroup) {
                 ViewGroup actionGroup = (ViewGroup) parent;
-                index = actionGroup.indexOfChild(view);
+                buttonIndex = actionGroup.indexOfChild(view);
             }
             final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
             final int rank = mEntryManager.getNotificationData().getRank(key);
+            final Notification.Action action =
+                    statusBarNotification.getNotification().actions[actionIndex];
             final NotificationVisibility nv = NotificationVisibility.obtain(key, rank, count, true);
             try {
-                mBarService.onNotificationActionClick(key, index, nv);
+                mBarService.onNotificationActionClick(key, buttonIndex, action, nv, false);
             } catch (RemoteException e) {
                 // Ignore
             }
         }
 
-        private String getNotificationKeyForParent(ViewParent parent) {
+        private StatusBarNotification getNotificationForParent(ViewParent parent) {
             while (parent != null) {
                 if (parent instanceof ExpandableNotificationRow) {
-                    return ((ExpandableNotificationRow) parent)
-                            .getStatusBarNotification().getKey();
+                    return ((ExpandableNotificationRow) parent).getStatusBarNotification();
                 }
                 parent = parent.getParent();
             }
@@ -393,7 +401,7 @@
     }
 
     public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) {
-        if (entry.row == null || entry.row.isDismissed()) {
+        if (entry.isDismissed()) {
             return false;
         }
         if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -403,7 +411,7 @@
     }
 
     public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) {
-        if (entry.row == null || entry.row.isDismissed()) {
+        if (entry.isDismissed()) {
             return false;
         }
         if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -532,7 +540,7 @@
 
                 // Ensure the entry hasn't already been removed. This can happen if there is an
                 // inflation exception while updating the remote history
-                if (entry.row == null || entry.row.isRemoved()) {
+                if (entry.isRemoved()) {
                     return;
                 }
 
@@ -570,7 +578,7 @@
 
                 mEntryManager.updateNotification(newSbn, null);
 
-                if (entry.row == null || entry.row.isRemoved()) {
+                if (entry.isRemoved()) {
                     return;
                 }
 
@@ -593,7 +601,7 @@
     protected class RemoteInputActiveExtender extends RemoteInputExtender {
         @Override
         public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
-            if (entry.row == null || entry.row.isDismissed()) {
+            if (entry.isDismissed()) {
                 return false;
             }
             return mRemoteInputController.isRemoteInputActive(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 37cc299..e7b4904 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -47,7 +47,6 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
 import com.android.systemui.statusbar.notification.stack.ViewState;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
 
@@ -68,7 +67,6 @@
 
     private boolean mDark;
     private NotificationIconContainer mShelfIcons;
-    private ShelfState mShelfState;
     private int[] mTmp = new int[2];
     private boolean mHideBackground;
     private int mIconAppearTopPadding;
@@ -115,7 +113,6 @@
         setClipChildren(false);
         setClipToPadding(false);
         mShelfIcons.setIsStaticLayout(false);
-        mShelfState = new ShelfState();
         setBottomRoundness(1.0f, false /* animate */);
         initDimens();
     }
@@ -187,52 +184,53 @@
     }
 
     @Override
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
-        return mShelfState;
+    public ExpandableViewState createExpandableViewState() {
+        return new ShelfState();
     }
 
-    public void updateState(StackScrollState resultState,
-            AmbientState ambientState) {
-        View lastView = ambientState.getLastVisibleBackgroundChild();
+    /** Update the state of the shelf. */
+    public void updateState(AmbientState ambientState) {
+        ExpandableView lastView = ambientState.getLastVisibleBackgroundChild();
+        ShelfState viewState = (ShelfState) getViewState();
         if (mShowNotificationShelf && lastView != null) {
             float maxShelfEnd = ambientState.getInnerHeight() + ambientState.getTopPadding()
                     + ambientState.getStackTranslation();
-            ExpandableViewState lastViewState = resultState.getViewStateForView(lastView);
+            ExpandableViewState lastViewState = lastView.getViewState();
             float viewEnd = lastViewState.yTranslation + lastViewState.height;
-            mShelfState.copyFrom(lastViewState);
-            mShelfState.height = getIntrinsicHeight();
+            viewState.copyFrom(lastViewState);
+            viewState.height = getIntrinsicHeight();
 
-            float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
+            float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height,
                     getFullyClosedTranslation());
             float darkTranslation = mAmbientState.getDarkTopPadding();
             float yRatio = mAmbientState.hasPulsingNotifications() ?
                     0 : mAmbientState.getDarkAmount();
-            mShelfState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
-            mShelfState.zTranslation = ambientState.getBaseZHeight();
+            viewState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
+            viewState.zTranslation = ambientState.getBaseZHeight();
             // For the small display size, it's not enough to make the icon not covered by
             // the top cutout so the denominator add the height of cutout.
             // Totally, (getIntrinsicHeight() * 2 + mCutoutHeight) should be smaller then
             // mAmbientState.getTopPadding().
-            float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
+            float openedAmount = (viewState.yTranslation - getFullyClosedTranslation())
                     / (getIntrinsicHeight() * 2 + mCutoutHeight);
             openedAmount = Math.min(1.0f, openedAmount);
-            mShelfState.openedAmount = openedAmount;
-            mShelfState.clipTopAmount = 0;
-            mShelfState.alpha = mAmbientState.hasPulsingNotifications() ? 0 : 1;
-            mShelfState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
-            mShelfState.hideSensitive = false;
-            mShelfState.xTranslation = getTranslationX();
+            viewState.openedAmount = openedAmount;
+            viewState.clipTopAmount = 0;
+            viewState.alpha = mAmbientState.hasPulsingNotifications() ? 0 : 1;
+            viewState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
+            viewState.hideSensitive = false;
+            viewState.xTranslation = getTranslationX();
             if (mNotGoneIndex != -1) {
-                mShelfState.notGoneIndex = Math.min(mShelfState.notGoneIndex, mNotGoneIndex);
+                viewState.notGoneIndex = Math.min(viewState.notGoneIndex, mNotGoneIndex);
             }
-            mShelfState.hasItemsInStableShelf = lastViewState.inShelf;
-            mShelfState.hidden = !mAmbientState.isShadeExpanded()
+            viewState.hasItemsInStableShelf = lastViewState.inShelf;
+            viewState.hidden = !mAmbientState.isShadeExpanded()
                     || mAmbientState.isQsCustomizerShowing();
-            mShelfState.maxShelfEnd = maxShelfEnd;
+            viewState.maxShelfEnd = maxShelfEnd;
         } else {
-            mShelfState.hidden = true;
-            mShelfState.location = ExpandableViewState.LOCATION_GONE;
-            mShelfState.hasItemsInStableShelf = false;
+            viewState.hidden = true;
+            viewState.location = ExpandableViewState.LOCATION_GONE;
+            viewState.hasItemsInStableShelf = false;
         }
     }
 
@@ -261,7 +259,7 @@
         int notGoneIndex = 0;
         int colorOfViewBeforeLast = NO_COLOR;
         boolean backgroundForceHidden = false;
-        if (mHideBackground && !mShelfState.hasItemsInStableShelf) {
+        if (mHideBackground && !((ShelfState) getViewState()).hasItemsInStableShelf) {
             backgroundForceHidden = true;
         }
         int colorTwoBefore = NO_COLOR;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index ea67736..daa2fd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -51,6 +51,7 @@
 public class NotificationViewHierarchyManager {
     private static final String TAG = "NotificationViewHierarchyManager";
 
+    //TODO: change this top <Entry, List<Entry>>?
     private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
             mTmpChildOrderMap = new HashMap<>();
 
@@ -140,6 +141,7 @@
     /**
      * Updates the visual representation of the notifications.
      */
+    //TODO: Rewrite this to focus on Entries, or some other data object instead of views
     public void updateNotificationViews() {
         ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
                 .getActiveNotifications();
@@ -148,12 +150,12 @@
         final int N = activeNotifications.size();
         for (int i = 0; i < N; i++) {
             NotificationData.Entry ent = activeNotifications.get(i);
-            if (ent.row.isDismissed() || ent.row.isRemoved()) {
+            if (ent.isRowDismissed() || ent.isRowRemoved()) {
                 // we don't want to update removed notifications because they could
                 // temporarily become children if they were isolated before.
                 continue;
             }
-            ent.row.setStatusBarState(mStatusBarStateListener.getCurrentState());
+            ent.getRow().setStatusBarState(mStatusBarStateListener.getCurrentState());
             boolean showAsBubble = ent.isBubble() && !ent.isBubbleDismissed()
                     && mStatusBarStateListener.getCurrentState() == SHADE;
             if (showAsBubble) {
@@ -175,20 +177,19 @@
             boolean deviceSensitive = devicePublic
                     && !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
                     mLockscreenUserManager.getCurrentUserId());
-            ent.row.setSensitive(sensitive, deviceSensitive);
-            ent.row.setNeedsRedaction(needsRedaction);
-            if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
-                ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
-                        ent.row.getStatusBarNotification());
+            ent.getRow().setSensitive(sensitive, deviceSensitive);
+            ent.getRow().setNeedsRedaction(needsRedaction);
+            if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
+                NotificationData.Entry summary = mGroupManager.getGroupSummary(ent.notification);
                 List<ExpandableNotificationRow> orderedChildren =
-                        mTmpChildOrderMap.get(summary);
+                        mTmpChildOrderMap.get(summary.getRow());
                 if (orderedChildren == null) {
                     orderedChildren = new ArrayList<>();
-                    mTmpChildOrderMap.put(summary, orderedChildren);
+                    mTmpChildOrderMap.put(summary.getRow(), orderedChildren);
                 }
-                orderedChildren.add(ent.row);
+                orderedChildren.add(ent.getRow());
             } else {
-                toShow.add(ent.row);
+                toShow.add(ent.getRow());
             }
 
         }
@@ -391,19 +392,19 @@
                         && !row.isLowPriority()));
             }
 
-            entry.row.setOnAmbient(getShadeController().isDozing());
+            entry.getRow().setOnAmbient(getShadeController().isDozing());
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
-                    entry.notification) && !entry.row.isRemoved();
+                    entry.notification) && !entry.isRowRemoved();
             boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
                     .notification);
             if (!showOnKeyguard) {
                 // min priority notifications should show if their summary is showing
                 if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
-                    ExpandableNotificationRow summary = mGroupManager.getLogicalGroupSummary(
+                    NotificationData.Entry summary = mGroupManager.getLogicalGroupSummary(
                             entry.notification);
                     if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
-                            summary.getStatusBarNotification()))         {
+                            summary.notification))         {
                         showOnKeyguard = true;
                     }
                 }
@@ -411,16 +412,16 @@
             if (suppressedSummary
                     || mLockscreenUserManager.shouldHideNotifications(userId)
                     || (onKeyguard && !showOnKeyguard)) {
-                entry.row.setVisibility(View.GONE);
+                entry.getRow().setVisibility(View.GONE);
             } else {
-                boolean wasGone = entry.row.getVisibility() == View.GONE;
+                boolean wasGone = entry.getRow().getVisibility() == View.GONE;
                 if (wasGone) {
-                    entry.row.setVisibility(View.VISIBLE);
+                    entry.getRow().setVisibility(View.VISIBLE);
                 }
-                if (!isChildNotification && !entry.row.isRemoved()) {
+                if (!isChildNotification && !entry.getRow().isRemoved()) {
                     if (wasGone) {
                         // notify the scroller of a child addition
-                        mListContainer.generateAddAnimation(entry.row,
+                        mListContainer.generateAddAnimation(entry.getRow(),
                                 !showOnKeyguard /* fromMoreCard */);
                     }
                     visibleNotifications++;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index 929f43e..e8abcc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -250,16 +250,16 @@
         // Make a copy because closing the remote inputs will modify mOpen.
         ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size());
         for (int i = mOpen.size() - 1; i >= 0; i--) {
-            NotificationData.Entry item = mOpen.get(i).first.get();
-            if (item != null && item.row != null) {
-                list.add(item);
+            NotificationData.Entry entry = mOpen.get(i).first.get();
+            if (entry != null && entry.rowExists()) {
+                list.add(entry);
             }
         }
 
         for (int i = list.size() - 1; i >= 0; i--) {
-            NotificationData.Entry item = list.get(i);
-            if (item.row != null) {
-                item.row.closeRemoteInput();
+            NotificationData.Entry entry = list.get(i);
+            if (entry.rowExists()) {
+                entry.closeRemoteInput();
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 758c33a..f5d6904 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -15,12 +15,15 @@
  */
 package com.android.systemui.statusbar;
 
+import android.app.Notification;
 import android.os.RemoteException;
 import android.util.ArraySet;
 
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
 import java.util.Set;
 
@@ -32,6 +35,9 @@
     private IStatusBarService mBarService;
     private Set<String> mSendingKeys = new ArraySet<>();
     private Callback mCallback;
+    private final NotificationEntryManager mEntryManager =
+            Dependency.get(NotificationEntryManager.class);
+
 
     public SmartReplyController() {
         mBarService = Dependency.get(IStatusBarService.class);
@@ -41,12 +47,34 @@
         mCallback = callback;
     }
 
-    public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply) {
+    /**
+     * Notifies StatusBarService a smart reply is sent.
+     */
+    public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply,
+            boolean generatedByAssistant) {
         mCallback.onSmartReplySent(entry, reply);
         mSendingKeys.add(entry.key);
         try {
-            mBarService.onNotificationSmartReplySent(entry.notification.getKey(),
-                    replyIndex);
+            mBarService.onNotificationSmartReplySent(
+                    entry.notification.getKey(), replyIndex, reply, generatedByAssistant);
+        } catch (RemoteException e) {
+            // Nothing to do, system going down
+        }
+    }
+
+    /**
+     * Notifies StatusBarService a smart action is clicked.
+     */
+    public void smartActionClicked(
+            NotificationData.Entry entry, int actionIndex, Notification.Action action,
+            boolean generatedByAssistant) {
+        final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
+        final int rank = mEntryManager.getNotificationData().getRank(entry.key);
+        final NotificationVisibility nv =
+                NotificationVisibility.obtain(entry.key, rank, count, true);
+        try {
+            mBarService.onNotificationActionClick(
+                    entry.key, actionIndex, action, nv, generatedByAssistant);
         } catch (RemoteException e) {
             // Nothing to do, system going down
         }
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 da6d977..d7680b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -64,6 +64,9 @@
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -106,7 +109,6 @@
         public int importance;
         public StatusBarIconView icon;
         public StatusBarIconView expandedIcon;
-        public ExpandableNotificationRow row; // the outer expanded view
         private boolean interruption;
         public boolean autoRedacted; // whether the redacted notification was generated by us
         public int targetSdk;
@@ -119,6 +121,10 @@
         public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
         public CharSequence[] smartReplies = new CharSequence[0];
 
+        private Entry parent; // our parent (if we're in a group)
+        private ArrayList<Entry> children = new ArrayList<Entry>();
+        private ExpandableNotificationRow row; // the outer expanded view
+
         private int mCachedContrastColor = COLOR_INVALID;
         private int mCachedContrastColorIsFor = COLOR_INVALID;
         private InflationTask mRunningTask = null;
@@ -212,6 +218,24 @@
             }
         }
 
+        public ExpandableNotificationRow getRow() {
+            return row;
+        }
+
+        //TODO: This will go away when we have a way to bind an entry to a row
+        public void setRow(ExpandableNotificationRow row) {
+            this.row = row;
+        }
+
+        @Nullable
+        public List<Entry> getChildren() {
+            if (children.size() <= 0) {
+                return null;
+            }
+
+            return children;
+        }
+
         public void notifyFullScreenIntentLaunched() {
             setInterruption();
             lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
@@ -409,6 +433,198 @@
                 initializationTime = time;
             }
         }
+
+        public void sendAccessibilityEvent(int eventType) {
+            if (row != null) {
+                row.sendAccessibilityEvent(eventType);
+            }
+        }
+
+        /**
+         * Used by NotificationMediaManager to determine... things
+         * @return {@code true} if we are a media notification
+         */
+        public boolean isMediaNotification() {
+            if (row == null) return false;
+
+            return row.isMediaRow();
+        }
+
+        /**
+         * We are a top level child if our parent is the list of notifications duh
+         * @return {@code true} if we're a top level notification
+         */
+        public boolean isTopLevelChild() {
+            return row != null && row.isTopLevelChild();
+        }
+
+        public void resetUserExpansion() {
+            if (row != null) row.resetUserExpansion();
+        }
+
+        public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
+            if (row != null) row.freeContentViewWhenSafe(inflationFlag);
+        }
+
+        public void setAmbientPulsing(boolean pulsing) {
+            if (row != null) row.setAmbientPulsing(pulsing);
+        }
+
+        public boolean rowExists() {
+            return row != null;
+        }
+
+        public boolean isRowDismissed() {
+            return row != null && row.isDismissed();
+        }
+
+        public boolean isRowRemoved() {
+            return row != null && row.isRemoved();
+        }
+
+        /**
+         * @return {@code true} if the row is null or removed
+         */
+        public boolean isRemoved() {
+            //TODO: recycling invalidates this
+            return row == null || row.isRemoved();
+        }
+
+        /**
+         * @return {@code true} if the row is null or dismissed
+         */
+        public boolean isDismissed() {
+            //TODO: recycling
+            return row == null || row.isDismissed();
+        }
+
+        public boolean isRowPinned() {
+            return row != null && row.isPinned();
+        }
+
+        public void setRowPinned(boolean pinned) {
+            if (row != null) row.setPinned(pinned);
+        }
+
+        public boolean isRowAnimatingAway() {
+            return row != null && row.isHeadsUpAnimatingAway();
+        }
+
+        public boolean isRowHeadsUp() {
+            return row != null && row.isHeadsUp();
+        }
+
+        public void setHeadsUp(boolean shouldHeadsUp) {
+            if (row != null) row.setHeadsUp(shouldHeadsUp);
+        }
+
+        public boolean mustStayOnScreen() {
+            return row != null && row.mustStayOnScreen();
+        }
+
+        public void setHeadsUpIsVisible() {
+            if (row != null) row.setHeadsUpIsVisible();
+        }
+
+        //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
+        public ExpandableNotificationRow getHeadsUpAnimationView() {
+            return row;
+        }
+
+        public void setUserLocked(boolean userLocked) {
+            if (row != null) row.setUserLocked(userLocked);
+        }
+
+        public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
+            if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
+        }
+
+        public void setGroupExpansionChanging(boolean changing) {
+            if (row != null) row.setGroupExpansionChanging(changing);
+        }
+
+        public void notifyHeightChanged(boolean needsAnimation) {
+            if (row != null) row.notifyHeightChanged(needsAnimation);
+        }
+
+        public void closeRemoteInput() {
+            if (row != null) row.closeRemoteInput();
+        }
+
+        public boolean areChildrenExpanded() {
+            return row != null && row.areChildrenExpanded();
+        }
+
+        public boolean keepInParent() {
+            return row != null && row.keepInParent();
+        }
+
+        //TODO: probably less confusing to say "is group fully visible"
+        public boolean isGroupNotFullyVisible() {
+            return row == null || row.isGroupNotFullyVisible();
+        }
+
+        public NotificationGuts getGuts() {
+            if (row != null) return row.getGuts();
+            return null;
+        }
+
+        public boolean hasLowPriorityStateUpdated() {
+            return row != null && row.hasLowPriorityStateUpdated();
+        }
+
+        public void removeRow() {
+            if (row != null) row.setRemoved();
+        }
+
+        public boolean isSummaryWithChildren() {
+            return row != null && row.isSummaryWithChildren();
+        }
+
+        public void setKeepInParent(boolean keep) {
+            if (row != null) row.setKeepInParent(keep);
+        }
+
+        public void onDensityOrFontScaleChanged() {
+            if (row != null) row.onDensityOrFontScaleChanged();
+        }
+
+        public boolean areGutsExposed() {
+            return row != null && row.getGuts().isExposed();
+        }
+
+        public boolean isChildInGroup() {
+            return parent == null;
+        }
+
+        public void setLowPriorityStateUpdated(boolean updated) {
+            if (row != null) row.setLowPriorityStateUpdated(updated);
+        }
+
+        /**
+         * @return Can the underlying notification be cleared? This can be different from whether the
+         *         notification can be dismissed in case notifications are sensitive on the lockscreen.
+         * @see #canViewBeDismissed()
+         */
+        public boolean isClearable() {
+            if (notification == null || !notification.isClearable()) {
+                return false;
+            }
+            if (children.size() > 0) {
+                for (int i = 0; i < children.size(); i++) {
+                    Entry child =  children.get(i);
+                    if (!child.isClearable()) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+
+        public boolean canViewBeDismissed() {
+            if (row == null) return true;
+            return row.canViewBeDismissed();
+        }
     }
 
     private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 0818513..7970f16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,7 +24,6 @@
 
 import android.annotation.Nullable;
 import android.app.Notification;
-import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -108,13 +107,13 @@
         VisualStabilityManager.Callback, BubbleController.BubbleDismissListener {
     private static final String TAG = "NotificationEntryMgr";
     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    protected static final boolean ENABLE_HEADS_UP = true;
-    protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+    private static final boolean ENABLE_HEADS_UP = true;
+    private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
-    protected final NotificationMessagingUtil mMessagingUtil;
+    private final NotificationMessagingUtil mMessagingUtil;
     protected final Context mContext;
     protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
-    protected final NotificationClicker mNotificationClicker = new NotificationClicker();
+    private final NotificationClicker mNotificationClicker = new NotificationClicker();
 
     private final NotificationGroupManager mGroupManager =
             Dependency.get(NotificationGroupManager.class);
@@ -145,14 +144,15 @@
     private NotificationPresenter mPresenter;
     private Callback mCallback;
     protected PowerManager mPowerManager;
-    protected NotificationListenerService.RankingMap mLatestRankingMap;
+    private NotificationListenerService.RankingMap mLatestRankingMap;
     protected HeadsUpManager mHeadsUpManager;
     protected NotificationData mNotificationData;
-    protected ContentObserver mHeadsUpObserver;
+    private ContentObserver mHeadsUpObserver;
     protected boolean mUseHeadsUp = false;
-    protected boolean mDisableNotificationAlerts;
+    private boolean mDisableNotificationAlerts;
     protected NotificationListContainer mListContainer;
-    protected final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
+    @VisibleForTesting
+    final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
             = new ArrayList<>();
     private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
     private NotificationViewHierarchyManager.StatusBarStateListener mStatusBarStateListener;
@@ -384,9 +384,9 @@
                 entry.notification.getUser().getIdentifier());
 
         final StatusBarNotification sbn = entry.notification;
-        if (entry.row != null) {
+        if (entry.rowExists()) {
             entry.reset();
-            updateNotification(entry, pmUser, sbn, entry.row);
+            updateNotification(entry, pmUser, sbn, entry.getRow());
         } else {
             new RowInflaterTask().inflate(mContext, parent, entry,
                     row -> {
@@ -485,22 +485,6 @@
         updateNotifications();
     }
 
-    /**
-     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
-     * about the failure.
-     *
-     * WARNING: this will call back into us.  Don't hold any locks.
-     */
-    void handleNotificationError(StatusBarNotification n, String message) {
-        removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
-        try {
-            mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
-                    n.getInitialPid(), message, n.getUserId());
-        } catch (RemoteException ex) {
-            // The end is nigh.
-        }
-    }
-
     private void abortExistingInflation(String key) {
         if (mPendingNotifications.containsKey(key)) {
             NotificationData.Entry entry = mPendingNotifications.get(key);
@@ -513,13 +497,31 @@
         }
     }
 
+    /**
+     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+     * about the failure.
+     *
+     * WARNING: this will call back into us.  Don't hold any locks.
+     */
     @Override
-    public void handleInflationException(StatusBarNotification notification, Exception e) {
-        handleNotificationError(notification, e.getMessage());
+    public void handleInflationException(StatusBarNotification n, Exception e) {
+        removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
+        try {
+            mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
+                    n.getInitialPid(), e.getMessage(), n.getUserId());
+        } catch (RemoteException ex) {
+            // The end is nigh.
+        }
     }
 
     private void addEntry(NotificationData.Entry shadeEntry) {
-        addNotificationViews(shadeEntry);
+        if (shadeEntry == null) {
+            return;
+        }
+        // Add the expanded view and icon.
+        mNotificationData.add(shadeEntry);
+        tagForeground(shadeEntry.notification);
+        updateNotifications();
         mCallback.onNotificationAdded(shadeEntry);
     }
 
@@ -540,14 +542,14 @@
                 // Mark as seen immediately
                 setNotificationShown(entry.notification);
             } else {
-                entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+                entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
             }
         }
         if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
             if (shouldPulse(entry)) {
                 mAmbientPulseManager.showNotification(entry);
             } else {
-                entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+                entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
             }
         }
     }
@@ -558,20 +560,20 @@
         mPendingNotifications.remove(entry.key);
         // If there was an async task started after the removal, we don't want to add it back to
         // the list, otherwise we might get leaks.
-        if (!entry.row.isRemoved()) {
+        if (!entry.isRowRemoved()) {
             boolean isNew = mNotificationData.get(entry.key) == null;
             if (isNew) {
                 showAlertingView(entry, inflatedFlags);
                 addEntry(entry);
             } else {
-                if (entry.row.hasLowPriorityStateUpdated()) {
+                if (entry.getRow().hasLowPriorityStateUpdated()) {
                     mVisualStabilityManager.onLowPriorityUpdated(entry);
                     mPresenter.updateNotificationViews();
                 }
                 mGroupAlertTransferHelper.onInflationFinished(entry);
             }
         }
-        entry.row.setLowPriorityStateUpdated(false);
+        entry.setLowPriorityStateUpdated(false);
     }
 
     @Override
@@ -630,9 +632,9 @@
         getMediaManager().onNotificationRemoved(key);
         mForegroundServiceController.removeNotification(entry.notification);
 
-        if (entry.row != null) {
-            entry.row.setRemoved();
-            mListContainer.cleanUpViewState(entry.row);
+        if (entry.rowExists()) {
+            entry.removeRow();
+            mListContainer.cleanUpViewStateForEntry(entry);
         }
 
         // Let's remove the children if this was a summary
@@ -667,19 +669,19 @@
      */
     private void handleGroupSummaryRemoved(String key) {
         NotificationData.Entry entry = mNotificationData.get(key);
-        if (entry != null && entry.row != null
-                && entry.row.isSummaryWithChildren()) {
-            if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) {
+        if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
+            if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
                 // We don't want to remove children for autobundled notifications as they are not
                 // always cancelled. We only remove them if they were dismissed by the user.
                 return;
             }
-            List<ExpandableNotificationRow> notificationChildren =
-                    entry.row.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow row = notificationChildren.get(i);
-                NotificationData.Entry childEntry = row.getEntry();
-                boolean isForeground = (row.getStatusBarNotification().getNotification().flags
+            List<NotificationData.Entry> childEntries = entry.getChildren();
+            if (childEntries == null) {
+                return;
+            }
+            for (int i = 0; i < childEntries.size(); i++) {
+                NotificationData.Entry childEntry = childEntries.get(i);
+                boolean isForeground = (entry.notification.getNotification().flags
                         & Notification.FLAG_FOREGROUND_SERVICE) != 0;
                 boolean keepForReply =
                         getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry)
@@ -689,10 +691,10 @@
                     // a child we're keeping around for reply!
                     continue;
                 }
-                row.setKeepInParent(true);
+                entry.setKeepInParent(true);
                 // we need to set this state earlier as otherwise we might generate some weird
                 // animations
-                row.setRemoved();
+                entry.removeRow();
             }
         }
     }
@@ -702,15 +704,15 @@
                 mNotificationData.getNotificationsForCurrentUser();
         for (int i = 0; i < userNotifications.size(); i++) {
             NotificationData.Entry entry = userNotifications.get(i);
-            boolean exposedGuts = mGutsManager.getExposedGuts() != null
-                    && entry.row.getGuts() == mGutsManager.getExposedGuts();
-            entry.row.onDensityOrFontScaleChanged();
+            entry.onDensityOrFontScaleChanged();
+            boolean exposedGuts = entry.areGutsExposed();
             if (exposedGuts) {
-                mGutsManager.onDensityOrFontScaleChanged(entry.row);
+                mGutsManager.onDensityOrFontScaleChanged(entry);
             }
         }
     }
 
+    //TODO: This method associates a row with an entry, but eventually needs to not do that
     protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
             StatusBarNotification sbn, ExpandableNotificationRow row) {
         boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
@@ -733,8 +735,8 @@
         entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
         entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
 
-        entry.row = row;
-        entry.row.setOnActivatedListener(mPresenter);
+        entry.setRow(row);
+        row.setOnActivatedListener(mPresenter);
 
         boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
                 mNotificationData.getImportance(sbn.getKey()));
@@ -755,17 +757,7 @@
         row.inflateViews();
     }
 
-    protected void addNotificationViews(NotificationData.Entry entry) {
-        if (entry == null) {
-            return;
-        }
-        // Add the expanded view and icon.
-        mNotificationData.add(entry);
-        tagForeground(entry.notification);
-        updateNotifications();
-    }
-
-    protected NotificationData.Entry createNotificationViews(
+    private NotificationData.Entry createNotificationViews(
             StatusBarNotification sbn, NotificationListenerService.Ranking ranking)
             throws InflationException {
         if (DEBUG) {
@@ -773,7 +765,7 @@
         }
 
         NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking);
-        if (shouldAutoBubble(entry)) {
+        if (BubbleController.shouldAutoBubble(getContext(), entry)) {
             entry.setIsBubble(true);
         }
 
@@ -841,7 +833,7 @@
     }
 
     @VisibleForTesting
-    protected void tagForeground(StatusBarNotification notification) {
+    void tagForeground(StatusBarNotification notification) {
         ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
                 notification.getUserId(), notification.getPackageName());
         if (activeOps != null) {
@@ -917,7 +909,7 @@
         if (!notification.isClearable()) {
             // The user may have performed a dismiss action on the notification, since it's
             // not clearable we should snap it back.
-            mListContainer.snapViewIfNeeded(entry.row);
+            mListContainer.snapViewIfNeeded(entry);
         }
 
         if (DEBUG) {
@@ -970,11 +962,11 @@
 
             if (NotificationUiAdjustment.needReinflate(
                     oldAdjustments.get(entry.key), newAdjustment)) {
-                if (entry.row != null) {
+                if (entry.rowExists()) {
                     entry.reset();
                     PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
                             entry.notification.getUser().getIdentifier());
-                    updateNotification(entry, pmUser, entry.notification, entry.row);
+                    updateNotification(entry, pmUser, entry.notification, entry.getRow());
                 } else {
                     // Once the RowInflaterTask is done, it will pick up the updated entry, so
                     // no-op here.
@@ -1098,7 +1090,7 @@
      * @param entry the entry to check
      * @return true if the entry should ambient pulse, false otherwise
      */
-    protected boolean shouldPulse(NotificationData.Entry entry) {
+    private boolean shouldPulse(NotificationData.Entry entry) {
         StatusBarNotification sbn = entry.notification;
 
         if (!getShadeController().isDozing()) {
@@ -1173,7 +1165,7 @@
         return true;
     }
 
-    protected void setNotificationShown(StatusBarNotification n) {
+    private void setNotificationShown(StatusBarNotification n) {
         setNotificationsShown(new String[]{n.getKey()});
     }
 
@@ -1185,7 +1177,7 @@
         }
     }
 
-    protected boolean isSnoozedPackage(StatusBarNotification sbn) {
+    private boolean isSnoozedPackage(StatusBarNotification sbn) {
         return mHeadsUpManager.isSnoozed(sbn.getPackageName());
     }
 
@@ -1214,17 +1206,6 @@
         }
     }
 
-
-    /**
-     * Whether a bubble is appropriate to auto-bubble or not.
-     */
-    private boolean shouldAutoBubble(NotificationData.Entry entry) {
-        int priority = mNotificationData.getImportance(entry.key);
-        NotificationChannel channel = mNotificationData.getChannel(entry.key);
-        boolean canAppOverlay = channel != null && channel.canOverlayApps();
-        return BubbleController.shouldAutoBubble(entry, priority, canAppOverlay);
-    }
-
     /**
      * Callback for NotificationEntryManager.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 53ebe74..a194eef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -16,18 +16,16 @@
 
 package com.android.systemui.statusbar.notification;
 
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-
 /**
  * An object that can determine the visibility of a Notification.
  */
 public interface VisibilityLocationProvider {
 
     /**
-     * Returns whether an ExpandableNotificationRow is in a visible location or not.
+     * Returns whether an Entry is in a visible location or not.
      *
-     * @param row
+     * @param entry
      * @return true if row is in a visible location
      */
-    boolean isInVisibleLocation(ExpandableNotificationRow row);
+    boolean isInVisibleLocation(NotificationData.Entry entry);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 75613a4..fce7980 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -120,7 +120,7 @@
             return true;
         }
         if (mAllowedReorderViews.contains(row)
-                && !mVisibilityLocationProvider.isInVisibleLocation(row)) {
+                && !mVisibilityLocationProvider.isInVisibleLocation(row.getEntry())) {
             return true;
         }
         return false;
@@ -142,12 +142,12 @@
         if (isHeadsUp) {
             // Heads up notifications should in general be allowed to reorder if they are out of
             // view and stay at the current location if they aren't.
-            mAllowedReorderViews.add(entry.row);
+            mAllowedReorderViews.add(entry.getRow());
         }
     }
 
     public void onLowPriorityUpdated(NotificationData.Entry entry) {
-        mLowPriorityReorderingViews.add(entry.row);
+        mLowPriorityReorderingViews.add(entry.getRow());
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 5dfd5d0..87313b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -115,7 +115,7 @@
             for (int i = 0; i < N; i++) {
                 NotificationData.Entry entry = activeNotifications.get(i);
                 String key = entry.notification.getKey();
-                boolean isVisible = mListContainer.isInVisibleLocation(entry.row);
+                boolean isVisible = mListContainer.isInVisibleLocation(entry);
                 NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
                 boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
                 if (isVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b6d99b2..0cd431f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,22 +17,14 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
-        .ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
-        .VISIBLE_TYPE_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
-        .VISIBLE_TYPE_CONTRACTED;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
-        .VISIBLE_TYPE_HEADSUP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_CONTENT_VIEW_PUBLIC;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .InflationCallback;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -104,7 +96,7 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -336,7 +328,6 @@
     private float mTranslationWhenRemoved;
     private boolean mWasChildInGroupWhenRemoved;
     private int mNotificationColorAmbient;
-    private NotificationViewState mNotificationViewState;
 
     private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
             new SystemNotificationAsyncTask();
@@ -893,29 +884,32 @@
                 visualStabilityManager, callback);
     }
 
-    public void getChildrenStates(StackScrollState resultState,
-            AmbientState ambientState) {
+    /** Updates states of all children. */
+    public void updateChildrenStates(AmbientState ambientState) {
         if (mIsSummaryWithChildren) {
-            ExpandableViewState parentState = resultState.getViewStateForView(this);
-            mChildrenContainer.getState(resultState, parentState, ambientState);
+            ExpandableViewState parentState = getViewState();
+            mChildrenContainer.updateState(parentState, ambientState);
         }
     }
 
-    public void applyChildrenState(StackScrollState state) {
+    /** Applies children states. */
+    public void applyChildrenState() {
         if (mIsSummaryWithChildren) {
-            mChildrenContainer.applyState(state);
+            mChildrenContainer.applyState();
         }
     }
 
-    public void prepareExpansionChanged(StackScrollState state) {
+    /** Prepares expansion changed. */
+    public void prepareExpansionChanged() {
         if (mIsSummaryWithChildren) {
-            mChildrenContainer.prepareExpansionChanged(state);
+            mChildrenContainer.prepareExpansionChanged();
         }
     }
 
-    public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) {
+    /** Starts child animations. */
+    public void startChildAnimation(AnimationProperties properties) {
         if (mIsSummaryWithChildren) {
-            mChildrenContainer.startAnimationToState(finalState, properties);
+            mChildrenContainer.startAnimationToState(properties);
         }
     }
 
@@ -1411,16 +1405,16 @@
 
     public void performDismiss(boolean fromAccessibility) {
         if (isOnlyChildInGroup()) {
-            ExpandableNotificationRow groupSummary =
+            NotificationData.Entry groupSummary =
                     mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
             if (groupSummary.isClearable()) {
                 // If this is the only child in the group, dismiss the group, but don't try to show
                 // the blocking helper affordance!
-                groupSummary.performDismiss(fromAccessibility);
+                groupSummary.getRow().performDismiss(fromAccessibility);
             }
         }
         setDismissed(fromAccessibility);
-        if (isClearable()) {
+        if (mEntry.isClearable()) {
             // TODO: track dismiss sentiment
             if (mOnDismissRunnable != null) {
                 mOnDismissRunnable.run();
@@ -2046,7 +2040,7 @@
                     .setInterpolator(Interpolators.ALPHA_OUT);
             setAboveShelf(true);
             mExpandAnimationRunning = true;
-            mNotificationViewState.cancelAnimations(this);
+            getViewState().cancelAnimations(this);
             mNotificationLaunchHeight = AmbientState.getNotificationLaunchHeight(getContext());
         } else {
             mExpandAnimationRunning = false;
@@ -2244,28 +2238,6 @@
         setRippleAllowed(allowed);
     }
 
-    /**
-     * @return Can the underlying notification be cleared? This can be different from whether the
-     *         notification can be dismissed in case notifications are sensitive on the lockscreen.
-     * @see #canViewBeDismissed()
-     */
-    public boolean isClearable() {
-        if (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
-            return false;
-        }
-        if (mIsSummaryWithChildren) {
-            List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow child = notificationChildren.get(i);
-                if (!child.isClearable()) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
     @Override
     public int getIntrinsicHeight() {
         if (isShownAsBubble()) {
@@ -2533,10 +2505,10 @@
     /**
      * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
      *         otherwise some state might not be updated. To request about the general clearability
-     *         see {@link #isClearable()}.
+     *         see {@link NotificationData.Entry#isClearable()}.
      */
     public boolean canViewBeDismissed() {
-        return isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+        return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
     }
 
     private boolean shouldShowPublic() {
@@ -2945,13 +2917,8 @@
     }
 
     @Override
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
-        mNotificationViewState = new NotificationViewState(stackScrollState);
-        return mNotificationViewState;
-    }
-
-    public NotificationViewState getViewState() {
-        return mNotificationViewState;
+    public ExpandableViewState createExpandableViewState() {
+        return new NotificationViewState();
     }
 
     @Override
@@ -3038,6 +3005,21 @@
         return mOnAmbient;
     }
 
+    //TODO: this logic can't depend on layout if we are recycling!
+    public boolean isMediaRow() {
+        return getExpandedContentView() != null
+                && getExpandedContentView().findViewById(
+                com.android.internal.R.id.media_actions) != null;
+    }
+
+    public boolean isTopLevelChild() {
+        return getParent() instanceof NotificationStackScrollLayout;
+    }
+
+    public boolean isGroupNotFullyVisible() {
+        return getClipTopAmount() > 0 || getTranslationY() < 0;
+    }
+
     public void setAboveShelf(boolean aboveShelf) {
         boolean wasAboveShelf = isAboveShelf();
         mAboveShelf = aboveShelf;
@@ -3046,14 +3028,7 @@
         }
     }
 
-    public static class NotificationViewState extends ExpandableViewState {
-
-        private final StackScrollState mOverallState;
-
-
-        private NotificationViewState(StackScrollState stackScrollState) {
-            mOverallState = stackScrollState;
-        }
+    private static class NotificationViewState extends ExpandableViewState {
 
         @Override
         public void applyToView(View view) {
@@ -3064,7 +3039,7 @@
                 }
                 handleFixedTranslationZ(row);
                 super.applyToView(view);
-                row.applyChildrenState(mOverallState);
+                row.applyChildrenState();
             }
         }
 
@@ -3095,7 +3070,7 @@
                 }
                 handleFixedTranslationZ(row);
                 super.animateTo(child, properties);
-                row.startChildAnimation(mOverallState, properties);
+                row.startChildAnimation(properties);
             }
         }
     }
@@ -3145,11 +3120,13 @@
         pw.print(", alpha: " + getAlpha());
         pw.print(", translation: " + getTranslation());
         pw.print(", removed: " + isRemoved());
-        pw.print(", privateShowing: " + (getShowingLayout() == mPrivateLayout));
+        NotificationContentView showingLayout = getShowingLayout();
+        pw.print(", privateShowing: " + (showingLayout == mPrivateLayout));
         pw.println();
+        showingLayout.dump(fd, pw, args);
         pw.print("    ");
-        if (mNotificationViewState != null) {
-            mNotificationViewState.dump(fd, pw, args);
+        if (getViewState() != null) {
+            getViewState().dump(fd, pw, args);
         } else {
             pw.print("no viewState!!!");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 0589e3f..1e8de07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -21,23 +21,27 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.annotation.Nullable;
+
 import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * An abstract view for expandable views.
  */
 public abstract class ExpandableView extends FrameLayout implements Dumpable {
+    private static final String TAG = "ExpandableView";
 
     public static final float NO_ROUNDNESS = -1;
     protected OnHeightChangedListener mOnHeightChangedListener;
@@ -54,6 +58,7 @@
     private ViewGroup mTransientContainer;
     private boolean mInShelf;
     private boolean mTransformingInShelf;
+    @Nullable private ExpandableViewState mViewState;
 
     public ExpandableView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -511,10 +516,56 @@
 
     public void setActualHeightAnimating(boolean animating) {}
 
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+    protected ExpandableViewState createExpandableViewState() {
         return new ExpandableViewState();
     }
 
+    /** Sets {@link ExpandableViewState} to default state. */
+    public ExpandableViewState resetViewState() {
+        // TODO(http://b/119762423): Move the null check to getViewState().
+        if (mViewState == null) {
+            mViewState = createExpandableViewState();
+        }
+
+        // initialize with the default values of the view
+        mViewState.height = getIntrinsicHeight();
+        mViewState.gone = getVisibility() == View.GONE;
+        mViewState.alpha = 1f;
+        mViewState.notGoneIndex = -1;
+        mViewState.xTranslation = getTranslationX();
+        mViewState.hidden = false;
+        mViewState.scaleX = getScaleX();
+        mViewState.scaleY = getScaleY();
+        mViewState.inShelf = false;
+        mViewState.headsUpIsVisible = false;
+
+        // handling reset for child notifications
+        if (this instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) this;
+            List<ExpandableNotificationRow> children = row.getNotificationChildren();
+            if (row.isSummaryWithChildren() && children != null) {
+                for (ExpandableNotificationRow childRow : children) {
+                    childRow.resetViewState();
+                }
+            }
+        }
+
+        return mViewState;
+    }
+
+    @Nullable public ExpandableViewState getViewState() {
+        return mViewState;
+    }
+
+    /** Applies internal {@link ExpandableViewState} to this view. */
+    public void applyViewState() {
+        if (mViewState == null) {
+            Log.wtf(TAG, "No child state was found when applying this state to the hostView");
+        } else if (!mViewState.gone) {
+            mViewState.applyToView(this);
+        }
+    }
+
     /**
      * @return whether the current view doesn't add height to the overall content. This means that
      * if it is added to a list of items, it's content will still have the same height.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 1f15ed0..311bf7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -24,7 +24,6 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
 
 public class FooterView extends StackScrollerDecorView {
     private final int mClearAllTopPadding;
@@ -87,7 +86,7 @@
     }
 
     @Override
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+    public ExpandableViewState createExpandableViewState() {
         return new FooterViewState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index bb9a341..92d1b45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -55,6 +55,8 @@
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.SmartReplyView;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.List;
 
 /**
@@ -1227,17 +1229,18 @@
         mOnContentViewInactiveListeners.clear();
         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
         updateAllSingleLineViews();
+        ExpandableNotificationRow row = entry.getRow();
         if (mContractedChild != null) {
-            mContractedWrapper.onContentUpdated(entry.row);
+            mContractedWrapper.onContentUpdated(row);
         }
         if (mExpandedChild != null) {
-            mExpandedWrapper.onContentUpdated(entry.row);
+            mExpandedWrapper.onContentUpdated(row);
         }
         if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.onContentUpdated(entry.row);
+            mHeadsUpWrapper.onContentUpdated(row);
         }
         if (mAmbientChild != null) {
-            mAmbientWrapper.onContentUpdated(entry.row);
+            mAmbientWrapper.onContentUpdated(row);
         }
         applyRemoteInputAndSmartReply(entry);
         updateLegacy();
@@ -1287,10 +1290,10 @@
             return;
         }
 
-        SmartRepliesAndActions smartRepliesAndActions = chooseSmartRepliesAndActions(
-                mSmartReplyConstants, entry);
+        SmartRepliesAndActions smartRepliesAndActions =
+                chooseSmartRepliesAndActions(mSmartReplyConstants, entry);
 
-        applyRemoteInput(entry, smartRepliesAndActions.freeformRemoteInputActionPair != null);
+        applyRemoteInput(entry, smartRepliesAndActions.hasFreeformRemoteInput);
         applySmartReplyView(smartRepliesAndActions, entry);
     }
 
@@ -1317,58 +1320,47 @@
         boolean appGeneratedSmartRepliesExist =
                 enableAppGeneratedSmartReplies
                         && remoteInputActionPair != null
-                        && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices());
+                        && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())
+                        && remoteInputActionPair.second.actionIntent != null;
 
         List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
         boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
 
+        SmartReplyView.SmartReplies smartReplies = null;
+        SmartReplyView.SmartActions smartActions = null;
         if (appGeneratedSmartRepliesExist) {
-            return new SmartRepliesAndActions(remoteInputActionPair.first,
-                    remoteInputActionPair.second.actionIntent,
+            smartReplies = new SmartReplyView.SmartReplies(
                     remoteInputActionPair.first.getChoices(),
-                    appGeneratedSmartActions,
-                    freeformRemoteInputActionPair);
-        } else if (appGeneratedSmartActionsExist) {
-            return new SmartRepliesAndActions(null, null, null, appGeneratedSmartActions,
-                    freeformRemoteInputActionPair);
-        } else if (!ArrayUtils.isEmpty(entry.smartReplies)
-                && freeformRemoteInputActionPair != null
-                && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()) {
-            // App didn't generate anything, use NAS-generated replies and actions
-            return new SmartRepliesAndActions(freeformRemoteInputActionPair.first,
-                    freeformRemoteInputActionPair.second.actionIntent,
-                    entry.smartReplies,
-                    entry.systemGeneratedSmartActions,
-                    freeformRemoteInputActionPair);
+                    remoteInputActionPair.first,
+                    remoteInputActionPair.second.actionIntent,
+                    false /* fromAssistant */);
         }
-        // App didn't generate anything, and there are no NAS-generated smart replies.
-        return new SmartRepliesAndActions(null, null, null, entry.systemGeneratedSmartActions,
-                freeformRemoteInputActionPair);
-    }
-
-    @VisibleForTesting
-    static class SmartRepliesAndActions {
-        public final RemoteInput remoteInputWithChoices;
-        public final PendingIntent pendingIntentForSmartReplies;
-        public final CharSequence[] smartReplies;
-        public final List<Notification.Action> smartActions;
-        public final Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair;
-
-        SmartRepliesAndActions(RemoteInput remoteInput, PendingIntent pendingIntent,
-                CharSequence[] choices, List<Notification.Action> smartActions,
-                Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair) {
-            this.remoteInputWithChoices = remoteInput;
-            this.pendingIntentForSmartReplies = pendingIntent;
-            this.smartReplies = choices;
-            this.smartActions = smartActions;
-            this.freeformRemoteInputActionPair = freeformRemoteInputActionPair;
+        if (appGeneratedSmartActionsExist) {
+            smartActions = new SmartReplyView.SmartActions(appGeneratedSmartActions,
+                    false /* fromAssistant */);
         }
-
-        boolean smartRepliesExist() {
-            return remoteInputWithChoices != null
-                    && pendingIntentForSmartReplies != null
-                    && !ArrayUtils.isEmpty(smartReplies);
+        // Apps didn't provide any smart replies / actions, use those from NAS (if any).
+        if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) {
+            boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.smartReplies)
+                    && freeformRemoteInputActionPair != null
+                    && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()
+                    && freeformRemoteInputActionPair.second.actionIntent != null;
+            if (useGeneratedReplies) {
+                smartReplies = new SmartReplyView.SmartReplies(
+                        entry.smartReplies,
+                        freeformRemoteInputActionPair.first,
+                        freeformRemoteInputActionPair.second.actionIntent,
+                        true /* fromAssistant */);
+            }
+            boolean useSmartActions = !ArrayUtils.isEmpty(entry.systemGeneratedSmartActions)
+                    && notification.getAllowSystemGeneratedContextualActions();
+            if (useSmartActions) {
+                smartActions = new SmartReplyView.SmartActions(
+                        entry.systemGeneratedSmartActions, true /* fromAssistant */);
+            }
         }
+        return new SmartRepliesAndActions(
+                smartReplies, smartActions, freeformRemoteInputActionPair != null);
     }
 
     private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
@@ -1475,12 +1467,9 @@
         if (mExpandedChild != null) {
             mExpandedSmartReplyView =
                     applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
-            if (mExpandedSmartReplyView != null
-                    && smartRepliesAndActions.remoteInputWithChoices != null
-                    && smartRepliesAndActions.smartReplies != null
-                    && smartRepliesAndActions.smartReplies.length > 0) {
-                mSmartReplyController.smartRepliesAdded(entry,
-                        smartRepliesAndActions.smartReplies.length);
+            if (mExpandedSmartReplyView != null && smartRepliesAndActions.smartReplies != null) {
+                mSmartReplyController.smartRepliesAdded(
+                        entry, smartRepliesAndActions.smartReplies.choices.length);
             }
         }
     }
@@ -1494,8 +1483,8 @@
         }
         LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
         // If there are no smart replies and no smart actions - early out.
-        if (!smartRepliesAndActions.smartRepliesExist()
-                && smartRepliesAndActions.smartActions.isEmpty()) {
+        if (smartRepliesAndActions.smartReplies == null
+                && smartRepliesAndActions.smartActions == null) {
             smartReplyContainer.setVisibility(View.GONE);
             return null;
         }
@@ -1525,10 +1514,14 @@
         }
         if (smartReplyView != null) {
             smartReplyView.resetSmartSuggestions(smartReplyContainer);
-            smartReplyView.addRepliesFromRemoteInput(smartRepliesAndActions.remoteInputWithChoices,
-                    smartRepliesAndActions.pendingIntentForSmartReplies, mSmartReplyController,
-                    entry, smartRepliesAndActions.smartReplies);
-            smartReplyView.addSmartActions(smartRepliesAndActions.smartActions);
+            if (smartRepliesAndActions.smartReplies != null) {
+                smartReplyView.addRepliesFromRemoteInput(
+                        smartRepliesAndActions.smartReplies, mSmartReplyController, entry);
+            }
+            if (smartRepliesAndActions.smartActions != null) {
+                smartReplyView.addSmartActions(
+                        smartRepliesAndActions.smartActions, mSmartReplyController, entry);
+            }
             smartReplyContainer.setVisibility(View.VISIBLE);
         }
         return smartReplyView;
@@ -1928,4 +1921,41 @@
             mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
         }
     }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.print("    ");
+        pw.print("contentView visibility: " + getVisibility());
+        pw.print(", alpha: " + getAlpha());
+        pw.print(", clipBounds: " + getClipBounds());
+        pw.print(", contentHeight: " + mContentHeight);
+        pw.print(", visibleType: " + mVisibleType);
+        View view = getViewForVisibleType(mVisibleType);
+        pw.print(", visibleView ");
+        if (view != null) {
+            pw.print(" visibility: " + view.getVisibility());
+            pw.print(", alpha: " + view.getAlpha());
+            pw.print(", clipBounds: " + view.getClipBounds());
+        } else {
+            pw.print("null");
+        }
+        pw.println();
+    }
+
+    @VisibleForTesting
+    static class SmartRepliesAndActions {
+        @Nullable
+        public final SmartReplyView.SmartReplies smartReplies;
+        @Nullable
+        public final SmartReplyView.SmartActions smartActions;
+        public final boolean hasFreeformRemoteInput;
+
+        SmartRepliesAndActions(
+                @Nullable SmartReplyView.SmartReplies smartReplies,
+                @Nullable SmartReplyView.SmartActions smartActions,
+                boolean hasFreeformRemoteInput) {
+            this.smartReplies = smartReplies;
+            this.smartActions = smartActions;
+            this.hasFreeformRemoteInput = hasFreeformRemoteInput;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 37bf06e..3b407b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -18,10 +18,7 @@
 import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
-
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 
 import android.app.INotificationManager;
 import android.app.NotificationChannel;
@@ -43,7 +40,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.plugins.ActivityStarter;
@@ -86,7 +82,6 @@
     private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
 
     // which notification is currently being longpress-examined by the user
-    private final IStatusBarService mBarService;
     private NotificationGuts mNotificationGutsExposed;
     private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
     private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
@@ -103,8 +98,6 @@
 
         mAccessibilityManager = (AccessibilityManager)
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        mBarService = IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -116,9 +109,9 @@
         mOnSettingsClickListener = onSettingsClick;
     }
 
-    public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
-        setExposedGuts(row.getGuts());
-        bindGuts(row);
+    public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
+        setExposedGuts(entry.getGuts());
+        bindGuts(entry.getRow());
     }
 
     /**
@@ -191,13 +184,7 @@
             } else if (gutsView instanceof AppOpsInfo) {
                 initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
             } else if (gutsView instanceof NotificationInfo) {
-                int action;
-                if (item instanceof NotificationMenuRow.NotificationInfoMenuItem) {
-                    action = ((NotificationMenuRow.NotificationInfoMenuItem) item).mAction;
-                } else {
-                    action = ACTION_NONE;
-                }
-                initializeNotificationInfo(row, (NotificationInfo) gutsView, action);
+                initializeNotificationInfo(row, (NotificationInfo) gutsView);
             }
             return true;
         } catch (Exception e) {
@@ -256,13 +243,11 @@
      * Sets up the {@link NotificationInfo} inside the notification row's guts.
      * @param row view to set up the guts for
      * @param notificationInfoView view to set up/bind within {@code row}
-     * @param action The action to take immediately upon binding, if any.
      */
     @VisibleForTesting
     void initializeNotificationInfo(
             final ExpandableNotificationRow row,
-            NotificationInfo notificationInfoView,
-            @NotificationInfo.NotificationInfoAction int action) throws Exception {
+            NotificationInfo notificationInfoView) throws Exception {
         NotificationGuts guts = row.getGuts();
         StatusBarNotification sbn = row.getStatusBarNotification();
         String packageName = sbn.getPackageName();
@@ -306,8 +291,7 @@
                 isForBlockingHelper,
                 row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
                 row.getEntry().noisy,
-                row.getEntry().importance,
-                action);
+                row.getEntry().importance);
 
     }
 
@@ -441,8 +425,8 @@
     public boolean shouldExtendLifetime(NotificationData.Entry entry) {
         return entry != null
                 &&(mNotificationGutsExposed != null
-                    && entry.row.getGuts() != null
-                    && mNotificationGutsExposed == entry.row.getGuts()
+                    && entry.getGuts() != null
+                    && mNotificationGutsExposed == entry.getGuts()
                     && !mNotificationGutsExposed.isLeavebehind());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 3a7091b..213ac70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -92,6 +92,7 @@
     private String mPackageName;
     private String mAppName;
     private int mAppUid;
+    private String mDelegatePkg;
     private int mNumUniqueChannelsInRow;
     private NotificationChannel mSingleNotificationChannel;
     private int mStartingChannelImportance;
@@ -186,14 +187,13 @@
             boolean isDeviceProvisioned,
             boolean isNonblockable,
             boolean isNoisy,
-            int importance,
-            @NotificationInfoAction int action)
+            int importance)
             throws RemoteException {
         bindNotification(pm, iNotificationManager, pkg, notificationChannel,
                 numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
                 onAppSettingsClick, isDeviceProvisioned, isNonblockable,
                 false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy,
-                importance, action);
+                importance);
     }
 
     public void bindNotification(
@@ -211,8 +211,7 @@
             boolean isForBlockingHelper,
             boolean isUserSentimentNegative,
             boolean isNoisy,
-            int importance,
-            @NotificationInfoAction int action)
+            int importance)
             throws RemoteException {
         mINotificationManager = iNotificationManager;
         mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -235,6 +234,7 @@
                 (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
         mIsForBlockingHelper = isForBlockingHelper;
         mAppUid = mSbn.getUid();
+        mDelegatePkg = mSbn.getOpPkg();
         mIsDeviceProvisioned = isDeviceProvisioned;
         mIsNoisy = isNoisy;
 
@@ -253,10 +253,6 @@
         bindHeader();
         bindPrompt();
         bindButtons();
-
-        if (action != ACTION_NONE) {
-            swapContent(action, false /* don't animate */);
-        }
     }
 
     private void bindHeader() throws RemoteException {
@@ -281,26 +277,8 @@
         ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
         ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
 
-        // Set group information if this channel has an associated group.
-        CharSequence groupName = null;
-        if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
-            final NotificationChannelGroup notificationChannelGroup =
-                    mINotificationManager.getNotificationChannelGroupForPackage(
-                            mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
-            if (notificationChannelGroup != null) {
-                groupName = notificationChannelGroup.getName();
-            }
-        }
-        TextView groupNameView = findViewById(R.id.group_name);
-        TextView groupDividerView = findViewById(R.id.pkg_group_divider);
-        if (groupName != null) {
-            groupNameView.setText(groupName);
-            groupNameView.setVisibility(View.VISIBLE);
-            groupDividerView.setVisibility(View.VISIBLE);
-        } else {
-            groupNameView.setVisibility(View.GONE);
-            groupDividerView.setVisibility(View.GONE);
-        }
+        // Delegate
+        bindDelegate();
 
         // Settings button.
         final View settingsButton = findViewById(R.id.info);
@@ -320,9 +298,10 @@
         }
     }
 
-    private void bindPrompt() {
+    private void bindPrompt() throws RemoteException {
         final TextView blockPrompt = findViewById(R.id.block_prompt);
         bindName();
+        bindGroup();
         if (mIsNonblockable) {
             blockPrompt.setText(R.string.notification_unblockable_desc);
         } else {
@@ -345,6 +324,60 @@
         }
     }
 
+    private void bindDelegate() {
+        TextView delegateView = findViewById(R.id.delegate_name);
+        TextView dividerView = findViewById(R.id.pkg_divider);
+
+        CharSequence delegatePkg = null;
+        if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
+            // this notification was posted by a delegate!
+            ApplicationInfo info;
+            try {
+                info = mPm.getApplicationInfo(
+                        mDelegatePkg,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                | PackageManager.MATCH_DISABLED_COMPONENTS
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+                if (info != null) {
+                    delegatePkg = String.valueOf(mPm.getApplicationLabel(info));
+                }
+            } catch (PackageManager.NameNotFoundException e) { }
+        }
+        if (delegatePkg != null) {
+            delegateView.setText(mContext.getResources().getString(
+                    R.string.notification_delegate_header, delegatePkg));
+            delegateView.setVisibility(View.VISIBLE);
+            dividerView.setVisibility(View.VISIBLE);
+        } else {
+            delegateView.setVisibility(View.GONE);
+            dividerView.setVisibility(View.GONE);
+        }
+    }
+
+    private void bindGroup() throws RemoteException {
+        // Set group information if this channel has an associated group.
+        CharSequence groupName = null;
+        if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
+            final NotificationChannelGroup notificationChannelGroup =
+                    mINotificationManager.getNotificationChannelGroupForPackage(
+                            mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
+            if (notificationChannelGroup != null) {
+                groupName = notificationChannelGroup.getName();
+            }
+        }
+        TextView groupNameView = findViewById(R.id.group_name);
+        TextView groupDividerView = findViewById(R.id.pkg_group_divider);
+        if (groupName != null) {
+            groupNameView.setText(groupName);
+            groupNameView.setVisibility(View.VISIBLE);
+            groupDividerView.setVisibility(View.VISIBLE);
+        } else {
+            groupNameView.setVisibility(View.GONE);
+            groupDividerView.setVisibility(View.GONE);
+        }
+    }
+
     @VisibleForTesting
     void logBlockingHelperCounter(String counterTag) {
         if (mIsForBlockingHelper) {
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 b6ff6fc..948d2a5 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
@@ -17,9 +17,6 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_BLOCK;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_TOGGLE_SILENT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -46,7 +43,6 @@
 import com.android.systemui.statusbar.AlphaOptimizedImageView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
-import com.android.systemui.statusbar.notification.row.NotificationInfo.NotificationInfoAction;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
@@ -73,7 +69,7 @@
 
     private Context mContext;
     private FrameLayout mMenuContainer;
-    private NotificationInfoMenuItem mInfoItem;
+    private NotificationMenuItem mInfoItem;
     private MenuItem mAppOpsItem;
     private MenuItem mSnoozeItem;
     private ArrayList<MenuItem> mLeftMenuItems;
@@ -248,36 +244,30 @@
         if (!isForeground) {
             // Only show snooze for non-foreground notifications
             mSnoozeItem = createSnoozeItem(mContext);
-            mLeftMenuItems.add(mSnoozeItem);
         }
-        mInfoItem = createInfoItem(mContext);
-        if (!NotificationUtils.useNewInterruptionModel(mContext)) {
-            mLeftMenuItems.add(mInfoItem);
-        }
-
         mAppOpsItem = createAppOpsItem(mContext);
-        mLeftMenuItems.add(mAppOpsItem);
-
         if (NotificationUtils.useNewInterruptionModel(mContext)) {
-            if (!mParent.getIsNonblockable()) {
-                mRightMenuItems.add(createBlockItem(mContext, mInfoItem.getGutsView()));
-            }
-            // TODO(kprevas): this is duplicated logic
-            // but it's currently spread across NotificationGutsManager and NotificationInfo.
-            // Try to consolidate and reuse here.
-            boolean canToggleSilent = !mParent.getIsNonblockable()
-                    && !isForeground
-                    && mParent.getEntry().noisy;
-            if (canToggleSilent) {
-                int channelImportance = mParent.getEntry().channel.getImportance();
-                int effectiveImportance =
-                        channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
-                                ? mParent.getEntry().importance : channelImportance;
-                mRightMenuItems.add(createToggleSilentItem(mContext, mInfoItem.getGutsView(),
-                        effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT));
-            }
+            int channelImportance = mParent.getEntry().channel.getImportance();
+            int effectiveImportance =
+                    channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
+                            ? mParent.getEntry().importance : channelImportance;
+            mInfoItem = createInfoItem(mContext,
+                    effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT);
         } else {
-            mRightMenuItems.addAll(mLeftMenuItems);
+            mInfoItem = createInfoItem(mContext);
+        }
+
+        if (!NotificationUtils.useNewInterruptionModel(mContext)) {
+            if (!isForeground) {
+                mRightMenuItems.add(mSnoozeItem);
+            }
+            mRightMenuItems.add(mInfoItem);
+            mRightMenuItems.add(mAppOpsItem);
+            mLeftMenuItems.addAll(mRightMenuItems);
+        } else {
+            mRightMenuItems.add(mInfoItem);
+            mRightMenuItems.add(mAppOpsItem);
+            mRightMenuItems.add(mSnoozeItem);
         }
 
         populateMenuViews();
@@ -634,13 +624,24 @@
         return snooze;
     }
 
-    static NotificationInfoMenuItem createInfoItem(Context context) {
+    static NotificationMenuItem createInfoItem(Context context) {
         Resources res = context.getResources();
         String infoDescription = res.getString(R.string.notification_menu_gear_description);
         NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
                 R.layout.notification_info, null, false);
-        return new NotificationInfoMenuItem(context, infoDescription, infoContent,
-                R.drawable.ic_settings, ACTION_NONE);
+        return new NotificationMenuItem(context, infoDescription, infoContent,
+                R.drawable.ic_settings);
+    }
+
+    static NotificationMenuItem createInfoItem(Context context, boolean isCurrentlySilent) {
+        Resources res = context.getResources();
+        String infoDescription = res.getString(R.string.notification_menu_gear_description);
+        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;
+        return new NotificationMenuItem(context, infoDescription, infoContent, iconResId);
     }
 
     static MenuItem createAppOpsItem(Context context) {
@@ -651,29 +652,6 @@
         return info;
     }
 
-    private static MenuItem createBlockItem(Context context, NotificationInfo gutsView) {
-        return new NotificationInfoMenuItem(
-                context,
-                context.getResources().getString(R.string.inline_stop_button),
-                gutsView,
-                R.drawable.ic_notification_block,
-                ACTION_BLOCK);
-    }
-
-    private static MenuItem createToggleSilentItem(Context context, NotificationInfo gutsView,
-            boolean isCurrentlySilent) {
-        return new NotificationInfoMenuItem(
-                context,
-                isCurrentlySilent
-                        ? context.getResources().getString(R.string.inline_silent_button_alert)
-                        : context.getResources().getString(R.string.inline_silent_button_silent),
-                gutsView,
-                isCurrentlySilent
-                        ? R.drawable.ic_notifications_alert
-                        : R.drawable.ic_notifications_silence,
-                ACTION_TOGGLE_SILENT);
-    }
-
     private void addMenuView(MenuItem item, ViewGroup parent) {
         View menuView = item.getMenuView();
         if (menuView != null) {
@@ -789,23 +767,4 @@
             return mContentDescription;
         }
     }
-
-    /** A {@link NotificationMenuItem} with an associated {@link NotificationInfoAction}. */
-    public static class NotificationInfoMenuItem extends NotificationMenuItem {
-
-        @NotificationInfoAction
-        int mAction;
-
-        public NotificationInfoMenuItem(Context context, String contentDescription,
-                NotificationInfo content, int iconResId,
-                @NotificationInfoAction int action) {
-            super(context, contentDescription, content, iconResId);
-            this.mAction = action;
-        }
-
-        @Override
-        public NotificationInfo getGutsView() {
-            return (NotificationInfo) super.getGutsView();
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ff1a6fc..670908f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -40,7 +40,7 @@
 
     private static final int NO_SECTION_BOUNDARY = -1;
 
-    private ArrayList<View> mDraggedViews = new ArrayList<>();
+    private ArrayList<ExpandableView> mDraggedViews = new ArrayList<>();
     private int mScrollY;
     private boolean mDimmed;
     private ActivatableNotificationView mActivatedChild;
@@ -131,7 +131,8 @@
         this.mScrollY = scrollY;
     }
 
-    public void onBeginDrag(View view) {
+    /** Call when dragging begins. */
+    public void onBeginDrag(ExpandableView view) {
         mDraggedViews.add(view);
     }
 
@@ -139,7 +140,7 @@
         mDraggedViews.remove(view);
     }
 
-    public ArrayList<View> getDraggedViews() {
+    public ArrayList<ExpandableView> getDraggedViews() {
         return mDraggedViews;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 74b4aa2a..0f38bd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -274,7 +274,7 @@
         updateGroupOverflow();
         row.setContentTransformationAmount(0, false /* isLastChild */);
         // It doesn't make sense to keep old animations around, lets cancel them!
-        ExpandableNotificationRow.NotificationViewState viewState = row.getViewState();
+        ExpandableViewState viewState = row.getViewState();
         if (viewState != null) {
             viewState.cancelAnimations(row);
             row.cancelAppearDrawing();
@@ -562,12 +562,10 @@
 
     /**
      * Update the state of all its children based on a linear layout algorithm.
-     *  @param resultState the state to update
      * @param parentState the state of the parent
      * @param ambientState
      */
-    public void getState(StackScrollState resultState, ExpandableViewState parentState,
-            AmbientState ambientState) {
+    public void updateState(ExpandableViewState parentState, AmbientState ambientState) {
         int childCount = mChildren.size();
         int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
         boolean firstChild = true;
@@ -605,7 +603,7 @@
                 firstChild = false;
             }
 
-            ExpandableViewState childState = resultState.getViewStateForView(child);
+            ExpandableViewState childState = child.getViewState();
             int intrinsicHeight = child.getIntrinsicHeight();
             childState.height = intrinsicHeight;
             childState.yTranslation = yPosition + launchTransitionCompensation;
@@ -639,7 +637,7 @@
         if (mOverflowNumber != null) {
             ExpandableNotificationRow overflowView = mChildren.get(Math.min(
                     getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
-            mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
+            mGroupOverFlowState.copyFrom(overflowView.getViewState());
 
             if (mContainingNotification.isOnAmbient()) {
                 mGroupOverFlowState.alpha = 0.0f;
@@ -724,7 +722,8 @@
         return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
     }
 
-    public void applyState(StackScrollState state) {
+    /** Applies state to children. */
+    public void applyState() {
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
         float expandFraction = 0.0f;
@@ -737,7 +736,7 @@
                 && !mHideDividersDuringExpand);
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
-            ExpandableViewState viewState = state.getViewStateForView(child);
+            ExpandableViewState viewState = child.getViewState();
             viewState.applyToView(child);
 
             // layout the divider
@@ -799,14 +798,14 @@
      * This is called when the children expansion has changed and positions the children properly
      * for an appear animation.
      *
-     * @param state the new state we animate to
      */
-    public void prepareExpansionChanged(StackScrollState state) {
+    public void prepareExpansionChanged() {
         // TODO: do something that makes sense, like placing the invisible views correctly
         return;
     }
 
-    public void startAnimationToState(StackScrollState state, AnimationProperties properties) {
+    /** Animate to a given state. */
+    public void startAnimationToState(AnimationProperties properties) {
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
         float expandFraction = getGroupExpandFraction();
@@ -816,7 +815,7 @@
                 && !mHideDividersDuringExpand);
         for (int i = childCount - 1; i >= 0; i--) {
             ExpandableNotificationRow child = mChildren.get(i);
-            ExpandableViewState viewState = state.getViewStateForView(child);
+            ExpandableViewState viewState = child.getViewState();
             viewState.animateTo(child, properties);
 
             // layout the divider
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 4d100a4..f0a2653 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -45,34 +45,31 @@
 
     /**
      * Change the position of child to a new location
-     *
-     * @param child the view to change the position for
+     *  @param child the view to change the position for
      * @param newIndex the new index
      */
-    void changeViewPosition(View child, int newIndex);
+    void changeViewPosition(ExpandableView child, int newIndex);
 
     /**
      * Called when a child was added to a group.
      *
      * @param row row of the group child that was added
      */
-    void notifyGroupChildAdded(View row);
+    void notifyGroupChildAdded(ExpandableView row);
 
     /**
      * Called when a child was removed from a group.
-     *
-     * @param row row of the child that was removed
+     *  @param row row of the child that was removed
      * @param childrenContainer ViewGroup of the group that the child was removed from
      */
-    void notifyGroupChildRemoved(View row, ViewGroup childrenContainer);
+    void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer);
 
     /**
      * Generate an animation for an added child view.
-     *
-     * @param child The view to be added.
+     *  @param child The view to be added.
      * @param fromMoreCard Whether this add is coming from the "more" card on lockscreen.
      */
-    void generateAddAnimation(View child, boolean fromMoreCard);
+    void generateAddAnimation(ExpandableView child, boolean fromMoreCard);
 
     /**
      * Generate a child order changed event.
@@ -118,9 +115,9 @@
     /**
      * Handle snapping a non-dismissable row back if the user tried to dismiss it.
      *
-     * @param row row to snap back
+     * @param entry the entry whose row needs to snap back
      */
-    void snapViewIfNeeded(ExpandableNotificationRow row);
+    void snapViewIfNeeded(NotificationData.Entry entry);
 
     /**
      * Get the view parent for a notification entry. For example, NotificationStackScrollLayout.
@@ -149,9 +146,9 @@
      * Called when a notification is removed from the shade. This cleans up the state for a
      * given view.
      *
-     * @param view view to clean up view state for
+     * @param entry the entry whose view's view state needs to be cleaned up (say that 5 times fast)
      */
-    void cleanUpViewState(View view);
+    void cleanUpViewStateForEntry(NotificationData.Entry entry);
 
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index c867a41..4f0831f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -16,13 +16,12 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
-        .NUM_SECTIONS;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
 
-import android.view.View;
-
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
 import java.util.HashSet;
@@ -37,7 +36,7 @@
     private ActivatableNotificationView[] mLastInSectionViews;
     private ActivatableNotificationView[] mTmpFirstInSectionViews;
     private ActivatableNotificationView[] mTmpLastInSectionViews;
-    private HashSet<View> mAnimatedChildren;
+    private HashSet<ExpandableView> mAnimatedChildren;
     private Runnable mRoundingChangedCallback;
     private ExpandableNotificationRow mTrackedHeadsUp;
     private float mAppearFraction;
@@ -50,13 +49,13 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
-        updateView(headsUp, false /* animate */);
+    public void onHeadsUpPinned(NotificationData.Entry headsUp) {
+        updateView(headsUp.getRow(), false /* animate */);
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
-        updateView(headsUp, true /* animate */);
+    public void onHeadsUpUnPinned(NotificationData.Entry headsUp) {
+        updateView(headsUp.getRow(), true /* animate */);
     }
 
     public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
@@ -211,7 +210,7 @@
         return anyChanged;
     }
 
-    public void setAnimatedChildren(HashSet<View> animatedChildren) {
+    public void setAnimatedChildren(HashSet<ExpandableView> animatedChildren) {
         mAnimatedChildren = animatedChildren;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ecd0d98..626e688 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -208,16 +208,12 @@
      */
     protected final StackScrollAlgorithm mStackScrollAlgorithm;
 
-    /**
-     * The current State this Layout is in
-     */
-    private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
     private final AmbientState mAmbientState;
     private NotificationGroupManager mGroupManager;
-    private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
+    private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
     private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
-    private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
-    private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
+    private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>();
+    private ArrayList<ExpandableView> mChildrenChangingPositions = new ArrayList<>();
     private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
     private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
     private ArrayList<View> mSwipedOutViews = new ArrayList<>();
@@ -275,7 +271,7 @@
     private boolean mDontReportNextOverScroll;
     private boolean mDontClampNextScroll;
     private boolean mNeedViewResizeAnimation;
-    private View mExpandedGroupView;
+    private ExpandableView mExpandedGroupView;
     private boolean mEverythingNeedsAnimation;
 
     /**
@@ -382,7 +378,7 @@
     private boolean mGroupExpandedForMeasure;
     private boolean mScrollable;
     private View mForcedScroll;
-    private View mNeedingPulseAnimation;
+    private ExpandableView mNeedingPulseAnimation;
 
     /**
      * @see #setDarkAmount(float, float)
@@ -600,12 +596,12 @@
             public void setRemoteInputActive(NotificationData.Entry entry,
                     boolean remoteInputActive) {
                 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
-                entry.row.notifyHeightChanged(true /* needsAnimation */);
+                entry.notifyHeightChanged(true /* needsAnimation */);
                 updateFooter();
             }
 
             public void lockScrollTo(NotificationData.Entry entry) {
-                NotificationStackScrollLayout.this.lockScrollTo(entry.row);
+                NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
             }
 
             public void requestDisallowLongPressAndDismiss() {
@@ -897,8 +893,10 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
-    public boolean isInVisibleLocation(ExpandableNotificationRow row) {
-        ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
+    public boolean isInVisibleLocation(NotificationData.Entry entry) {
+        ExpandableNotificationRow row = entry.getRow();
+        ExpandableViewState childViewState = row.getViewState();
+
         if (childViewState == null) {
             return false;
         }
@@ -944,7 +942,7 @@
                 ? 0
                 : mScroller.getCurrVelocity());
         mAmbientState.setScrollY(mOwnScrollY);
-        mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
+        mStackScrollAlgorithm.resetViewStates(mAmbientState);
         if (!isCurrentlyAnimating() && !mNeedsAnimation) {
             applyCurrentState();
         } else {
@@ -1213,12 +1211,12 @@
         if (topEntry == null) {
             return 0;
         }
-        ExpandableNotificationRow row = topEntry.row;
+        ExpandableNotificationRow row = topEntry.getRow();
         if (row.isChildInGroup()) {
-            final ExpandableNotificationRow groupSummary
+            final NotificationData.Entry groupSummary
                     = mGroupManager.getGroupSummary(row.getStatusBarNotification());
             if (groupSummary != null) {
-                row = groupSummary;
+                row = groupSummary.getRow();
             }
         }
         return row.getPinnedHeadsUpHeight();
@@ -1390,11 +1388,12 @@
                     && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                 if (slidingChild instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
+                    NotificationData.Entry entry = row.getEntry();
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
-                            && mHeadsUpManager.getTopEntry().row != row
+                            && mHeadsUpManager.getTopEntry().getRow() != row
                             && mGroupManager.getGroupSummary(
-                            mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
-                            != row) {
+                                mHeadsUpManager.getTopEntry().notification)
+                            != entry) {
                         continue;
                     }
                     return row.getViewAtPosition(touchY - childTop);
@@ -1524,7 +1523,8 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void snapViewIfNeeded(ExpandableNotificationRow child) {
+    public void snapViewIfNeeded(NotificationData.Entry entry) {
+        ExpandableNotificationRow child = entry.getRow();
         boolean animate = mIsExpanded || isPinnedHeadsUp(child);
         // If the child is showing the notification menu snap to that
         float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
@@ -1934,12 +1934,12 @@
      * @return the last child which has visibility unequal to GONE
      */
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    public View getLastChildNotGone() {
+    public ExpandableView getLastChildNotGone() {
         int childCount = getChildCount();
         for (int i = childCount - 1; i >= 0; i--) {
             View child = getChildAt(i);
             if (child.getVisibility() != View.GONE && child != mShelf) {
-                return child;
+                return (ExpandableView) child;
             }
         }
         return null;
@@ -2508,35 +2508,33 @@
         // we only call our internal methods if this is actually a removal and not just a
         // notification which becomes a child notification
         if (!mChildTransferInProgress) {
-            onViewRemovedInternal(child, this);
+            onViewRemovedInternal((ExpandableView) child, this);
         }
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     @Override
-    public void cleanUpViewState(View child) {
+    public void cleanUpViewStateForEntry(NotificationData.Entry entry) {
+        View child = entry.getRow();
         if (child == mSwipeHelper.getTranslatingParentView()) {
             mSwipeHelper.clearTranslatingParentView();
         }
-        mCurrentStackScrollState.removeViewStateForView(child);
     }
 
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    private void onViewRemovedInternal(View child, ViewGroup container) {
+    private void onViewRemovedInternal(ExpandableView child, ViewGroup container) {
         if (mChangePositionInProgress) {
             // This is only a position change, don't do anything special
             return;
         }
-        ExpandableView expandableView = (ExpandableView) child;
-        expandableView.setOnHeightChangedListener(null);
-        mCurrentStackScrollState.removeViewStateForView(child);
-        updateScrollStateForRemovedChild(expandableView);
+        child.setOnHeightChangedListener(null);
+        updateScrollStateForRemovedChild(child);
         boolean animationGenerated = generateRemoveAnimation(child);
         if (animationGenerated) {
             if (!mSwipedOutViews.contains(child)
-                    || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
+                    || Math.abs(child.getTranslation()) != child.getWidth()) {
                 container.addTransientView(child, 0);
-                expandableView.setTransientContainer(container);
+                child.setTransientContainer(container);
             }
         } else {
             mSwipedOutViews.remove(child);
@@ -2580,14 +2578,14 @@
      * @return Whether an animation was generated.
      */
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    private boolean generateRemoveAnimation(View child) {
+    private boolean generateRemoveAnimation(ExpandableView child) {
         if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
             mAddedHeadsUpChildren.remove(child);
             return false;
         }
         if (isClickedHeadsUp(child)) {
             // An animation is already running, add it transiently
-            mClearTransientViewsWhenFinished.add((ExpandableView) child);
+            mClearTransientViewsWhenFinished.add(child);
             return true;
         }
         if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
@@ -2644,9 +2642,9 @@
     private boolean isChildInInvisibleGroup(View child) {
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            ExpandableNotificationRow groupSummary =
+            NotificationData.Entry groupSummary =
                     mGroupManager.getGroupSummary(row.getStatusBarNotification());
-            if (groupSummary != null && groupSummary != row) {
+            if (groupSummary != null && groupSummary.getRow() != row) {
                 return row.getVisibility() == View.INVISIBLE;
             }
         }
@@ -2761,7 +2759,7 @@
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onViewAdded(View child) {
         super.onViewAdded(child);
-        onViewAddedInternal(child);
+        onViewAddedInternal((ExpandableView) child);
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -2831,31 +2829,28 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    private void onViewAddedInternal(View child) {
+    private void onViewAddedInternal(ExpandableView child) {
         updateHideSensitiveForChild(child);
-        ((ExpandableView) child).setOnHeightChangedListener(this);
+        child.setOnHeightChangedListener(this);
         generateAddAnimation(child, false /* fromMoreCard */);
         updateAnimationState(child);
         updateChronometerForChild(child);
     }
 
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    private void updateHideSensitiveForChild(View child) {
-        if (child instanceof ExpandableView) {
-            ExpandableView expandableView = (ExpandableView) child;
-            expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
-        }
+    private void updateHideSensitiveForChild(ExpandableView child) {
+        child.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
     }
 
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
+    public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {
         onViewRemovedInternal(row, childrenContainer);
     }
 
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void notifyGroupChildAdded(View row) {
+    public void notifyGroupChildAdded(ExpandableView row) {
         onViewAddedInternal(row);
     }
 
@@ -2927,7 +2922,7 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void generateAddAnimation(View child, boolean fromMoreCard) {
+    public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {
         if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
             // Generate Animations
             mChildrenToAddAnimated.add(child);
@@ -2944,7 +2939,7 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void changeViewPosition(View child, int newIndex) {
+    public void changeViewPosition(ExpandableView child, int newIndex) {
         int currentIndex = indexOfChild(child);
 
         if (currentIndex == -1) {
@@ -2983,8 +2978,7 @@
         }
         if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
             setAnimationRunning(true);
-            mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
-                    mGoToFullShadeDelay);
+            mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay);
             mAnimationEvents.clear();
             updateBackground();
             updateViewShadows();
@@ -3031,7 +3025,7 @@
                     continue;
                 }
             } else {
-                ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
+                ExpandableViewState viewState = row.getViewState();
                 if (viewState == null) {
                     // A view state was never generated for this view, so we don't need to animate
                     // this. This may happen with notification children.
@@ -3097,7 +3091,7 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateChildRemovalEvents() {
-        for (View child : mChildrenToRemoveAnimated) {
+        for (ExpandableView child : mChildrenToRemoveAnimated) {
             boolean childWasSwipedOut = mSwipedOutViews.contains(child);
 
             // we need to know the view after this one
@@ -3139,7 +3133,7 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generatePositionChangeEvents() {
-        for (View child : mChildrenChangingPositions) {
+        for (ExpandableView child : mChildrenChangingPositions) {
             mAnimationEvents.add(new AnimationEvent(child,
                     AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
         }
@@ -3153,7 +3147,7 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateChildAdditionEvents() {
-        for (View child : mChildrenToAddAnimated) {
+        for (ExpandableView child : mChildrenToAddAnimated) {
             if (mFromMoreCardAdditions.contains(child)) {
                 mAnimationEvents.add(new AnimationEvent(child,
                         AnimationEvent.ANIMATION_TYPE_ADD,
@@ -3245,7 +3239,7 @@
 
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
     protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
-        return new StackScrollAlgorithm(context);
+        return new StackScrollAlgorithm(context, this);
     }
 
     /**
@@ -3910,7 +3904,7 @@
             mStatusBar.resetUserExpandedStates();
             clearTemporaryViews();
             clearUserLockedViews();
-            ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
+            ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
             if (draggedViews.size() > 0) {
                 draggedViews.clear();
                 updateContinuousShadowDrawing();
@@ -4194,7 +4188,12 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void applyCurrentState() {
-        mCurrentStackScrollState.apply();
+        int numChildren = getChildCount();
+        for (int i = 0; i < numChildren; i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            child.applyViewState();
+        }
+
         if (mListener != null) {
             mListener.onChildLocationsChanged();
         }
@@ -4662,6 +4661,11 @@
         mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
     }
 
+    public void generateHeadsUpAnimation(NotificationData.Entry entry, boolean isHeadsUp) {
+        ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
+        generateHeadsUpAnimation(row, isHeadsUp);
+    }
+
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
         if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
@@ -4933,8 +4937,7 @@
             if (!(child instanceof ExpandableNotificationRow)) {
                 pw.println("  " + child.getClass().getSimpleName());
                 // Notifications dump it's viewstate as part of their dump to support children
-                ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(
-                        child);
+                ExpandableViewState viewState = child.getViewState();
                 if (viewState == null) {
                     pw.println("    no viewState!!!");
                 } else {
@@ -4951,7 +4954,7 @@
             ExpandableView child = (ExpandableView) getTransientView(i);
             child.dump(fd, pw, args);
         }
-        ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
+        ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
         int draggedCount = draggedViews.size();
         pw.println("  Dragged Views: " + draggedCount);
         for (int i = 0; i < draggedCount; i++) {
@@ -5487,7 +5490,7 @@
         static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
 
         final long eventStartTime;
-        final View changingView;
+        final ExpandableView mChangingView;
         final int animationType;
         final AnimationFilter filter;
         final long length;
@@ -5495,21 +5498,21 @@
         int darkAnimationOriginIndex;
         boolean headsUpFromBottom;
 
-        AnimationEvent(View view, int type) {
+        AnimationEvent(ExpandableView view, int type) {
             this(view, type, LENGTHS[type]);
         }
 
-        AnimationEvent(View view, int type, AnimationFilter filter) {
+        AnimationEvent(ExpandableView view, int type, AnimationFilter filter) {
             this(view, type, LENGTHS[type], filter);
         }
 
-        AnimationEvent(View view, int type, long length) {
+        AnimationEvent(ExpandableView view, int type, long length) {
             this(view, type, length, FILTERS[type]);
         }
 
-        AnimationEvent(View view, int type, long length, AnimationFilter filter) {
+        AnimationEvent(ExpandableView view, int type, long length, AnimationFilter filter) {
             eventStartTime = AnimationUtils.currentAnimationTimeMillis();
-            changingView = view;
+            mChangingView = view;
             animationType = type;
             this.length = length;
             this.filter = filter;
@@ -5692,7 +5695,7 @@
                         && (parent.areGutsExposed()
                         || mSwipeHelper.getExposedMenuView() == parent
                         || (parent.getNotificationChildren().size() == 1
-                        && parent.isClearable()))) {
+                        && parent.getEntry().isClearable()))) {
                     // In this case the group is expanded and showing the menu for the
                     // group, further interaction should apply to the group, not any
                     // child notifications so we use the parent of the child. We also do the same
@@ -5707,7 +5710,7 @@
         public void onBeginDrag(View v) {
             mFalsingManager.onNotificatonStartDismissing();
             setSwipingInProgress(true);
-            mAmbientState.onBeginDrag(v);
+            mAmbientState.onBeginDrag((ExpandableView) v);
             updateContinuousShadowDrawing();
             requestChildrenUpdate();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 87c361a..25fb7f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -42,6 +42,7 @@
 public class StackScrollAlgorithm {
 
     private static final String LOG_TAG = "StackScrollAlgorithm";
+    private final ViewGroup mHostView;
 
     private int mPaddingBetweenElements;
     private int mIncreasedPaddingBetweenElements;
@@ -55,7 +56,8 @@
     private float mHeadsUpInset;
     private int mPinnedZTranslationExtra;
 
-    public StackScrollAlgorithm(Context context) {
+    public StackScrollAlgorithm(Context context, ViewGroup hostView) {
+        mHostView = hostView;
         initView(context);
     }
 
@@ -79,49 +81,59 @@
         mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
     }
 
-    public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
+    /**
+     * Updates the state of all children in the hostview based on this algorithm.
+     */
+    public void resetViewStates(AmbientState ambientState) {
         // The state of the local variables are saved in an algorithmState to easily subdivide it
         // into multiple phases.
         StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
 
         // First we reset the view states to their default values.
-        resultState.resetViewStates();
+        resetChildViewStates();
 
-        initAlgorithmState(resultState, algorithmState, ambientState);
+        initAlgorithmState(mHostView, algorithmState, ambientState);
 
-        updatePositionsForState(resultState, algorithmState, ambientState);
+        updatePositionsForState(algorithmState, ambientState);
 
-        updateZValuesForState(resultState, algorithmState, ambientState);
+        updateZValuesForState(algorithmState, ambientState);
 
-        updateHeadsUpStates(resultState, algorithmState, ambientState);
+        updateHeadsUpStates(algorithmState, ambientState);
 
-        updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
-        updateClipping(resultState, algorithmState, ambientState);
-        updateSpeedBumpState(resultState, algorithmState, ambientState);
-        updateShelfState(resultState, ambientState);
-        getNotificationChildrenStates(resultState, algorithmState, ambientState);
+        updateDimmedActivatedHideSensitive(ambientState, algorithmState);
+        updateClipping(algorithmState, ambientState);
+        updateSpeedBumpState(algorithmState, ambientState);
+        updateShelfState(ambientState);
+        getNotificationChildrenStates(algorithmState, ambientState);
     }
 
-    private void getNotificationChildrenStates(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState,
+    private void resetChildViewStates() {
+        int numChildren = mHostView.getChildCount();
+        for (int i = 0; i < numChildren; i++) {
+            ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
+            child.resetViewState();
+        }
+    }
+
+    private void getNotificationChildrenStates(StackScrollAlgorithmState algorithmState,
             AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
             ExpandableView v = algorithmState.visibleChildren.get(i);
             if (v instanceof ExpandableNotificationRow) {
                 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-                row.getChildrenStates(resultState, ambientState);
+                row.updateChildrenStates(ambientState);
             }
         }
     }
 
-    private void updateSpeedBumpState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+    private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
         int belowSpeedBump = ambientState.getSpeedBumpIndex();
         for (int i = 0; i < childCount; i++) {
-            View child = algorithmState.visibleChildren.get(i);
-            ExpandableViewState childViewState = resultState.getViewStateForView(child);
+            ExpandableView child = algorithmState.visibleChildren.get(i);
+            ExpandableViewState childViewState = child.getViewState();
 
             // The speed bump can also be gone, so equality needs to be taken when comparing
             // indices.
@@ -129,15 +141,16 @@
         }
 
     }
-    private void updateShelfState(StackScrollState resultState, AmbientState ambientState) {
+
+    private void updateShelfState(AmbientState ambientState) {
         NotificationShelf shelf = ambientState.getShelf();
         if (shelf != null) {
-            shelf.updateState(resultState, ambientState);
+            shelf.updateState(ambientState);
         }
     }
 
-    private void updateClipping(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+    private void updateClipping(StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
         float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding()
                 + ambientState.getStackTranslation() + ambientState.getExpandAnimationTopChange()
                 : 0;
@@ -146,7 +159,7 @@
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
-            ExpandableViewState state = resultState.getViewStateForView(child);
+            ExpandableViewState state = child.getViewState();
             if (!child.mustStayOnScreen() || state.headsUpIsVisible) {
                 previousNotificationEnd = Math.max(drawStart, previousNotificationEnd);
                 previousNotificationStart = Math.max(drawStart, previousNotificationStart);
@@ -190,15 +203,15 @@
      * Updates the dimmed, activated and hiding sensitive states of the children.
      */
     private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
-            StackScrollState resultState, StackScrollAlgorithmState algorithmState) {
+            StackScrollAlgorithmState algorithmState) {
         boolean dimmed = ambientState.isDimmed();
         boolean dark = ambientState.isFullyDark();
         boolean hideSensitive = ambientState.isHideSensitive();
         View activatedChild = ambientState.getActivatedChild();
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
-            View child = algorithmState.visibleChildren.get(i);
-            ExpandableViewState childViewState = resultState.getViewStateForView(child);
+            ExpandableView child = algorithmState.visibleChildren.get(i);
+            ExpandableViewState childViewState = child.getViewState();
             childViewState.dimmed = dimmed;
             childViewState.dark = dark;
             childViewState.hideSensitive = hideSensitive;
@@ -212,7 +225,7 @@
     /**
      * Initialize the algorithm state like updating the visible children.
      */
-    private void initAlgorithmState(StackScrollState resultState, StackScrollAlgorithmState state,
+    private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state,
             AmbientState ambientState) {
         float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
 
@@ -224,7 +237,6 @@
         state.scrollY = (int) (scrollY + bottomOverScroll);
 
         //now init the visible children and update paddings
-        ViewGroup hostView = resultState.getHostView();
         int childCount = hostView.getChildCount();
         state.visibleChildren.clear();
         state.visibleChildren.ensureCapacity(childCount);
@@ -249,7 +261,7 @@
                     // we need normal padding now, to be in sync with what the stack calculates
                     lastView = null;
                 }
-                notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
+                notGoneIndex = updateNotGoneIndex(state, notGoneIndex, v);
                 float increasedPadding = v.getIncreasedPaddingAmount();
                 if (increasedPadding != 0.0f) {
                     state.paddingMap.put(v, increasedPadding);
@@ -282,13 +294,11 @@
                     ExpandableNotificationRow row = (ExpandableNotificationRow) v;
 
                     // handle the notgoneIndex for the children as well
-                    List<ExpandableNotificationRow> children =
-                            row.getNotificationChildren();
+                    List<ExpandableNotificationRow> children = row.getNotificationChildren();
                     if (row.isSummaryWithChildren() && children != null) {
                         for (ExpandableNotificationRow childRow : children) {
                             if (childRow.getVisibility() != View.GONE) {
-                                ExpandableViewState childState
-                                        = resultState.getViewStateForView(childRow);
+                                ExpandableViewState childState = childRow.getViewState();
                                 childState.notGoneIndex = notGoneIndex;
                                 notGoneIndex++;
                             }
@@ -301,8 +311,8 @@
         ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification();
         state.indexOfExpandingNotification = expandingNotification != null
                 ? expandingNotification.isChildInGroup()
-                    ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
-                    : state.visibleChildren.indexOf(expandingNotification)
+                ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
+                : state.visibleChildren.indexOf(expandingNotification)
                 : -1;
     }
 
@@ -322,10 +332,9 @@
         }
     }
 
-    private int updateNotGoneIndex(StackScrollState resultState,
-            StackScrollAlgorithmState state, int notGoneIndex,
+    private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex,
             ExpandableView v) {
-        ExpandableViewState viewState = resultState.getViewStateForView(v);
+        ExpandableViewState viewState = v.getViewState();
         viewState.notGoneIndex = notGoneIndex;
         state.visibleChildren.add(v);
         notGoneIndex++;
@@ -335,27 +344,27 @@
     /**
      * Determine the positions for the views. This is the main part of the algorithm.
      *
-     * @param resultState The result state to update if a change to the properties of a child occurs
      * @param algorithmState The state in which the current pass of the algorithm is currently in
-     * @param ambientState The current ambient state
+     * @param ambientState   The current ambient state
      */
-    private void updatePositionsForState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+    private void updatePositionsForState(StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
 
         // The y coordinate of the current child.
         float currentYPosition = -algorithmState.scrollY;
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
-            currentYPosition = updateChild(i, resultState, algorithmState, ambientState,
-                    currentYPosition);
+            currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
         }
     }
 
-    protected float updateChild(int i, StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState,
+    protected float updateChild(
+            int i,
+            StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState,
             float currentYPosition) {
         ExpandableView child = algorithmState.visibleChildren.get(i);
-        ExpandableViewState childViewState = resultState.getViewStateForView(child);
+        ExpandableViewState childViewState = child.getViewState();
         childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
         int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
         int childHeight = getMaxAllowedChildHeight(child);
@@ -404,8 +413,8 @@
         return algorithmState.getPaddingAfterChild(child);
     }
 
-    private void updateHeadsUpStates(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+    private void updateHeadsUpStates(StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
         ExpandableNotificationRow topHeadsUpEntry = null;
         for (int i = 0; i < childCount; i++) {
@@ -417,7 +426,7 @@
             if (!row.isHeadsUp()) {
                 break;
             }
-            ExpandableViewState childState = resultState.getViewStateForView(row);
+            ExpandableViewState childState = row.getViewState();
             if (topHeadsUpEntry == null && row.mustStayOnScreen() && !childState.headsUpIsVisible) {
                 topHeadsUpEntry = row;
                 childState.location = ExpandableViewState.LOCATION_FIRST_HUN;
@@ -439,7 +448,8 @@
                 childState.yTranslation = Math.max(childState.yTranslation, mHeadsUpInset);
                 childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
                 childState.hidden = false;
-                ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
+                ExpandableViewState topState =
+                        topHeadsUpEntry == null ? null : topHeadsUpEntry.getViewState();
                 if (topState != null && !isTopEntry && (!mIsExpanded
                         || unmodifiedEndLocation < topState.yTranslation + topState.height)) {
                     // Ensure that a headsUp doesn't vertically extend further than the heads-up at
@@ -491,9 +501,8 @@
      * Clamp the height of the child down such that its end is at most on the beginning of
      * the shelf.
      *
-     * @param child
      * @param childViewState the view state of the child
-     * @param ambientState the ambient state
+     * @param ambientState   the ambient state
      */
     private void clampPositionToShelf(ExpandableView child,
             ExpandableViewState childViewState,
@@ -521,31 +530,31 @@
             ExpandableView expandableView = (ExpandableView) child;
             return expandableView.getIntrinsicHeight();
         }
-        return child == null? mCollapsedSize : child.getHeight();
+        return child == null ? mCollapsedSize : child.getHeight();
     }
 
     /**
      * Calculate the Z positions for all children based on the number of items in both stacks and
      * save it in the resultState
-     *  @param resultState The result state to update the zTranslation values
+     *
      * @param algorithmState The state in which the current pass of the algorithm is currently in
-     * @param ambientState The ambient state of the algorithm
+     * @param ambientState   The ambient state of the algorithm
      */
-    private void updateZValuesForState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+    private void updateZValuesForState(StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
         float childrenOnTop = 0.0f;
         for (int i = childCount - 1; i >= 0; i--) {
             childrenOnTop = updateChildZValue(i, childrenOnTop,
-                    resultState, algorithmState, ambientState);
+                    algorithmState, ambientState);
         }
     }
 
     protected float updateChildZValue(int i, float childrenOnTop,
-            StackScrollState resultState, StackScrollAlgorithmState algorithmState,
+            StackScrollAlgorithmState algorithmState,
             AmbientState ambientState) {
         ExpandableView child = algorithmState.visibleChildren.get(i);
-        ExpandableViewState childViewState = resultState.getViewStateForView(child);
+        ExpandableViewState childViewState = child.getViewState();
         int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
         float baseZ = ambientState.getBaseZHeight();
         if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
deleted file mode 100644
index e55707c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification.stack;
-
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-
-import java.util.List;
-import java.util.WeakHashMap;
-
-/**
- * A state of a
- * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout} which
- * can be applied to a viewGroup.
- */
-public class StackScrollState {
-
-    private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
-
-    private final ViewGroup mHostView;
-    private WeakHashMap<ExpandableView, ExpandableViewState> mStateMap;
-
-    public StackScrollState(ViewGroup hostView) {
-        mHostView = hostView;
-        mStateMap = new WeakHashMap<>();
-    }
-
-    public ViewGroup getHostView() {
-        return mHostView;
-    }
-
-    public void resetViewStates() {
-        int numChildren = mHostView.getChildCount();
-        for (int i = 0; i < numChildren; i++) {
-            ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
-            resetViewState(child);
-
-            // handling reset for child notifications
-            if (child instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                List<ExpandableNotificationRow> children =
-                        row.getNotificationChildren();
-                if (row.isSummaryWithChildren() && children != null) {
-                    for (ExpandableNotificationRow childRow : children) {
-                        resetViewState(childRow);
-                    }
-                }
-            }
-        }
-    }
-
-    private void resetViewState(ExpandableView view) {
-        ExpandableViewState viewState = mStateMap.get(view);
-        if (viewState == null) {
-            viewState = view.createNewViewState(this);
-            mStateMap.put(view, viewState);
-        }
-        // initialize with the default values of the view
-        viewState.height = view.getIntrinsicHeight();
-        viewState.gone = view.getVisibility() == View.GONE;
-        viewState.alpha = 1f;
-        viewState.notGoneIndex = -1;
-        viewState.xTranslation = view.getTranslationX();
-        viewState.hidden = false;
-        viewState.scaleX = view.getScaleX();
-        viewState.scaleY = view.getScaleY();
-        viewState.inShelf = false;
-        viewState.headsUpIsVisible = false;
-    }
-
-    public ExpandableViewState getViewStateForView(View requestedView) {
-        return mStateMap.get(requestedView);
-    }
-
-    public void removeViewStateForView(View child) {
-        mStateMap.remove(child);
-    }
-
-    /**
-     * Apply the properties saved in {@link #mStateMap} to the children of the {@link #mHostView}.
-     * The properties are only applied if they effectively changed.
-     */
-    public void apply() {
-        int numChildren = mHostView.getChildCount();
-        for (int i = 0; i < numChildren; i++) {
-            ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
-            ExpandableViewState state = mStateMap.get(child);
-            if (state == null) {
-                Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
-                        "to the hostView");
-                continue;
-            }
-            if (state.gone) {
-                continue;
-            }
-            state.applyToView(child);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 34dab53..713bd90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -128,25 +128,25 @@
 
     public void startAnimationForEvents(
             ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
-            StackScrollState finalState, long additionalDelay) {
+            long additionalDelay) {
 
-        processAnimationEvents(mAnimationEvents, finalState);
+        processAnimationEvents(mAnimationEvents);
 
         int childCount = mHostLayout.getChildCount();
         mAnimationFilter.applyCombination(mNewEvents);
         mCurrentAdditionalDelay = additionalDelay;
         mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
-        mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState);
+        mCurrentLastNotAddedIndex = findLastNotAddedIndex();
         for (int i = 0; i < childCount; i++) {
             final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
 
-            ExpandableViewState viewState = finalState.getViewStateForView(child);
+            ExpandableViewState viewState = child.getViewState();
             if (viewState == null || child.getVisibility() == View.GONE
-                    || applyWithoutAnimation(child, viewState, finalState)) {
+                    || applyWithoutAnimation(child, viewState)) {
                 continue;
             }
 
-            initAnimationProperties(finalState, child, viewState);
+            initAnimationProperties(child, viewState);
             viewState.animateTo(child, mAnimationProperties);
         }
         if (!isRunning()) {
@@ -159,7 +159,7 @@
         mNewAddChildren.clear();
     }
 
-    private void initAnimationProperties(StackScrollState finalState, ExpandableView child,
+    private void initAnimationProperties(ExpandableView child,
             ExpandableViewState viewState) {
         boolean wasAdded = mAnimationProperties.wasAdded(child);
         mAnimationProperties.duration = mCurrentLength;
@@ -173,7 +173,7 @@
                         || viewState.clipTopAmount != child.getClipTopAmount()
                         || viewState.dark != child.isDark())) {
             mAnimationProperties.delay = mCurrentAdditionalDelay
-                    + calculateChildAnimationDelay(viewState, finalState);
+                    + calculateChildAnimationDelay(viewState);
         }
     }
 
@@ -193,8 +193,7 @@
      *
      * @return true if no animation should be performed
      */
-    private boolean applyWithoutAnimation(ExpandableView child, ExpandableViewState viewState,
-            StackScrollState finalState) {
+    private boolean applyWithoutAnimation(ExpandableView child, ExpandableViewState viewState) {
         if (mShadeExpanded) {
             return false;
         }
@@ -214,12 +213,12 @@
         return true;
     }
 
-    private int findLastNotAddedIndex(StackScrollState finalState) {
+    private int findLastNotAddedIndex() {
         int childCount = mHostLayout.getChildCount();
         for (int i = childCount - 1; i >= 0; i--) {
             final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
 
-            ExpandableViewState viewState = finalState.getViewStateForView(child);
+            ExpandableViewState viewState = child.getViewState();
             if (viewState == null || child.getVisibility() == View.GONE) {
                 continue;
             }
@@ -230,8 +229,7 @@
         return -1;
     }
 
-    private long calculateChildAnimationDelay(ExpandableViewState viewState,
-            StackScrollState finalState) {
+    private long calculateChildAnimationDelay(ExpandableViewState viewState) {
         if (mAnimationFilter.hasGoToFullShadeEvent) {
             return calculateDelayGoToFullShade(viewState);
         }
@@ -244,8 +242,8 @@
             switch (event.animationType) {
                 case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
                     int ownIndex = viewState.notGoneIndex;
-                    int changingIndex = finalState
-                            .getViewStateForView(event.changingView).notGoneIndex;
+                    int changingIndex =
+                            ((ExpandableView) (event.mChangingView)).getViewState().notGoneIndex;
                     int difference = Math.abs(ownIndex - changingIndex);
                     difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
                             difference - 1));
@@ -258,9 +256,9 @@
                 case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
                     int ownIndex = viewState.notGoneIndex;
                     boolean noNextView = event.viewAfterChangingView == null;
-                    View viewAfterChangingView = noNextView
+                    ExpandableView viewAfterChangingView = noNextView
                             ? mHostLayout.getLastChildNotGone()
-                            : event.viewAfterChangingView;
+                            : (ExpandableView) event.viewAfterChangingView;
                     if (viewAfterChangingView == null) {
                         // This can happen when the last view in the list is removed.
                         // Since the shelf is still around and the only view, the code still goes
@@ -268,8 +266,7 @@
                         // have changed.
                         continue;
                     }
-                    int nextIndex = finalState
-                            .getViewStateForView(viewAfterChangingView).notGoneIndex;
+                    int nextIndex = viewAfterChangingView.getViewState().notGoneIndex;
                     if (ownIndex >= nextIndex) {
                         // we only have the view afterwards
                         ownIndex++;
@@ -351,20 +348,17 @@
     /**
      * Process the animationEvents for a new animation
      *
-     * @param animationEvents the animation events for the animation to perform
-     * @param finalState the final state to animate to
+     *  @param animationEvents the animation events for the animation to perform
      */
     private void processAnimationEvents(
-            ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
-            StackScrollState finalState) {
+            ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents) {
         for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
-            final ExpandableView changingView = (ExpandableView) event.changingView;
+            final ExpandableView changingView = (ExpandableView) event.mChangingView;
             if (event.animationType ==
                     NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
 
                 // This item is added, initialize it's properties.
-                ExpandableViewState viewState = finalState
-                        .getViewStateForView(changingView);
+                ExpandableViewState viewState = changingView.getViewState();
                 if (viewState == null || viewState.gone) {
                     // The position for this child was never generated, let's continue.
                     continue;
@@ -381,8 +375,8 @@
 
                 // Find the amount to translate up. This is needed in order to understand the
                 // direction of the remove animation (either downwards or upwards)
-                ExpandableViewState viewState = finalState
-                        .getViewStateForView(event.viewAfterChangingView);
+                ExpandableViewState viewState =
+                        ((ExpandableView) event.viewAfterChangingView).getViewState();
                 int actualHeight = changingView.getActualHeight();
                 // upwards by default
                 float translationDirection = -1.0f;
@@ -426,11 +420,11 @@
                 }
             } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
-                row.prepareExpansionChanged(finalState);
+                ExpandableNotificationRow row = (ExpandableNotificationRow) event.mChangingView;
+                row.prepareExpansionChanged();
             } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR) {
-                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+                ExpandableViewState viewState = changingView.getViewState();
                 if (viewState != null) {
                     mTmpState.copyFrom(viewState);
                     mTmpState.yTranslation += mPulsingAppearingTranslation;
@@ -439,7 +433,7 @@
                 }
             } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
-                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+                ExpandableViewState viewState = changingView.getViewState();
                 if (viewState != null) {
                     viewState.alpha = 0;
                     // We want to animate the alpha away before the view starts translating,
@@ -454,7 +448,7 @@
             } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
                 // This item is added, initialize it's properties.
-                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+                ExpandableViewState viewState = changingView.getViewState();
                 mTmpState.copyFrom(viewState);
                 if (event.headsUpFromBottom) {
                     mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 40f9f45..3c8cad7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -143,9 +143,9 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
         updateTopEntry();
-        updateHeader(headsUp.getEntry());
+        updateHeader(entry);
     }
 
     /** To count the distance from the window right boundary to scroller right boundary. The
@@ -298,9 +298,9 @@
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
         updateTopEntry();
-        updateHeader(headsUp.getEntry());
+        updateHeader(entry);
     }
 
     public void setExpandedHeight(float expandedHeight, float appearFraction) {
@@ -339,7 +339,7 @@
     }
 
     public void updateHeader(NotificationData.Entry entry) {
-        ExpandableNotificationRow row = entry.row;
+        ExpandableNotificationRow row = entry.getRow();
         float headerVisibleAmount = 1.0f;
         if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
             headerVisibleAmount = mExpandFraction;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 00d6b14..9faada0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -304,18 +304,19 @@
             return;
         }
         if (hasPinnedHeadsUp()) {
-            ExpandableNotificationRow topEntry = getTopEntry().row;
+            NotificationData.Entry topEntry = getTopEntry();
             if (topEntry.isChildInGroup()) {
-                final ExpandableNotificationRow groupSummary
-                        = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+                final NotificationData.Entry groupSummary
+                        = mGroupManager.getGroupSummary(topEntry.notification);
                 if (groupSummary != null) {
                     topEntry = groupSummary;
                 }
             }
-            topEntry.getLocationOnScreen(mTmpTwoArray);
+            ExpandableNotificationRow topRow = topEntry.getRow();
+            topRow.getLocationOnScreen(mTmpTwoArray);
             int minX = mTmpTwoArray[0];
-            int maxX = mTmpTwoArray[0] + topEntry.getWidth();
-            int height = topEntry.getIntrinsicHeight();
+            int maxX = mTmpTwoArray[0] + topRow.getWidth();
+            int height = topRow.getIntrinsicHeight();
 
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
             info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index be4df45..9c1c71a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -84,8 +84,8 @@
                     // We might touch above the visible heads up child, but then we still would
                     // like to capture it.
                     NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
-                    if (topEntry != null && topEntry.row.isPinned()) {
-                        mPickedChild = topEntry.row;
+                    if (topEntry != null && topEntry.isRowPinned()) {
+                        mPickedChild = topEntry.getRow();
                         mTouchingHeadsUpView = true;
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index c0d1818..7d13679 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -227,6 +227,7 @@
             mShowingSoon = false;
             if (mExpansion == EXPANSION_VISIBLE) {
                 mKeyguardView.onResume();
+                mKeyguardView.resetSecurityContainer();
             }
             StatsLog.write(StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
                 StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index d8280ba..a81b7e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -22,6 +22,7 @@
 import android.content.res.Resources;
 import android.util.MathUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -229,6 +230,11 @@
                 - mBurnInPreventionOffsetX;
     }
 
+    @VisibleForTesting
+    void setPulsingPadding(int padding) {
+        mPulsingPadding = padding;
+    }
+
     public static class Result {
 
         /**
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 1002f9e..b83ebc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
-
 import android.annotation.NonNull;
 import android.hardware.input.InputManager;
 import android.os.Handler;
@@ -35,10 +33,8 @@
  */
 public class NavigationBackAction extends NavigationGestureAction {
 
-    private static final String PULL_HOME_GO_BACK_PROP = "quickstepcontroller_homegoesback";
     private static final String BACK_AFTER_END_PROP =
             "quickstepcontroller_homegoesbackwhenend";
-    private static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
     private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
     private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
 
@@ -60,23 +56,13 @@
     }
 
     @Override
-    public int requiresTouchDownHitTarget() {
-        return HIT_TARGET_HOME;
-    }
-
-    @Override
-    public boolean requiresDragWithHitTarget() {
-        return true;
-    }
-
-    @Override
     public boolean canPerformAction() {
         return mProxySender.getBackButtonAlpha() > 0;
     }
 
     @Override
     public boolean isEnabled() {
-        return swipeHomeGoBackGestureEnabled();
+        return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED);
     }
 
     @Override
@@ -110,13 +96,8 @@
         mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
     }
 
-    private boolean swipeHomeGoBackGestureEnabled() {
-        return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED)
-                && getGlobalBoolean(PULL_HOME_GO_BACK_PROP);
-    }
-
     private boolean shouldExecuteBackOnUp() {
-        return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED)
+        return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED)
                 && getGlobalBoolean(BACK_AFTER_END_PROP);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 0cf1b3d..8657003 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -56,7 +56,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Display;
-import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -863,16 +862,9 @@
 
     public static View create(Context context, FragmentListener listener) {
         final int displayId = context.getDisplay().getDisplayId();
-        final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
-        final int height = isDefaultDisplay
-                ? LayoutParams.MATCH_PARENT
-                : context.getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, height,
-                // TODO(b/117478341): Resolve one status bar/ navigation bar assumption
-                isDefaultDisplay
-                        ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
-                        : WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
                 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -884,10 +876,6 @@
         lp.setTitle("NavigationBar" + displayId);
         lp.accessibilityTitle = context.getString(R.string.nav_bar);
         lp.windowAnimations = 0;
-        if (!isDefaultDisplay) {
-            lp.flags |= LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
-            lp.gravity = Gravity.BOTTOM;
-        }
 
         View navigationBarView = LayoutInflater.from(context).inflate(
                 R.layout.navigation_bar_window, null);
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 5db43ea..33d022c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -77,6 +77,8 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.NavigationBarCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.phone.NavigationPrototypeController.GestureAction;
+import com.android.systemui.statusbar.phone.NavigationPrototypeController.OnPrototypeChangedListener;
 import com.android.systemui.statusbar.policy.DeadZone;
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 
@@ -146,6 +148,8 @@
     private RecentsOnboarding mRecentsOnboarding;
     private NotificationPanelView mPanelView;
 
+    private NavigationPrototypeController mPrototypeController;
+    private NavigationGestureAction[] mDefaultGestureMap;
     private QuickScrubAction mQuickScrubAction;
     private QuickStepAction mQuickStepAction;
     private NavigationBackAction mBackAction;
@@ -261,6 +265,18 @@
         }
     };
 
+    private OnPrototypeChangedListener mPrototypeListener = new OnPrototypeChangedListener() {
+        @Override
+        public void onGestureRemap(int[] actions) {
+            updateNavigationGestures();
+        }
+
+        @Override
+        public void onBackButtonVisibilityChanged(boolean visible) {
+            getBackButton().setVisibility(visible ? VISIBLE : GONE);
+        }
+    };
+
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -309,6 +325,14 @@
         mQuickScrubAction = new QuickScrubAction(this, mOverviewProxyService);
         mQuickStepAction = new QuickStepAction(this, mOverviewProxyService);
         mBackAction = new NavigationBackAction(this, mOverviewProxyService);
+        mDefaultGestureMap = new NavigationGestureAction[] {
+                mQuickStepAction, null /* swipeDownAction*/, null /* swipeLeftAction */,
+                mQuickScrubAction
+        };
+
+        mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
+        mPrototypeController.register();
+        mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
     }
 
     public BarTransitions getBarTransitions() {
@@ -323,8 +347,32 @@
         mPanelView = panel;
         if (mGestureHelper instanceof QuickStepController) {
             ((QuickStepController) mGestureHelper).setComponents(this);
-            ((QuickStepController) mGestureHelper).setGestureActions(mQuickStepAction,
-                    null /* swipeDownAction*/, mBackAction, mQuickScrubAction);
+            updateNavigationGestures();
+        }
+    }
+
+    private void updateNavigationGestures() {
+        if (mGestureHelper instanceof QuickStepController) {
+            final int[] assignedMap = mPrototypeController.getGestureActionMap();
+            ((QuickStepController) mGestureHelper).setGestureActions(
+                    getNavigationActionFromType(assignedMap[0], mDefaultGestureMap[0]),
+                    getNavigationActionFromType(assignedMap[1], mDefaultGestureMap[1]),
+                    getNavigationActionFromType(assignedMap[2], mDefaultGestureMap[2]),
+                    getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3]));
+        }
+    }
+
+    private NavigationGestureAction getNavigationActionFromType(@GestureAction int actionType,
+            NavigationGestureAction defaultAction) {
+        switch(actionType) {
+            case NavigationPrototypeController.ACTION_QUICKSTEP:
+                return mQuickStepAction;
+            case NavigationPrototypeController.ACTION_QUICKSCRUB:
+                return mQuickScrubAction;
+            case NavigationPrototypeController.ACTION_BACK:
+                return mBackAction;
+            default:
+                return defaultAction;
         }
     }
 
@@ -1043,6 +1091,7 @@
         if (mGestureHelper != null) {
             mGestureHelper.destroy();
         }
+        mPrototypeController.unregister();
         setUpSwipeUpOnboarding(false);
         for (int i = 0; i < mButtonDispatchers.size(); ++i) {
             mButtonDispatchers.valueAt(i).onDestroy();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
new file mode 100644
index 0000000..e8c0bf1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+
+import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Coordinates with the prototype settings plugin app that uses Settings.Global to allow different
+ * prototypes to run in the system. The class will handle communication changes from the settings
+ * app and call back to listeners.
+ */
+public class NavigationPrototypeController extends ContentObserver {
+    private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";
+
+    static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
+    private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK})
+    @interface GestureAction {}
+    static final int ACTION_DEFAULT = 0;
+    static final int ACTION_QUICKSTEP = 1;
+    static final int ACTION_QUICKSCRUB = 2;
+    static final int ACTION_BACK = 3;
+
+    private OnPrototypeChangedListener mListener;
+
+    /**
+     * Each index corresponds to a different action set in QuickStepController
+     * {@see updateSwipeLTRBackSetting}
+     */
+    private int[] mActionMap = new int[4];
+
+    private final Context mContext;
+
+    public NavigationPrototypeController(Handler handler, Context context) {
+        super(handler);
+        mContext = context;
+        updateSwipeLTRBackSetting();
+    }
+
+    public void setOnPrototypeChangedListener(OnPrototypeChangedListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Observe all the settings to react to from prototype settings
+     */
+    public void register() {
+        registerObserver(HIDE_BACK_BUTTON_SETTING);
+        registerObserver(GESTURE_MATCH_SETTING);
+    }
+
+    /**
+     * Disable observing settings to react to from prototype settings
+     */
+    public void unregister() {
+        mContext.getContentResolver().unregisterContentObserver(this);
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        super.onChange(selfChange, uri);
+        if (!selfChange && mListener != null) {
+            try {
+                final String path = uri.getPath();
+                if (path.endsWith(GESTURE_MATCH_SETTING)) {
+                    // Get the settings gesture map corresponding to each action
+                    // {@see updateSwipeLTRBackSetting}
+                    updateSwipeLTRBackSetting();
+                    mListener.onGestureRemap(mActionMap);
+                } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
+                    mListener.onBackButtonVisibilityChanged(
+                            !getGlobalBool(HIDE_BACK_BUTTON_SETTING));
+                }
+            } catch (SettingNotFoundException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * Retrieve the action map to apply to the quick step controller
+     * @return an action map
+     */
+    int[] getGestureActionMap() {
+        return mActionMap;
+    }
+
+    /**
+     * Since Settings.Global cannot pass arrays, use a string to represent each character as a
+     * gesture map to actions corresponding to {@see GestureAction}. The number is represented as:
+     * Number: [up] [down] [left] [right]
+     */
+    private void updateSwipeLTRBackSetting() {
+        String value = Settings.Global.getString(mContext.getContentResolver(),
+                GESTURE_MATCH_SETTING);
+        if (value != null) {
+            for (int i = 0; i < mActionMap.length; ++i) {
+                mActionMap[i] = Character.getNumericValue(value.charAt(i));
+            }
+        }
+    }
+
+    private boolean getGlobalBool(String name) throws SettingNotFoundException {
+        return Settings.Global.getInt(mContext.getContentResolver(), name) == 1;
+    }
+
+    private void registerObserver(String name) {
+        mContext.getContentResolver()
+                .registerContentObserver(Settings.Global.getUriFor(name), false, this);
+    }
+
+    public interface OnPrototypeChangedListener {
+        void onGestureRemap(@GestureAction int[] actions);
+        void onBackButtonVisibilityChanged(boolean visible);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index c74514e..3e31fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -198,7 +198,7 @@
                 alertNotificationWhenPossible(entry, getActiveAlertManager());
             } else {
                 // The transfer is no longer valid. Free the content.
-                entry.row.freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
+                entry.getRow().freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
             }
         }
     }
@@ -299,9 +299,9 @@
 
         Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
         if (child != null) {
-            if (child.row.keepInParent()
-                    || child.row.isRemoved()
-                    || child.row.isDismissed()) {
+            if (child.getRow().keepInParent()
+                    || child.isRowRemoved()
+                    || child.isRowDismissed()) {
                 // The notification is actually already removed. No need to alert it.
                 return;
             }
@@ -390,10 +390,10 @@
     private void alertNotificationWhenPossible(@NonNull Entry entry,
             @NonNull AlertingNotificationManager alertManager) {
         @InflationFlag int contentFlag = alertManager.getContentFlag();
-        if (!entry.row.isInflationFlagSet(contentFlag)) {
+        if (!entry.getRow().isInflationFlagSet(contentFlag)) {
             mPendingAlerts.put(entry.key, new PendingAlertInfo(entry, alertManager));
-            entry.row.updateInflationFlag(contentFlag, true /* shouldInflate */);
-            entry.row.inflateViews();
+            entry.getRow().updateInflationFlag(contentFlag, true /* shouldInflate */);
+            entry.getRow().inflateViews();
             return;
         }
         if (alertManager.isAlerting(entry.key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 8ceabf8..448b5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -87,8 +87,7 @@
         group.expanded = expanded;
         if (group.summary != null) {
             for (OnGroupChangeListener listener : mListeners) {
-                listener.onGroupExpansionChanged(group.summary.row,
-                        expanded);
+                listener.onGroupExpansionChanged(group.summary.getRow(), expanded);
             }
         }
     }
@@ -133,7 +132,7 @@
     }
 
     public void onEntryAdded(final NotificationData.Entry added) {
-        if (added.row.isRemoved()) {
+        if (added.isRowRemoved()) {
             added.setDebugThrowable(new Throwable());
         }
         final StatusBarNotification sbn = added.notification;
@@ -152,17 +151,17 @@
             if (existing != null && existing != added) {
                 Throwable existingThrowable = existing.getDebugThrowable();
                 Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key
-                        + "existing removed: " + existing.row.isRemoved()
+                        + "existing removed: " + existing.isRowRemoved()
                         + (existingThrowable != null
                                 ? Log.getStackTraceString(existingThrowable) + "\n": "")
-                        + " added removed" + added.row.isRemoved()
+                        + " added removed" + added.isRowRemoved()
                         , new Throwable());
             }
             group.children.put(added.key, added);
             updateSuppression(group);
         } else {
             group.summary = added;
-            group.expanded = added.row.areChildrenExpanded();
+            group.expanded = added.areChildrenExpanded();
             updateSuppression(group);
             if (!group.children.isEmpty()) {
                 ArrayList<NotificationData.Entry> childrenCopy
@@ -263,9 +262,9 @@
         if (!isOnlyChild(sbn)) {
             return false;
         }
-        ExpandableNotificationRow logicalGroupSummary = getLogicalGroupSummary(sbn);
+        NotificationData.Entry logicalGroupSummary = getLogicalGroupSummary(sbn);
         return logicalGroupSummary != null
-                && !logicalGroupSummary.getStatusBarNotification().equals(sbn);
+                && !logicalGroupSummary.notification.equals(sbn);
     }
 
     private int getTotalNumberOfChildren(StatusBarNotification sbn) {
@@ -339,7 +338,7 @@
      * Get the summary of a specified status bar notification. For isolated notification this return
      * itself.
      */
-    public ExpandableNotificationRow getGroupSummary(StatusBarNotification sbn) {
+    public NotificationData.Entry getGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(getGroupKey(sbn));
     }
 
@@ -348,16 +347,17 @@
      * but the logical summary, i.e when a child is isolated, it still returns the summary as if
      * it wasn't isolated.
      */
-    public ExpandableNotificationRow getLogicalGroupSummary(StatusBarNotification sbn) {
+    public NotificationData.Entry getLogicalGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(sbn.getGroupKey());
     }
 
     @Nullable
-    private ExpandableNotificationRow getGroupSummary(String groupKey) {
+    private NotificationData.Entry getGroupSummary(String groupKey) {
         NotificationGroup group = mGroupMap.get(groupKey);
+        //TODO: see if this can become an Entry
         return group == null ? null
                 : group.summary == null ? null
-                        : group.summary.row;
+                        : group.summary;
     }
 
     /**
@@ -438,11 +438,11 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
     }
 
     @Override
@@ -533,8 +533,7 @@
 
     private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
         return notificationGroup.summary == null
-                || notificationGroup.summary.row.getClipTopAmount() > 0
-                || notificationGroup.summary.row.getTranslationY() < 0;
+                || notificationGroup.summary.isGroupNotFullyVisible();
     }
 
     public void setHeadsUpManager(HeadsUpManager headsUpManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 184766c..2d5d562 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -192,13 +192,13 @@
                 && !mEntryManager.getNotificationData().isHighPriority(entry.notification)) {
             return false;
         }
-        if (!StatusBar.isTopLevelChild(entry)) {
+        if (!entry.isTopLevelChild()) {
             return false;
         }
-        if (entry.row.getVisibility() == View.GONE) {
+        if (entry.getRow().getVisibility() == View.GONE) {
             return false;
         }
-        if (entry.row.isDismissed() && hideDismissed) {
+        if (entry.isRowDismissed() && hideDismissed) {
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 851e6d0..33d176a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2500,25 +2500,26 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
-        mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true);
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
+        mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
 
         // When we're unpinning the notification via active edge they remain heads-upped,
         // we need to make sure that an animation happens in this case, otherwise the notification
         // will stick to the top without any interaction.
-        if (isFullyCollapsed() && headsUp.isHeadsUp()) {
-            mNotificationStackScroller.generateHeadsUpAnimation(headsUp, false);
-            headsUp.setHeadsUpIsVisible();
+        if (isFullyCollapsed() && entry.isRowHeadsUp()) {
+            mNotificationStackScroller.generateHeadsUpAnimation(
+                    entry.getHeadsUpAnimationView(), false);
+            entry.setHeadsUpIsVisible();
         }
     }
 
     @Override
     public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
-        mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp);
+        mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
     }
 
     @Override
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 0eff4d4..497fdfb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -30,9 +30,9 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Rect;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.Log;
@@ -100,6 +100,7 @@
     private NavigationGestureAction mCurrentAction;
     private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];
 
+    private final Rect mLastLayoutRect = new Rect();
     private final OverviewProxyService mOverviewEventSender;
     private final Context mContext;
     private final StatusBar mStatusBar;
@@ -107,7 +108,6 @@
     private final Matrix mTransformLocalMatrix = new Matrix();
 
     public QuickStepController(Context context) {
-        final Resources res = context.getResources();
         mContext = context;
         mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
         mOverviewEventSender = Dependency.get(OverviewProxyService.class);
@@ -142,6 +142,8 @@
             if (action != null) {
                 action.setBarState(true, mNavBarPosition, mDragHPositive, mDragVPositive);
                 action.onDarkIntensityChange(mDarkIntensity);
+                action.onLayout(true /* changed */, mLastLayoutRect.left, mLastLayoutRect.top,
+                        mLastLayoutRect.right, mLastLayoutRect.bottom);
             }
         }
     }
@@ -382,6 +384,7 @@
                 action.onLayout(changed, left, top, right, bottom);
             }
         }
+        mLastLayoutRect.set(left, top, right, bottom);
     }
 
     @Override
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 bdddf5b..2fc3adc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -22,6 +22,7 @@
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
@@ -195,7 +196,6 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -484,7 +484,7 @@
 
     private Runnable mLaunchTransitionEndRunnable;
     protected boolean mLaunchTransitionFadingAway;
-    private ExpandableNotificationRow mDraggedDownRow;
+    private NotificationData.Entry mDraggedDownEntry;
     private boolean mLaunchCameraOnScreenTurningOn;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
     private int mLastCameraLaunchSource;
@@ -843,16 +843,7 @@
         mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
         putComponent(HeadsUpManager.class, mHeadsUpManager);
 
-
-        try {
-            boolean showNav = mWindowManagerService.hasNavigationBar();
-            if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
-            if (showNav) {
-                createNavigationBar();
-            }
-        } catch (RemoteException ex) {
-            // no window manager? good luck with that
-        }
+        createNavigationBar();
 
         if (ENABLE_LOCKSCREEN_WALLPAPER) {
             mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
@@ -1061,13 +1052,24 @@
     }
 
     protected void createNavigationBar() {
-        mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
-            mNavigationBar = (NavigationBarFragment) fragment;
-            if (mLightBarController != null) {
-                mNavigationBar.setLightBarController(mLightBarController);
-            }
-            mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
-        });
+        try {
+            // TODO(117478341): Move this into DisplayNavigationBarController#createNavigationBars
+            // for-loop. We will also move the whole navigation bar logic together.
+            final boolean showNav = mWindowManagerService.hasNavigationBar(DEFAULT_DISPLAY);
+            if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
+            if (!showNav) return;
+
+            mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
+                mNavigationBar = (NavigationBarFragment) fragment;
+                if (mLightBarController != null) {
+                    mNavigationBar.setLightBarController(mLightBarController);
+                }
+                mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
+            });
+        } catch (RemoteException ex) {
+            // no window manager? good luck with that
+        }
+        mNavigationBarController.createNavigationBars();
     }
 
     /**
@@ -1265,10 +1267,6 @@
         mQSPanel.clickTile(tile);
     }
 
-    public static boolean isTopLevelChild(Entry entry) {
-        return entry.row.getParent() instanceof NotificationStackScrollLayout;
-    }
-
     public boolean areNotificationsHidden() {
         return mZenController.areNotificationsHiddenInShade();
     }
@@ -1491,12 +1489,12 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
         dismissVolumeDialog();
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
     }
 
     @Override
@@ -2605,9 +2603,7 @@
         final int notificationCount = activeNotifications.size();
         for (int i = 0; i < notificationCount; i++) {
             NotificationData.Entry entry = activeNotifications.get(i);
-            if (entry.row != null) {
-                entry.row.resetUserExpansion();
-            }
+            entry.resetUserExpansion();
         }
     }
 
@@ -3038,10 +3034,10 @@
             mStatusBarStateController.setState(StatusBarState.KEYGUARD);
         }
         updatePanelExpansionForKeyguard();
-        if (mDraggedDownRow != null) {
-            mDraggedDownRow.setUserLocked(false);
-            mDraggedDownRow.notifyHeightChanged(false  /* needsAnimation */);
-            mDraggedDownRow = null;
+        if (mDraggedDownEntry != null) {
+            mDraggedDownEntry.setUserLocked(false);
+            mDraggedDownEntry.notifyHeightChanged(false /* needsAnimation */);
+            mDraggedDownEntry = null;
         }
     }
 
@@ -3181,9 +3177,9 @@
             }
             long delay = mKeyguardMonitor.calculateGoingToFullShadeDelay();
             mNotificationPanel.animateToFullShade(delay);
-            if (mDraggedDownRow != null) {
-                mDraggedDownRow.setUserLocked(false);
-                mDraggedDownRow = null;
+            if (mDraggedDownEntry != null) {
+                mDraggedDownEntry.setUserLocked(false);
+                mDraggedDownEntry = null;
             }
 
             // TODO(115978725): Support animations on external nav bars.
@@ -3575,14 +3571,15 @@
 
         int userId = mLockscreenUserManager.getCurrentUserId();
         ExpandableNotificationRow row = null;
+        NotificationData.Entry entry = null;
         if (expandView instanceof ExpandableNotificationRow) {
-            row = (ExpandableNotificationRow) expandView;
-            row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
+            entry = ((ExpandableNotificationRow) expandView).getEntry();
+            entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
             // Indicate that the group expansion is changing at this time -- this way the group
             // and children backgrounds / divider animations will look correct.
-            row.setGroupExpansionChanging(true);
-            if (row.getStatusBarNotification() != null) {
-                userId = row.getStatusBarNotification().getUserId();
+            entry.setGroupExpansionChanging(true);
+            if (entry.notification != null) {
+                userId = entry.notification.getUserId();
             }
         }
         boolean fullShadeNeedsBouncer = !mLockscreenUserManager.
@@ -3592,7 +3589,7 @@
         if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
             mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
             showBouncerIfKeyguard();
-            mDraggedDownRow = row;
+            mDraggedDownEntry = entry;
             mPendingRemoteInputView = null;
         } else {
             mNotificationPanel.animateToFullShade(0 /* delay */);
@@ -4326,7 +4323,14 @@
         }, afterKeyguardGone);
     }
 
+    @Override
     public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
+        startPendingIntentDismissingKeyguard(intent, null);
+    }
+
+    @Override
+    public void startPendingIntentDismissingKeyguard(
+            final PendingIntent intent, @Nullable final Runnable intentSentCallback) {
         final boolean afterKeyguardGone = intent.isActivity()
                 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
@@ -4345,6 +4349,9 @@
             if (intent.isActivity()) {
                 mAssistManager.hideAssist();
             }
+            if (intentSentCallback != null) {
+                intentSentCallback.run();
+            }
         }, afterKeyguardGone);
     }
 
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 edfc049..588c3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -418,7 +418,7 @@
             StatusBarNotification parentToCancel = null;
             if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
                 StatusBarNotification summarySbn =
-                        mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification();
+                        mGroupManager.getLogicalGroupSummary(sbn).notification;
                 if (shouldAutoCancel(summarySbn)) {
                     parentToCancel = summarySbn;
                 }
@@ -591,7 +591,7 @@
     public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
         mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
         if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
-            mShadeController.goToLockedShade(clickedEntry.row);
+            mShadeController.goToLockedShade(clickedEntry.getRow());
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index fdab616..e7280643 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -123,15 +123,15 @@
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "setEntryPinned: " + isPinned);
         }
-        ExpandableNotificationRow row = headsUpEntry.mEntry.row;
-        if (row.isPinned() != isPinned) {
-            row.setPinned(isPinned);
+        NotificationData.Entry entry = headsUpEntry.mEntry;
+        if (entry.isRowPinned() != isPinned) {
+            entry.setRowPinned(isPinned);
             updatePinnedMode();
             for (OnHeadsUpChangedListener listener : mListeners) {
                 if (isPinned) {
-                    listener.onHeadsUpPinned(row);
+                    listener.onHeadsUpPinned(entry);
                 } else {
-                    listener.onHeadsUpUnPinned(row);
+                    listener.onHeadsUpUnPinned(entry);
                 }
             }
         }
@@ -144,7 +144,7 @@
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setHeadsUp(true);
+        entry.setHeadsUp(true);
         setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, true);
@@ -154,12 +154,12 @@
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setHeadsUp(false);
+        entry.setHeadsUp(false);
         setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, false);
         }
-        entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+        entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
     }
 
     protected void updatePinnedMode() {
@@ -282,7 +282,7 @@
     private boolean hasPinnedNotificationInternal() {
         for (String key : mAlertEntries.keySet()) {
             AlertEntry entry = getHeadsUpEntry(key);
-            if (entry.mEntry.row.isPinned()) {
+            if (entry.mEntry.isRowPinned()) {
                 return true;
             }
         }
@@ -302,10 +302,9 @@
 
             // when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
             // on the screen.
-            if (userUnPinned && entry.mEntry != null && entry.mEntry.row != null) {
-                ExpandableNotificationRow row = entry.mEntry.row;
-                if (row.mustStayOnScreen()) {
-                    row.setHeadsUpIsVisible();
+            if (userUnPinned && entry.mEntry != null) {
+                if (entry.mEntry.mustStayOnScreen()) {
+                    entry.mEntry.setHeadsUpIsVisible();
                 }
             }
         }
@@ -341,7 +340,7 @@
      */
     public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
-        if (headsUpEntry != null && entry.row.isPinned()) {
+        if (headsUpEntry != null && entry.isRowPinned()) {
             headsUpEntry.setExpanded(expanded);
         }
     }
@@ -365,15 +364,15 @@
 
         @Override
         protected boolean isSticky() {
-            return (mEntry.row.isPinned() && expanded)
+            return (mEntry.isRowPinned() && expanded)
                     || remoteInputActive || hasFullScreenIntent(mEntry);
         }
 
         @Override
         public int compareTo(@NonNull AlertEntry alertEntry) {
             HeadsUpEntry headsUpEntry = (HeadsUpEntry) alertEntry;
-            boolean isPinned = mEntry.row.isPinned();
-            boolean otherPinned = headsUpEntry.mEntry.row.isPinned();
+            boolean isPinned = mEntry.isRowPinned();
+            boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
             if (isPinned && !otherPinned) {
                 return -1;
             } else if (!isPinned && otherPinned) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index d434768..7ad547a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -33,12 +33,12 @@
     /**
      * A notification was just pinned to the top.
      */
-    default void onHeadsUpPinned(ExpandableNotificationRow headsUp) {}
+    default void onHeadsUpPinned(NotificationData.Entry entry) {}
 
     /**
      * A notification was just unpinned from the top.
      */
-    default void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {}
+    default void onHeadsUpUnPinned(NotificationData.Entry entry) {}
 
     /**
      * A notification just became a heads up or turned back to its normal state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index a485fa8..866015e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -245,7 +245,7 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        if (mEntry.row.isChangingPosition()) {
+        if (mEntry.getRow().isChangingPosition()) {
             if (getVisibility() == VISIBLE && mEditText.isFocusable()) {
                 mEditText.requestFocus();
             }
@@ -255,7 +255,7 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        if (mEntry.row.isChangingPosition() || isTemporarilyDetached()) {
+        if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
             return;
         }
         mController.removeRemoteInput(mEntry, mToken);
@@ -495,7 +495,7 @@
         }
 
         private void defocusIfNeeded(boolean animate) {
-            if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isChangingPosition()
+            if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition()
                     || isTemporarilyDetached()) {
                 if (isTemporarilyDetached()) {
                     // We might get reattached but then the other one of HUN / expanded might steal
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 0186683..f36066c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -1,6 +1,7 @@
 package com.android.systemui.statusbar.policy;
 
 import android.annotation.ColorInt;
+import android.annotation.NonNull;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
@@ -189,15 +190,13 @@
      * into the notification are shown.
      */
     public void addRepliesFromRemoteInput(
-            RemoteInput remoteInput, PendingIntent pendingIntent,
-            SmartReplyController smartReplyController, NotificationData.Entry entry,
-            CharSequence[] choices) {
-        if (remoteInput != null && pendingIntent != null) {
-            if (choices != null) {
-                for (int i = 0; i < choices.length; ++i) {
+            SmartReplies smartReplies,
+            SmartReplyController smartReplyController, NotificationData.Entry entry) {
+        if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) {
+            if (smartReplies.choices != null) {
+                for (int i = 0; i < smartReplies.choices.length; ++i) {
                     Button replyButton = inflateReplyButton(
-                            getContext(), this, i, choices[i], remoteInput, pendingIntent,
-                            smartReplyController, entry);
+                            getContext(), this, i, smartReplies, smartReplyController, entry);
                     addView(replyButton);
                 }
             }
@@ -209,12 +208,14 @@
      * Add smart actions to be shown next to smart replies. Only the actions that fit into the
      * notification are shown.
      */
-    public void addSmartActions(List<Notification.Action> smartActions) {
-        int numSmartActions = smartActions.size();
+    public void addSmartActions(SmartActions smartActions,
+            SmartReplyController smartReplyController, NotificationData.Entry entry) {
+        int numSmartActions = smartActions.actions.size();
         for (int n = 0; n < numSmartActions; n++) {
-            Notification.Action action = smartActions.get(n);
+            Notification.Action action = smartActions.actions.get(n);
             if (action.actionIntent != null) {
-                Button actionButton = inflateActionButton(getContext(), this, action);
+                Button actionButton = inflateActionButton(
+                        getContext(), this, n, smartActions, smartReplyController, entry);
                 addView(actionButton);
             }
         }
@@ -228,22 +229,25 @@
 
     @VisibleForTesting
     Button inflateReplyButton(Context context, ViewGroup root, int replyIndex,
-            CharSequence choice, RemoteInput remoteInput, PendingIntent pendingIntent,
-            SmartReplyController smartReplyController, NotificationData.Entry entry) {
+            SmartReplies smartReplies, SmartReplyController smartReplyController,
+            NotificationData.Entry entry) {
         Button b = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_reply_button, root, false);
+        CharSequence choice = smartReplies.choices[replyIndex];
         b.setText(choice);
 
         OnDismissAction action = () -> {
-            smartReplyController.smartReplySent(entry, replyIndex, b.getText());
+            smartReplyController.smartReplySent(
+                    entry, replyIndex, b.getText(), smartReplies.fromAssistant);
             Bundle results = new Bundle();
-            results.putString(remoteInput.getResultKey(), choice.toString());
+            results.putString(smartReplies.remoteInput.getResultKey(), choice.toString());
             Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            RemoteInput.addResultsToIntent(new RemoteInput[]{remoteInput}, intent, results);
+            RemoteInput.addResultsToIntent(new RemoteInput[]{smartReplies.remoteInput}, intent,
+                    results);
             RemoteInput.setResultsSource(intent, RemoteInput.SOURCE_CHOICE);
             entry.setHasSentReply();
             try {
-                pendingIntent.send(context, 0, intent);
+                smartReplies.pendingIntent.send(context, 0, intent);
             } catch (PendingIntent.CanceledException e) {
                 Log.w(TAG, "Unable to send smart reply", e);
             }
@@ -268,7 +272,10 @@
     }
 
     @VisibleForTesting
-    Button inflateActionButton(Context context, ViewGroup root, Notification.Action action) {
+    Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
+            SmartActions smartActions, SmartReplyController smartReplyController,
+            NotificationData.Entry entry) {
+        Notification.Action action = smartActions.actions.get(actionIndex);
         Button button = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_action_button, root, false);
         button.setText(action.title);
@@ -281,7 +288,10 @@
         button.setCompoundDrawables(iconDrawable, null, null, null);
 
         button.setOnClickListener(view ->
-                getActivityStarter().startPendingIntentDismissingKeyguard(action.actionIntent));
+                getActivityStarter().startPendingIntentDismissingKeyguard(
+                        action.actionIntent,
+                        () -> smartReplyController.smartActionClicked(
+                                entry, actionIndex, action, smartActions.fromAssistant)));
 
         // TODO(b/119010281): handle accessibility
 
@@ -741,4 +751,40 @@
             return show;
         }
     }
+
+    /**
+     * Data class for smart replies.
+     */
+    public static class SmartReplies {
+        @NonNull
+        public final RemoteInput remoteInput;
+        @NonNull
+        public final PendingIntent pendingIntent;
+        @NonNull
+        public final CharSequence[] choices;
+        public final boolean fromAssistant;
+
+        public SmartReplies(CharSequence[] choices, RemoteInput remoteInput,
+                PendingIntent pendingIntent, boolean fromAssistant) {
+            this.choices = choices;
+            this.remoteInput = remoteInput;
+            this.pendingIntent = pendingIntent;
+            this.fromAssistant = fromAssistant;
+        }
+    }
+
+
+    /**
+     * Data class for smart actions.
+     */
+    public static class SmartActions {
+        @NonNull
+        public final List<Notification.Action> actions;
+        public final boolean fromAssistant;
+
+        public SmartActions(List<Notification.Action> actions, boolean fromAssistant) {
+            this.actions = actions;
+            this.fromAssistant = fromAssistant;
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index a26b1b5..0953951 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -19,11 +19,9 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.AlarmManager;
 import android.content.ContentResolver;
@@ -34,10 +32,15 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
-import android.util.Log;
+
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.SliceProvider;
+import androidx.slice.SliceSpecs;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.core.SliceQuery;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ZenModeController;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -50,13 +53,6 @@
 import java.util.HashSet;
 import java.util.concurrent.TimeUnit;
 
-import androidx.slice.Slice;
-import androidx.slice.SliceItem;
-import androidx.slice.SliceProvider;
-import androidx.slice.SliceSpecs;
-import androidx.slice.builders.ListBuilder;
-import androidx.slice.core.SliceQuery;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
@@ -164,7 +160,7 @@
         }
 
         @Override
-        protected boolean isDndSuppressingNotifications() {
+        protected boolean isDndOn() {
             return mIsZenMode;
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 221cbe9..c28e74e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -37,6 +37,7 @@
 import android.provider.Settings;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.testing.TestableResources;
 
@@ -127,6 +128,8 @@
         resources.addOverride(R.integer.config_warningTemperature, 55);
 
         mPowerUI.start();
+        // Guarantees mHandler has processed all messages.
+        TestableLooper.get(this).processAllMessages();
         verify(mMockWarnings).showHighTemperatureWarning();
     }
 
@@ -139,6 +142,8 @@
         resources.addOverride(R.integer.config_warningTemperature, 55);
 
         mPowerUI.start();
+        // Guarantees mHandler has processed all messages.
+        TestableLooper.get(this).processAllMessages();
         verify(mMockWarnings).showHighTemperatureWarning();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 48491d7..24bcca50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -16,8 +16,12 @@
 
 package com.android.systemui.privacy
 
+import android.app.ActivityManager
 import android.app.AppOpsManager
+import android.content.Intent
 import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
 import android.support.test.filters.SmallTest
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
@@ -34,9 +38,11 @@
 import org.mockito.ArgumentMatchers.anyList
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
 import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
-
 import org.mockito.MockitoAnnotations
 
 @RunWith(AndroidTestingRunner::class)
@@ -44,10 +50,18 @@
 @RunWithLooper
 class PrivacyItemControllerTest : SysuiTestCase() {
 
+    companion object {
+        val CURRENT_USER_ID = ActivityManager.getCurrentUser()
+        val OTHER_USER = UserHandle(CURRENT_USER_ID + 1)
+        const val TAG = "PrivacyItemControllerTest"
+    }
+
     @Mock
     private lateinit var appOpsController: AppOpsController
     @Mock
     private lateinit var callback: PrivacyItemController.Callback
+    @Mock
+    private lateinit var userManager: UserManager
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var privacyItemController: PrivacyItemController
@@ -57,15 +71,17 @@
         MockitoAnnotations.initMocks(this)
         testableLooper = TestableLooper.get(this)
 
-        appOpsController = mDependency.injectMockDependency(AppOpsController:: class.java)
+        appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
         mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
         mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
+        mContext.addMockSystemService(UserManager::class.java, userManager)
 
         doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0)))
                 .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
 
         privacyItemController = PrivacyItemController(mContext, callback)
     }
+
     @Test
     fun testSetListeningTrue() {
         privacyItemController.setListening(true)
@@ -80,6 +96,38 @@
         privacyItemController.setListening(true)
         privacyItemController.setListening(false)
         verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
-                any(AppOpsController.Callback:: class.java))
+                any(AppOpsController.Callback::class.java))
+    }
+
+    @Test
+    fun testRegisterReceiver_allUsers() {
+        val spiedContext = spy(mContext)
+        val itemController = PrivacyItemController(spiedContext, callback)
+
+        verify(spiedContext, atLeastOnce()).registerReceiverAsUser(
+                eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null),
+                eq(null))
+        verify(spiedContext, never()).unregisterReceiver(eq(itemController.userSwitcherReceiver))
+    }
+
+    @Test
+    fun testReceiver_ACTION_USER_FOREGROUND() {
+        privacyItemController.userSwitcherReceiver.onReceive(context,
+                Intent(Intent.ACTION_USER_FOREGROUND))
+        verify(userManager).getProfiles(anyInt())
+    }
+
+    @Test
+    fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
+        privacyItemController.userSwitcherReceiver.onReceive(context,
+                Intent(Intent.ACTION_MANAGED_PROFILE_ADDED))
+        verify(userManager).getProfiles(anyInt())
+    }
+
+    @Test
+    fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
+        privacyItemController.userSwitcherReceiver.onReceive(context,
+                Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED))
+        verify(userManager).getProfiles(anyInt())
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index f49c5b4..9d0556f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -120,7 +120,7 @@
         mTestHandler = Handler.createAsync(Looper.myLooper());
         mSbn = createNewNotification(0 /* id */);
         mEntry = new NotificationData.Entry(mSbn);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
 
         mAlertingNotificationManager = createAlertingNotificationManager();
     }
@@ -171,7 +171,7 @@
         for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
             StatusBarNotification sbn = createNewNotification(i);
             NotificationData.Entry entry = new NotificationData.Entry(sbn);
-            entry.row = mRow;
+            entry.setRow(mRow);
             mAlertingNotificationManager.showNotification(entry);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index d409e2b..b5d305d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -78,7 +78,7 @@
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, new Notification(), UserHandle.CURRENT, null, 0);
         mEntry = new NotificationData.Entry(mSbn);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
 
         mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mCallback,
                 mDelegate, mController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index aca1f90..9bed59b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -227,7 +227,7 @@
                 null /* overrideGroupKey */,
                 System.currentTimeMillis());
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
-        entry.row = row;
+        entry.setRow(row);
         entry.createIcons(mContext, sbn);
         entry.channel = new NotificationChannel(
                 notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 602e613..72d6cd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -37,6 +37,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -73,7 +74,7 @@
     @Mock private ShadeController mShadeController;
 
     private NotificationViewHierarchyManager mViewHierarchyManager;
-    private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);;
+    private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);
 
     @Before
     public void setUp() {
@@ -95,7 +96,7 @@
     private NotificationData.Entry createEntry() throws Exception {
         ExpandableNotificationRow row = mHelper.createRow();
         NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification());
-        entry.row = row;
+        entry.setRow(row);
         return entry;
     }
 
@@ -108,9 +109,9 @@
         NotificationData.Entry entry2 = createEntry();
 
         // Set up the prior state to look like three top level notifications.
-        mListContainer.addContainerView(entry0.row);
-        mListContainer.addContainerView(entry1.row);
-        mListContainer.addContainerView(entry2.row);
+        mListContainer.addContainerView(entry0.getRow());
+        mListContainer.addContainerView(entry1.getRow());
+        mListContainer.addContainerView(entry2.getRow());
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1, entry2));
 
@@ -118,15 +119,15 @@
         when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
         when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(true);
         when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(true);
-        when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0.row);
-        when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0.row);
+        when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0);
+        when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0);
 
         // Run updateNotifications - the view hierarchy should be reorganized.
         mViewHierarchyManager.updateNotificationViews();
 
-        verify(mListContainer).notifyGroupChildAdded(entry1.row);
-        verify(mListContainer).notifyGroupChildAdded(entry2.row);
-        assertTrue(Lists.newArrayList(entry0.row).equals(mListContainer.mRows));
+        verify(mListContainer).notifyGroupChildAdded(entry1.getRow());
+        verify(mListContainer).notifyGroupChildAdded(entry2.getRow());
+        assertTrue(Lists.newArrayList(entry0.getRow()).equals(mListContainer.mRows));
     }
 
     @Test
@@ -135,11 +136,11 @@
         NotificationData.Entry entry0 = createEntry();
         NotificationData.Entry entry1 = createEntry();
         NotificationData.Entry entry2 = createEntry();
-        entry0.row.addChildNotification(entry1.row);
-        entry0.row.addChildNotification(entry2.row);
+        entry0.getRow().addChildNotification(entry1.getRow());
+        entry0.getRow().addChildNotification(entry2.getRow());
 
         // Set up the prior state to look like one top level notification.
-        mListContainer.addContainerView(entry0.row);
+        mListContainer.addContainerView(entry0.getRow());
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1, entry2));
 
@@ -152,10 +153,12 @@
         mViewHierarchyManager.updateNotificationViews();
 
         verify(mListContainer).notifyGroupChildRemoved(
-                entry1.row, entry0.row.getChildrenContainer());
+                entry1.getRow(), entry0.getRow().getChildrenContainer());
         verify(mListContainer).notifyGroupChildRemoved(
-                entry2.row, entry0.row.getChildrenContainer());
-        assertTrue(Lists.newArrayList(entry0.row, entry1.row, entry2.row).equals(mListContainer.mRows));
+                entry2.getRow(), entry0.getRow().getChildrenContainer());
+        assertTrue(
+                Lists.newArrayList(entry0.getRow(), entry1.getRow(), entry2.getRow())
+                        .equals(mListContainer.mRows));
     }
 
     @Test
@@ -163,10 +166,10 @@
         // Tests two top level notifications becoming a suppressed summary and a child.
         NotificationData.Entry entry0 = createEntry();
         NotificationData.Entry entry1 = createEntry();
-        entry0.row.addChildNotification(entry1.row);
+        entry0.getRow().addChildNotification(entry1.getRow());
 
         // Set up the prior state to look like a top level notification.
-        mListContainer.addContainerView(entry0.row);
+        mListContainer.addContainerView(entry0.getRow());
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1));
 
@@ -179,23 +182,23 @@
         mViewHierarchyManager.updateNotificationViews();
 
         verify(mListContainer).notifyGroupChildRemoved(
-                entry1.row, entry0.row.getChildrenContainer());
-        assertTrue(Lists.newArrayList(entry0.row, entry1.row).equals(mListContainer.mRows));
-        assertEquals(View.GONE, entry0.row.getVisibility());
-        assertEquals(View.VISIBLE, entry1.row.getVisibility());
+                entry1.getRow(), entry0.getRow().getChildrenContainer());
+        assertTrue(Lists.newArrayList(entry0.getRow(), entry1.getRow()).equals(mListContainer.mRows));
+        assertEquals(View.GONE, entry0.getRow().getVisibility());
+        assertEquals(View.VISIBLE, entry1.getRow().getVisibility());
     }
 
     @Test
     public void testUpdateNotificationViews_appOps() throws Exception {
         NotificationData.Entry entry0 = createEntry();
-        entry0.row = spy(entry0.row);
+        entry0.setRow(spy(entry0.getRow()));
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0));
-        mListContainer.addContainerView(entry0.row);
+        mListContainer.addContainerView(entry0.getRow());
 
         mViewHierarchyManager.updateNotificationViews();
 
-        verify(entry0.row, times(1)).showAppOpsIcons(any());
+        verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
     }
 
     private class FakeListContainer implements NotificationListContainer {
@@ -206,19 +209,19 @@
         public void setChildTransferInProgress(boolean childTransferInProgress) {}
 
         @Override
-        public void changeViewPosition(View child, int newIndex) {
+        public void changeViewPosition(ExpandableView child, int newIndex) {
             mRows.remove(child);
             mRows.add(newIndex, child);
         }
 
         @Override
-        public void notifyGroupChildAdded(View row) {}
+        public void notifyGroupChildAdded(ExpandableView row) {}
 
         @Override
-        public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {}
+        public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {}
 
         @Override
-        public void generateAddAnimation(View child, boolean fromMoreCard) {}
+        public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {}
 
         @Override
         public void generateChildOrderChangedEvent() {}
@@ -252,7 +255,7 @@
         public void setMaxDisplayedNotifications(int maxNotifications) {}
 
         @Override
-        public void snapViewIfNeeded(ExpandableNotificationRow row) {}
+        public void snapViewIfNeeded(Entry entry) {}
 
         @Override
         public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
@@ -271,10 +274,10 @@
         }
 
         @Override
-        public void cleanUpViewState(View view) {}
+        public void cleanUpViewStateForEntry(Entry entry) { }
 
         @Override
-        public boolean isInVisibleLocation(ExpandableNotificationRow row) {
+        public boolean isInVisibleLocation(Entry entry) {
             return true;
         }
 
@@ -286,5 +289,6 @@
         public boolean hasPulsingNotifications() {
             return false;
         }
+
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 1d977d8..76b992f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -16,18 +16,15 @@
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -38,7 +35,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -93,7 +89,7 @@
 
     @Test
     public void testSendSmartReply_updatesRemoteInput() {
-        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
 
         // Sending smart reply should make calls to NotificationEntryManager
         // to update the notification with reply and spinner.
@@ -103,11 +99,21 @@
 
     @Test
     public void testSendSmartReply_logsToStatusBar() throws RemoteException {
-        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
 
         // Check we log the result to the status bar service.
         verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
-                TEST_CHOICE_INDEX);
+                TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
+    }
+
+
+    @Test
+    public void testSendSmartReply_logsToStatusBar_generatedByAssistant() throws RemoteException {
+        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true);
+
+        // Check we log the result to the status bar service.
+        verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
+                TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, true);
     }
 
     @Test
@@ -121,14 +127,14 @@
 
     @Test
     public void testSendSmartReply_reportsSending() {
-        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
 
         assertTrue(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
     }
 
     @Test
     public void testSendingSmartReply_afterRemove_shouldReturnFalse() {
-        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+        mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, false);
         mSmartReplyController.stopSending(mEntry);
 
         assertFalse(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index b3b45eb..f94ba95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -121,9 +121,9 @@
         when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
         mNotificationData = new TestableNotificationData();
-        Dependency.get(InitController.class).executePostInitTasks();
         mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
         mRow = new NotificationTestHelper(getContext()).createRow();
+        Dependency.get(InitController.class).executePostInitTasks();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index d1fe5af..8706e21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -262,14 +262,14 @@
                 NotificationData.Entry.class);
         verify(mCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
         NotificationData.Entry entry = entryCaptor.getValue();
-        verify(mRemoteInputManager).bindRow(entry.row);
+        verify(mRemoteInputManager).bindRow(entry.getRow());
 
         // Row content inflation:
         verify(mCallback).onNotificationAdded(entry);
         verify(mPresenter).updateNotificationViews();
 
         assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
-        assertNotNull(entry.row);
+        assertNotNull(entry.getRow());
         assertEquals(mEntry.userSentiment,
                 NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
     }
@@ -294,7 +294,7 @@
         verify(mPresenter).updateNotificationViews();
         verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
         verify(mCallback).onNotificationUpdated(mSbn);
-        assertNotNull(mEntry.row);
+        assertNotNull(mEntry.getRow());
         assertEquals(mEntry.userSentiment,
                 NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
     }
@@ -303,7 +303,7 @@
     public void testRemoveNotification() throws Exception {
         com.android.systemui.util.Assert.isNotMainThread();
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
 
         mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -313,7 +313,7 @@
 
         verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
         verify(mForegroundServiceController).removeNotification(mSbn);
-        verify(mListContainer).cleanUpViewState(mRow);
+        verify(mListContainer).cleanUpViewStateForEntry(mEntry);
         verify(mPresenter).updateNotificationViews();
         verify(mCallback).onNotificationRemoved(mSbn.getKey(), mSbn);
         verify(mRow).setRemoved();
@@ -332,7 +332,7 @@
         extenders.clear();
         extenders.add(extender);
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
 
         mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -347,7 +347,7 @@
 
         when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
                 .thenReturn(mEntry.key);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
 
         mEntryManager.updateNotificationsForAppOp(
@@ -372,7 +372,7 @@
 
     @Test
     public void testAddNotificationExistingAppOps() {
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         ArraySet<Integer> expected = new ArraySet<>();
         expected.add(3);
@@ -395,7 +395,7 @@
 
     @Test
     public void testAdd_noExistingAppOps() {
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         when(mForegroundServiceController.getStandardLayoutKey(
                 mEntry.notification.getUserId(),
@@ -409,7 +409,7 @@
 
     @Test
     public void testAdd_existingAppOpsNotForegroundNoti() {
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         ArraySet<Integer> ops = new ArraySet<>();
         ops.add(3);
@@ -431,7 +431,7 @@
         when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntry.setInflationTask(mAsyncInflationTask);
         mEntryManager.getNotificationData().add(mEntry);
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
@@ -447,7 +447,7 @@
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         setSmartActions(mEntry.key, null);
 
@@ -461,7 +461,7 @@
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = null;
+        mEntry.setRow(null);
         mEntryManager.getNotificationData().add(mEntry);
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
 
@@ -476,7 +476,7 @@
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = null;
+        mEntry.setRow(null);
         mEntryManager.mPendingNotifications.put(mEntry.key, mEntry);
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index ffb1c2d..f190979 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -47,7 +47,7 @@
     public void setUp() {
         mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
         mEntry = new NotificationData.Entry(mock(StatusBarNotification.class));
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 1c7a8e8..3710fa8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -89,7 +89,7 @@
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, new Notification(), UserHandle.CURRENT, null, 0);
         mEntry = new NotificationData.Entry(mSbn);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
 
         mLogger = new TestableNotificationLogger(mBarService);
         mLogger.setUpWithContainer(mListContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index a6725b8..f0fa788 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -16,11 +16,9 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.Mockito.doNothing;
@@ -106,7 +104,8 @@
         mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
         mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
 
-        // Smart replies
+        // Smart replies and actions
+        when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
         when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
         mEntry = new NotificationData.Entry(mStatusBarNotification);
         when(mSmartReplyConstants.isEnabled()).thenReturn(true);
@@ -169,8 +168,10 @@
     private void setupAppGeneratedReplies(
             CharSequence[] smartReplyTitles,
             Notification.Action freeFormRemoteInputAction) {
+        PendingIntent pendingIntent =
+                PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0);
         Notification.Action action =
-                new Notification.Action.Builder(null, "Test Action", null).build();
+                new Notification.Action.Builder(null, "Test Action", pendingIntent).build();
         when(mRemoteInput.getChoices()).thenReturn(smartReplyTitles);
         Pair<RemoteInput, Notification.Action> remoteInputActionPair =
                 Pair.create(mRemoteInput, action);
@@ -190,7 +191,7 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertFalse(repliesAndActions.smartRepliesExist());
+        assertThat(repliesAndActions.smartReplies).isNull();
     }
 
     @Test
@@ -202,7 +203,9 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
+        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+        assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+        assertThat(repliesAndActions.smartActions).isNull();
     }
 
     @Test
@@ -218,8 +221,10 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
-        assertThat(repliesAndActions.smartActions, equalTo(smartActions));
+        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies);
+        assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+        assertThat(repliesAndActions.smartActions.actions).isEqualTo(smartActions);
+        assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
     }
 
     @Test
@@ -236,8 +241,9 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(mEntry.smartReplies));
-        assertThat(repliesAndActions.smartActions, is(empty()));
+        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(mEntry.smartReplies);
+        assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
+        assertThat(repliesAndActions.smartActions).isNull();
     }
 
     @Test
@@ -254,8 +260,8 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(null));
-        assertThat(repliesAndActions.smartActions, is(empty()));
+        assertThat(repliesAndActions.smartReplies).isNull();
+        assertThat(repliesAndActions.smartActions).isNull();
     }
 
     @Test
@@ -269,8 +275,10 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(null));
-        assertThat(repliesAndActions.smartActions, equalTo(mEntry.systemGeneratedSmartActions));
+        assertThat(repliesAndActions.smartReplies).isNull();
+        assertThat(repliesAndActions.smartActions.actions)
+                .isEqualTo(mEntry.systemGeneratedSmartActions);
+        assertThat(repliesAndActions.smartActions.fromAssistant).isTrue();
     }
 
     @Test
@@ -295,8 +303,27 @@
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
-        assertThat(repliesAndActions.smartReplies, equalTo(appGenSmartReplies));
-        assertThat(repliesAndActions.smartActions, equalTo(appGenSmartActions));
+        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(appGenSmartReplies);
+        assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse();
+        assertThat(repliesAndActions.smartActions.actions).isEqualTo(appGenSmartActions);
+        assertThat(repliesAndActions.smartActions.fromAssistant).isFalse();
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() {
+        // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+        // actions.
+        setupAppGeneratedReplies(null);
+
+        when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
+
+        mEntry.systemGeneratedSmartActions =
+                createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartActions).isNull();
+        assertThat(repliesAndActions.smartReplies).isNull();
     }
 
     private Notification.Action.Builder createActionBuilder(String actionTitle) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 3d2ea70..84bfae6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -20,8 +20,7 @@
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
@@ -62,8 +61,7 @@
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager
-        .OnSettingsClickListener;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
@@ -177,10 +175,17 @@
         NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
 
         ExpandableNotificationRow row = spy(realRow);
+
         when(row.getWindowToken()).thenReturn(new Binder());
         when(row.getGuts()).thenReturn(guts);
         doNothing().when(row).inflateGuts();
 
+        NotificationData.Entry realEntry = realRow.getEntry();
+        NotificationData.Entry entry = spy(realEntry);
+
+        when(entry.getRow()).thenReturn(row);
+        when(entry.getGuts()).thenReturn(guts);
+
         mGutsManager.openGuts(row, 0, 0, menuItem);
         mTestableLooper.processAllMessages();
         verify(guts).openControls(
@@ -190,13 +195,19 @@
                 anyBoolean(),
                 any(Runnable.class));
 
+        // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
+        verify(row).setGutsView(any());
+
         row.onDensityOrFontScaleChanged();
-        mGutsManager.onDensityOrFontScaleChanged(row);
+        mGutsManager.onDensityOrFontScaleChanged(entry);
+
         mTestableLooper.processAllMessages();
 
         mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
 
         verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+
+        // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
         verify(row, times(2)).setGutsView(any());
     }
 
@@ -284,8 +295,7 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_NONE);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -302,8 +312,7 @@
                 eq(true) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
                 eq(false) /*isNoisy */,
-                eq(0),
-                eq(NotificationInfo.ACTION_NONE));
+                eq(0));
     }
 
     @Test
@@ -315,8 +324,7 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_NONE);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -333,8 +341,7 @@
                 eq(false) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
                 eq(false) /*isNoisy */,
-                eq(0),
-                eq(NotificationInfo.ACTION_NONE));
+                eq(0));
     }
 
     @Test
@@ -347,8 +354,7 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_NONE);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -365,8 +371,7 @@
                 eq(true) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
                 eq(true) /*isNoisy */,
-                eq(0),
-                eq(NotificationInfo.ACTION_NONE));
+                eq(0));
     }
 
     @Test
@@ -379,8 +384,7 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_NONE);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -397,8 +401,7 @@
                 eq(true) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
                 eq(false) /*isNoisy */,
-                eq(IMPORTANCE_DEFAULT),
-                eq(NotificationInfo.ACTION_NONE));
+                eq(IMPORTANCE_DEFAULT));
     }
 
     @Test
@@ -411,8 +414,7 @@
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_NONE);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -429,8 +431,7 @@
                 eq(false) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
                 eq(false) /*isNoisy */,
-                eq(0),
-                eq(NotificationInfo.ACTION_NONE));
+                eq(0));
     }
 
     @Test
@@ -442,8 +443,7 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_BLOCK);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -460,8 +460,7 @@
                 eq(true) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
                 eq(false) /*isNoisy */,
-                eq(0),
-                eq(NotificationInfo.ACTION_BLOCK));
+                eq(0));
     }
 
     @Test
@@ -470,7 +469,7 @@
         ExpandableNotificationRow row = spy(createTestNotificationRow());
         doReturn(guts).when(row).getGuts();
         NotificationData.Entry entry = row.getEntry();
-        entry.row = row;
+        entry.setRow(row);
         mGutsManager.setExposedGuts(guts);
 
         assertTrue(mGutsManager.shouldExtendLifetime(entry));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 1cc1c63..3dd493f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -134,7 +134,7 @@
                 .thenReturn(packageInfo);
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.uid = TEST_UID;  // non-zero
-        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
+        when(mMockPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt())).thenReturn(
                 applicationInfo);
         final PackageInfo systemPackageInfo = new PackageInfo();
         systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME;
@@ -187,7 +187,7 @@
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
         assertTrue(textView.getText().toString().contains("App Name"));
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -200,16 +200,47 @@
                 .thenReturn(iconDrawable);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
         assertEquals(iconDrawable, iconView.getDrawable());
     }
 
     @Test
+    public void testBindNotification_noDelegate() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
+        final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+        assertEquals(GONE, nameView.getVisibility());
+        final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+        assertEquals(GONE, dividerView.getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_delegate() throws Exception {
+        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, "other", 0, null, TEST_UID, 0,
+                new Notification(), UserHandle.CURRENT, null, 0);
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.uid = 7;  // non-zero
+        when(mMockPackageManager.getApplicationInfo(eq("other"), anyInt())).thenReturn(
+                applicationInfo);
+        when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT);
+        final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
+        assertEquals(VISIBLE, nameView.getVisibility());
+        assertTrue(nameView.getText().toString().contains("Other"));
+        final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
+        assertEquals(VISIBLE, dividerView.getVisibility());
+    }
+
+    @Test
     public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(GONE, groupNameView.getVisibility());
         final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
@@ -226,7 +257,7 @@
                 .thenReturn(notificationChannelGroup);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(View.VISIBLE, groupNameView.getVisibility());
         assertEquals("Test Group Name", groupNameView.getText());
@@ -238,7 +269,7 @@
     public void testBindNotification_SetsTextChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(TEST_CHANNEL_NAME, textView.getText());
     }
@@ -247,7 +278,7 @@
     public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
-                false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, textView.getVisibility());
     }
@@ -260,7 +291,7 @@
                 eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
-                false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -269,7 +300,7 @@
     public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -278,7 +309,7 @@
     public void testBindNotification_BlockButton() throws Exception {
        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-               false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+               false, IMPORTANCE_DEFAULT);
         final View block = mNotificationInfo.findViewById(R.id.block);
         final View toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
         final View minimize = mNotificationInfo.findViewById(R.id.minimize);
@@ -292,7 +323,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, IMPORTANCE_DEFAULT);
         final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
         assertEquals(VISIBLE, toggleSilent.getVisibility());
         assertEquals(
@@ -304,7 +335,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
+                true, IMPORTANCE_LOW);
         final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
         assertEquals(VISIBLE, toggleSilent.getVisibility());
         assertEquals(
@@ -316,7 +347,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, IMPORTANCE_DEFAULT);
         final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
         assertEquals(VISIBLE, toggleSilent.getVisibility());
         assertEquals(
@@ -329,7 +360,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
+                true, IMPORTANCE_LOW);
         final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
         assertEquals(VISIBLE, toggleSilent.getVisibility());
         assertEquals(
@@ -341,7 +372,7 @@
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final View block = mNotificationInfo.findViewById(R.id.block);
         final View minimize = mNotificationInfo.findViewById(R.id.minimize);
         assertEquals(GONE, block.getVisibility());
@@ -356,7 +387,7 @@
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
                     latch.countDown();
-                }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, null, true, false, false, IMPORTANCE_DEFAULT);
 
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         settingsButton.performClick();
@@ -368,7 +399,7 @@
     public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -380,7 +411,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
-                }, null, false, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, null, false, false, false, IMPORTANCE_DEFAULT);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -389,11 +420,11 @@
     public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
                 (View v, NotificationChannel c, int appUid) -> {
-                }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, null, true, false, false, IMPORTANCE_DEFAULT);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertEquals(View.VISIBLE, settingsButton.getVisibility());
     }
@@ -402,7 +433,7 @@
     public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
         verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
     }
@@ -411,7 +442,7 @@
     public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
-                true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, true, false, IMPORTANCE_DEFAULT);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
         verify(mMetricsLogger, times(1)).count(anyString(), anyInt());
     }
@@ -424,7 +455,7 @@
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(null, c);
                     latch.countDown();
-                }, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, null, true, true, false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.info).performClick();
         // Verify that listener was triggered.
@@ -437,7 +468,7 @@
             throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
-                null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                null, true, true, false, IMPORTANCE_DEFAULT);
         final TextView channelNameView =
                 mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, channelNameView.getVisibility());
@@ -448,7 +479,7 @@
     public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
-                null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                null, true, true, false, IMPORTANCE_DEFAULT);
         final TextView blockView = mNotificationInfo.findViewById(R.id.block);
         assertEquals(GONE, blockView.getVisibility());
     }
@@ -457,7 +488,7 @@
     public void testbindNotification_BlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
-                true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, true, false, IMPORTANCE_DEFAULT);
         final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
@@ -467,7 +498,7 @@
     public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -478,7 +509,7 @@
     public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
@@ -489,7 +520,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -503,7 +534,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         mTestableLooper.processAllMessages();
@@ -517,7 +548,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         mTestableLooper.processAllMessages();
@@ -531,7 +562,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         mTestableLooper.processAllMessages();
@@ -545,7 +576,7 @@
         int originalImportance = mNotificationChannel.getImportance();
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true, false);
         mTestableLooper.processAllMessages();
@@ -560,7 +591,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -578,8 +609,8 @@
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT
+        );
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -600,8 +631,8 @@
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */,
-                true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT
+        );
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -622,8 +653,7 @@
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
                 true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT);
 
         NotificationGuts guts = spy(new NotificationGuts(mContext, null));
         when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -651,8 +681,7 @@
                 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT);
 
         NotificationGuts guts = spy(new NotificationGuts(mContext, null));
         when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -681,7 +710,7 @@
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
                 true, true /* isUserSentimentNegative */, false /* isNoisy */,
-                IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
 
@@ -700,8 +729,7 @@
                 null /* onSettingsClick */, null /* onAppSettingsClick */,
                 true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -724,7 +752,7 @@
                 true /* isForBlockingHelper */,
                 true,
                 false /* isUserSentimentNegative */,
-                false /* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false /* isNoisy */, IMPORTANCE_DEFAULT);
         NotificationGuts guts = mock(NotificationGuts.class);
         doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
         mNotificationInfo.setGutsParent(guts);
@@ -739,7 +767,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
 
@@ -753,7 +781,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -786,7 +814,7 @@
                 false /* isNonblockable */,
                 true /* isForBlockingHelper */,
                 true /* isUserSentimentNegative */,
-                false/* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false/* isNoisy */, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -808,7 +836,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
 
@@ -823,7 +851,7 @@
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -844,7 +872,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -862,7 +890,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -884,7 +912,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -906,7 +934,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -927,7 +955,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -949,7 +977,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -971,7 +999,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_LOW);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -992,7 +1020,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -1008,7 +1036,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -1025,7 +1053,7 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
                 (Runnable saveImportance, StatusBarNotification sbn) -> {
-                }, null, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, null, null, true, true, false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -1043,8 +1071,8 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
                 (Runnable saveImportance, StatusBarNotification sbn) -> {
                     saveImportance.run();
-                }, null, null, true, false, false, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                }, null, null, true, false, false, IMPORTANCE_DEFAULT
+        );
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -1080,7 +1108,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
                 (View v, Intent intent) -> {
                     latch.countDown();
-                }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, true, false, false, IMPORTANCE_DEFAULT);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(View.VISIBLE, settingsLink.getVisibility());
         settingsLink.performClick();
@@ -1108,7 +1136,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
                 (View v, Intent intent) -> {
                     latch.countDown();
-                }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, true, false, false, IMPORTANCE_DEFAULT);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(View.VISIBLE, settingsLink.getVisibility());
         settingsLink.performClick();
@@ -1127,7 +1155,7 @@
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
-                null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                null, true, false, false, IMPORTANCE_DEFAULT);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(GONE, settingsLink.getVisibility());
     }
@@ -1148,7 +1176,7 @@
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(GONE, settingsLink.getVisibility());
     }
@@ -1171,7 +1199,7 @@
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true,
-                true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, true, false, IMPORTANCE_DEFAULT);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(GONE, settingsLink.getVisibility());
     }
@@ -1188,7 +1216,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -1201,7 +1229,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -1214,7 +1242,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -1228,7 +1256,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -1242,7 +1270,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -1254,7 +1282,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -1262,60 +1290,4 @@
         waitForStopButton();
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
     }
-
-    @Test
-    public void testBindNotificationWithInitialBlockAction() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_BLOCK);
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        ArgumentCaptor<NotificationChannel> updated =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), updated.capture());
-        assertTrue((updated.getValue().getUserLockedFields()
-                & USER_LOCKED_IMPORTANCE) != 0);
-        assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance());
-    }
-
-    @Test
-    public void testBindNotificationWithInitialSilenceAction() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_TOGGLE_SILENT);
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        ArgumentCaptor<NotificationChannel> updated =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), updated.capture());
-        assertTrue((updated.getValue().getUserLockedFields()
-                & USER_LOCKED_IMPORTANCE) != 0);
-        assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance());
-    }
-
-    @Test
-    public void testBindNotificationWithInitialUnSilenceAction() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_LOW, NotificationInfo.ACTION_TOGGLE_SILENT);
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        ArgumentCaptor<NotificationChannel> updated =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), updated.capture());
-        assertTrue((updated.getValue().getUserLockedFields()
-                & USER_LOCKED_IMPORTANCE) != 0);
-        assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance());
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index a4a111a..662e016 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -26,11 +26,11 @@
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -45,7 +45,7 @@
 public class NotificationRoundnessManagerTest extends SysuiTestCase {
 
     private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
-    private HashSet<View> mAnimatedChildren = new HashSet<>();
+    private HashSet<ExpandableView> mAnimatedChildren = new HashSet<>();
     private Runnable mRoundnessCallback = mock(Runnable.class);
     private ExpandableNotificationRow mFirst;
     private ExpandableNotificationRow mSecond;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 74ce5f6..e65e806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -325,7 +325,9 @@
 
         // add notification
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
-        when(row.isClearable()).thenReturn(true);
+        NotificationData.Entry entry = mock(NotificationData.Entry.class);
+        when(row.getEntry()).thenReturn(entry);
+        when(entry.isClearable()).thenReturn(true);
         mStackScroller.addContainerView(row);
 
         mStackScroller.onUpdateRowStates();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 10b0d83..c99e766 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -81,12 +81,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertEquals(mFirst.getEntry(), mHeadsUpStatusBarView.getShowingEntry());
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertEquals(null, mHeadsUpStatusBarView.getShowingEntry());
     }
 
@@ -95,12 +95,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertTrue(mHeadsUpAppearanceController.isShown());
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertFalse(mHeadsUpAppearanceController.isShown());
     }
 
@@ -109,12 +109,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 0.0f, 0.0f);
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f);
     }
 
@@ -125,12 +125,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertEquals(View.INVISIBLE, mOperatorNameView.getVisibility());
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertEquals(View.VISIBLE, mOperatorNameView.getVisibility());
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 1070795..44deb10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -97,7 +97,7 @@
     @Test
     public void testCanRemoveImmediately_notTopEntry() {
         NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
-        laterEntry.row = mRow;
+        laterEntry.setRow(mRow);
         mHeadsUpManager.showNotification(mEntry);
         mHeadsUpManager.showNotification(laterEntry);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index f8ad298..31014b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -37,7 +37,6 @@
     private static final int EMPTY_MARGIN = 0;
     private static final int EMPTY_HEIGHT = 0;
     private static final boolean SECURE_LOCKED = false;
-    private static final boolean PULSING_NO = false;
     private static final float ZERO_DRAG = 0.f;
     private static final float OPAQUE = 1.f;
     private static final float TRANSPARENT = 0.f;
@@ -48,6 +47,7 @@
     private float mPanelExpansion;
     private int mKeyguardStatusHeight;
     private float mDark;
+    private boolean mPulsing;
 
     @Before
     public void setUp() {
@@ -171,6 +171,156 @@
         assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT);
     }
 
+    @Test
+    public void notifPositionMiddleOfScreenOnAOD() {
+        // GIVEN on AOD and both stack scroll and clock have 0 height
+        givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionIndependentOfKeyguardStatusHeightOnAOD() {
+        // GIVEN on AOD and clock has a nonzero height
+        givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = 100;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionWithLargeClockOnAOD() {
+        // GIVEN on AOD and clock has a nonzero height
+        givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = SCREEN_HEIGHT;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding is, unfortunately, the entire screen.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(SCREEN_HEIGHT);
+    }
+
+    @Test
+    public void notifPositionWhilePulsingOnAOD() {
+        // GIVEN on AOD and pulsing
+        givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        mPulsing = true;
+        mClockPositionAlgorithm.setPulsingPadding(200);
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the notif padding doesn't adjust for pulsing.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionMiddleOfScreenOnLockScreen() {
+        // GIVEN on lock screen and both stack scroll and clock have 0 height
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionAdjustsForStackHeightOnLockScreen() {
+        // GIVEN on lock screen and stack scroller has a nonzero height
+        givenLockScreen();
+        mNotificationStackHeight = 500;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding adjusts for the expanded notif stack.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(750);
+    }
+
+    @Test
+    public void notifPositionAdjustsForClockHeightOnLockScreen() {
+        // GIVEN on lock screen and stack scroller has a nonzero height
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = 200;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding adjusts for both clock and notif stack.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionAdjustsForStackHeightAndClockHeightOnLockScreen() {
+        // GIVEN on lock screen and stack scroller has a nonzero height
+        givenLockScreen();
+        mNotificationStackHeight = 500;
+        mKeyguardStatusHeight = 200;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding adjusts for both clock and notif stack.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(810);
+    }
+
+    @Test
+    public void notifPositionWithLargeClockOnLockScreen() {
+        // GIVEN on lock screen and clock has a nonzero height
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = SCREEN_HEIGHT;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionWithFullDragOnLockScreen() {
+        // GIVEN the lock screen is dragged up
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        mPanelExpansion = 0.f;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the notif padding is zero.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
+    }
+
+    @Test
+    public void notifPositionWithLargeClockFullDragOnLockScreen() {
+        // GIVEN the lock screen is dragged up and a full screen clock
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = SCREEN_HEIGHT;
+        mPanelExpansion = 0.f;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the notif padding is zero.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
+    }
+
+    @Test
+    public void notifPositionWhilePulsingOnLockScreen() {
+        // GIVEN on lock screen and pulsing
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        mPulsing = true;
+        mClockPositionAlgorithm.setPulsingPadding(200);
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the notif padding adjusts for pulsing.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1200);
+    }
+
     private void givenAOD() {
         mPanelExpansion = 1.f;
         mDark = 1.f;
@@ -184,7 +334,7 @@
     private void positionClock() {
         mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
                 mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mDark, SECURE_LOCKED,
-                PULSING_NO, ZERO_DRAG);
+                mPulsing, ZERO_DRAG);
         mClockPositionAlgorithm.run(mClockPosition);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 96c57f2..c3bc511 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -149,7 +149,8 @@
         Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
         Entry childEntry = mGroupTestHelper.createChildNotification();
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -166,12 +167,14 @@
         Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
         Entry childEntry = mGroupTestHelper.createChildNotification();
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
 
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(true);
         mGroupAlertTransferHelper.onInflationFinished(childEntry);
 
         // Alert is immediately removed from summary, and we show child as its content is inflated.
@@ -185,7 +188,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         NotificationData.Entry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
@@ -199,10 +203,12 @@
         mGroupManager.onEntryAdded(childEntry2);
 
         // Child entry finishes its inflation.
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(true);
         mGroupAlertTransferHelper.onInflationFinished(childEntry);
 
-        verify(childEntry.row, times(1)).freeContentViewWhenSafe(mHeadsUpManager.getContentFlag());
+        verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
+            .getContentFlag());
         assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
     }
 
@@ -212,7 +218,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
         mGroupManager.onEntryAdded(summaryEntry);
@@ -229,7 +236,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
         mGroupManager.onEntryAdded(summaryEntry);
@@ -251,7 +259,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
         mGroupManager.onEntryAdded(summaryEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index 1483ae5..b0bd1fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -100,7 +100,7 @@
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
 
         assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.notification));
-        assertEquals(summaryEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
+        assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry.notification));
     }
 
     @Test
@@ -143,9 +143,8 @@
 
         // Child entries that are heads upped should be considered separate groups visually even if
         // they are the same group logically
-        assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
-        assertEquals(summaryEntry.row,
-                mGroupManager.getLogicalGroupSummary(childEntry.notification));
+        assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+        assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
     }
 
     @Test
@@ -161,8 +160,7 @@
 
         // Child entries that are heads upped should be considered separate groups visually even if
         // they are the same group logically
-        assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
-        assertEquals(summaryEntry.row,
-                mGroupManager.getLogicalGroupSummary(childEntry.notification));
+        assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+        assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index 01f44fd4..7ad68eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -81,7 +81,7 @@
                 0 /* postTime */);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
-        entry.row = row;
+        entry.setRow(row);
         when(row.getEntry()).thenReturn(entry);
         when(row.getStatusBarNotification()).thenReturn(sbn);
         when(row.isInflationFlagSet(anyInt())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 9e659c8..df7aeab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -62,6 +62,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -181,7 +182,16 @@
     public void testSendSmartReply_controllerCalled() {
         setSmartReplies(TEST_CHOICES);
         mView.getChildAt(2).performClick();
-        verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2]);
+        verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
+                false /* generatedByAsssitant */);
+    }
+
+    @Test
+    public void testSendSmartReply_controllerCalled_generatedByAssistant() {
+        setSmartReplies(TEST_CHOICES, true);
+        mView.getChildAt(2).performClick();
+        verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2],
+                true /* generatedByAsssitant */);
     }
 
     @Test
@@ -392,11 +402,17 @@
     }
 
     private void setSmartReplies(CharSequence[] choices) {
+        setSmartReplies(choices, false);
+    }
+
+    private void setSmartReplies(CharSequence[] choices, boolean fromAssistant) {
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
                 new Intent(TEST_ACTION), 0);
         RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
+        SmartReplyView.SmartReplies smartReplies =
+                new SmartReplyView.SmartReplies(choices, input, pendingIntent, fromAssistant);
         mView.resetSmartSuggestions(mContainer);
-        mView.addRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, choices);
+        mView.addRepliesFromRemoteInput(smartReplies, mLogger, mEntry);
     }
 
     private Notification.Action createAction(String actionTitle) {
@@ -415,12 +431,18 @@
 
     private void setSmartActions(String[] actionTitles) {
         mView.resetSmartSuggestions(mContainer);
-        mView.addSmartActions(createActions(actionTitles));
+        mView.addSmartActions(
+                new SmartReplyView.SmartActions(createActions(actionTitles), false),
+                mLogger,
+                mEntry);
     }
 
     private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
         setSmartReplies(choices);
-        mView.addSmartActions(createActions(actionTitles));
+        mView.addSmartActions(
+                new SmartReplyView.SmartActions(createActions(actionTitles), false),
+                mLogger,
+                mEntry);
     }
 
     private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
@@ -453,9 +475,11 @@
 
         // Add smart replies
         Button previous = null;
+        SmartReplyView.SmartReplies smartReplies =
+                new SmartReplyView.SmartReplies(choices, null, null, false);
         for (int i = 0; i < choices.length; ++i) {
-            Button current = mView.inflateReplyButton(mContext, mView, i, choices[i],
-                    null, null, null, null);
+            Button current = mView.inflateReplyButton(mContext, mView, i, smartReplies,
+                    null, null);
             current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
                     current.getPaddingBottom());
             if (previous != null) {
@@ -536,7 +560,7 @@
 
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any());
+        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
     }
 
     @Test
@@ -721,7 +745,9 @@
     }
 
     private Button inflateActionButton(Notification.Action action) {
-        return mView.inflateActionButton(getContext(), mView, action);
+        return mView.inflateActionButton(getContext(), mView, 0,
+                new SmartReplyView.SmartActions(Collections.singletonList(action), false),
+                mLogger, mEntry);
     }
 
     @Test
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 8d691ff..12e7376 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -273,33 +273,6 @@
 
     private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
             int policyFlags) {
-        if (DEBUG_ALL) Slog.i(LOG_TAG, "dispatchTransformedEvent(event = " + event + ")");
-
-        // If the touchscreen event is within the magnified portion of the screen we have
-        // to change its location to be where the user thinks he is poking the
-        // UI which may have been magnified and panned.
-        if (mMagnificationController.isMagnifying()
-                && event.isFromSource(SOURCE_TOUCHSCREEN)
-                && mMagnificationController.magnificationRegionContains(
-                        event.getX(), event.getY())) {
-            final float scale = mMagnificationController.getScale();
-            final float scaledOffsetX = mMagnificationController.getOffsetX();
-            final float scaledOffsetY = mMagnificationController.getOffsetY();
-            final int pointerCount = event.getPointerCount();
-            PointerCoords[] coords = getTempPointerCoordsWithMinSize(pointerCount);
-            PointerProperties[] properties = getTempPointerPropertiesWithMinSize(
-                    pointerCount);
-            for (int i = 0; i < pointerCount; i++) {
-                event.getPointerCoords(i, coords[i]);
-                coords[i].x = (coords[i].x - scaledOffsetX) / scale;
-                coords[i].y = (coords[i].y - scaledOffsetY) / scale;
-                event.getPointerProperties(i, properties[i]);
-            }
-            event = MotionEvent.obtain(event.getDownTime(),
-                    event.getEventTime(), event.getAction(), pointerCount, properties,
-                    coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0, event.getSource(),
-                    event.getFlags());
-        }
         if (DEBUG_EVENT_STREAM) {
             storeEventInto(mDebugOutputEventHistory, event);
             try {
diff --git a/services/art-profile b/services/art-profile
index bdd49de..af9d7a9 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -18849,7 +18849,7 @@
 PLcom/android/server/wm/WindowManagerService;->onSystemUiStarted()V
 PLcom/android/server/wm/WindowManagerService;->openSession(Landroid/view/IWindowSessionCallback;Lcom/android/internal/view/IInputMethodClient;Lcom/android/internal/view/IInputContext;)Landroid/view/IWindowSession;
 PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransition(Ljava/lang/String;IILandroid/os/IRemoteCallback;)V
-PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
+PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
 PLcom/android/server/wm/WindowManagerService;->performBootTimeout()V
 PLcom/android/server/wm/WindowManagerService;->performEnableScreen()V
 PLcom/android/server/wm/WindowManagerService;->postWindowRemoveCleanupLocked(Lcom/android/server/wm/WindowState;)V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 17d8ea7..c56f31e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -18,11 +18,13 @@
 
 import static android.Manifest.permission.MANAGE_AUTO_FILL;
 import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
+import static android.util.DebugUtils.flagsToString;
 
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sFullScreenMode;
 import static com.android.server.autofill.Helper.sVerbose;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -72,9 +74,12 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.intelligence.IntelligenceManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -95,6 +100,27 @@
 
     private static final Object sLock = AutofillManagerService.class;
 
+
+    /**
+     * IME supports Smart Suggestions.
+     */
+    // NOTE: must be public because of flagsToString()
+    public static final int FLAG_SMART_SUGGESTION_IME = 0x1;
+
+    /**
+     * System supports Smarts Suggestions (as a popup-window similar to standard Autofill).
+     */
+    // NOTE: must be public because of flagsToString()
+    public static final int FLAG_SMART_SUGGESTION_SYSTEM = 0x2;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_SMART_SUGGESTION_" }, value = {
+            FLAG_SMART_SUGGESTION_IME,
+            FLAG_SMART_SUGGESTION_SYSTEM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface SmartSuggestionMode {}
+
     static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
 
     private static final char COMPAT_PACKAGE_DELIMITER = ':';
@@ -102,7 +128,6 @@
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
 
-
     /**
      * Maximum number of partitions that can be allowed in a session.
      *
@@ -130,6 +155,7 @@
 
     private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
     private final LocalService mLocalService = new LocalService();
+    final IntelligenceManagerInternal mIntelligenceManagerInternal;
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -153,13 +179,21 @@
     @GuardedBy("mLock")
     private boolean mAllowInstantService;
 
+    /**
+     * Supported modes for Augmented Autofill Smart Suggestions.
+     */
+    @GuardedBy("mLock")
+    private int mSupportedSmartSuggestionModes;
+
     public AutofillManagerService(Context context) {
         super(context, UserManager.DISALLOW_AUTOFILL);
         mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
+        mIntelligenceManagerInternal = LocalServices.getService(IntelligenceManagerInternal.class);
 
         setLogLevelFromSettings();
         setMaxPartitionsFromSettings();
         setMaxVisibleDatasetsFromSettings();
+        setSmartSuggestionEmulationFromSettings();
 
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -186,6 +220,9 @@
         resolver.registerContentObserver(Settings.Global.getUriFor(
                 Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, observer,
                 UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS), false, observer,
+                UserHandle.USER_ALL);
     }
 
     @Override // from AbstractMasterSystemService
@@ -200,6 +237,9 @@
             case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
                 setMaxVisibleDatasetsFromSettings();
                 break;
+            case Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS:
+                setSmartSuggestionEmulationFromSettings();
+                break;
             default:
                 Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead");
                 // fall through
@@ -243,6 +283,10 @@
         mUi.hideAll(null);
     }
 
+    @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() {
+        return mSupportedSmartSuggestionModes;
+    }
+
     // Called by Shell command.
     void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
         Slog.i(TAG, "destroySessions() for userId " + userId);
@@ -420,6 +464,19 @@
         }
     }
 
+    private void setSmartSuggestionEmulationFromSettings() {
+        final int flags = Settings.Global.getInt(getContext().getContentResolver(),
+                Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS, 0);
+        if (sDebug) {
+            Slog.d(TAG, "setSmartSuggestionEmulationFromSettings(): "
+                    + smartSuggestionFlagsToString(flags));
+        }
+
+        synchronized (mLock) {
+            mSupportedSmartSuggestionModes = flags;
+        }
+    }
+
     // Called by Shell command.
     void getScore(@Nullable String algorithmName, @NonNull String value1,
             @NonNull String value2, @NonNull RemoteCallback callback) {
@@ -610,6 +667,10 @@
         }
     }
 
+    static String smartSuggestionFlagsToString(int flags) {
+        return flagsToString(AutofillManagerService.class, "FLAG_SMART_SUGGESTION_", flags);
+    }
+
     private final class LocalService extends AutofillManagerInternal {
         @Override
         public void onBackKeyPressed() {
@@ -1158,6 +1219,10 @@
                     pw.print("from settings: ");
                     pw.println(getWhitelistedCompatModePackagesFromSettings());
                     pw.print("Allow instant service: "); pw.println(mAllowInstantService);
+                    if (mSupportedSmartSuggestionModes != 0) {
+                        pw.print("Smart Suggestion modes: ");
+                        pw.println(smartSuggestionFlagsToString(mSupportedSmartSuggestionModes));
+                    }
                     if (showHistory) {
                         pw.println(); pw.println("Requests history:"); pw.println();
                         mRequestsHistory.reverseDump(fd, pw, args);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 67ccc9b..0df99d4 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -73,6 +73,7 @@
 import com.android.server.AbstractPerUserSystemService;
 import com.android.server.LocalServices;
 import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
+import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
 import com.android.server.autofill.ui.AutoFillUI;
 
 import java.io.PrintWriter;
@@ -268,8 +269,8 @@
         pruneAbandonedSessionsLocked();
 
         final Session newSession = createSessionByTokenLocked(activityToken, taskId, uid,
-                appCallbackToken, hasCallback, componentName, compatMode, bindInstantServiceAllowed,
-                flags);
+                appCallbackToken, hasCallback, componentName, compatMode,
+                bindInstantServiceAllowed, flags);
         if (newSession == null) {
             return NO_SESSION;
         }
@@ -823,6 +824,12 @@
         return true;
     }
 
+    @GuardedBy("mLock")
+    @SmartSuggestionMode int getSupportedSmartSuggestionModesLocked() {
+        // TODO(b/111330312): once we support IME, we need to set it per-user (OR'ed with master)
+        return mMaster.getSupportedSmartSuggestionModesLocked();
+    }
+
     @Override
     @GuardedBy("mLock")
     protected void dumpLocked(String prefix, PrintWriter pw) {
@@ -962,6 +969,9 @@
                 if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
                 session.forceRemoveSelfLocked();
             }
+            else {
+                session.destroyAugmentedAutofillWindowsLocked();
+            }
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1ff1acd..8676f7f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -25,6 +25,9 @@
 import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_IME;
+import static com.android.server.autofill.AutofillManagerService.FLAG_SMART_SUGGESTION_SYSTEM;
+import static com.android.server.autofill.AutofillManagerService.smartSuggestionFlagsToString;
 import static com.android.server.autofill.Helper.getNumericValue;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
@@ -93,8 +96,11 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.AbstractRemoteService;
+import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
 import com.android.server.autofill.ui.AutoFillUI;
 import com.android.server.autofill.ui.PendingUi;
+import com.android.server.intelligence.IntelligenceManagerInternal;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -242,6 +248,10 @@
     @GuardedBy("mLock")
     private final SparseArray<LogMaker> mRequestLogs = new SparseArray<>(1);
 
+    @GuardedBy("mLock")
+    @Nullable
+    private AugmentedAutofillCallback mAugmentedAutofillCallback;
+
     /**
      * Receiver of assist data from the app's {@link Activity}.
      */
@@ -2497,15 +2507,83 @@
         processResponseLocked(newResponse, newClientState, 0);
     }
 
+    @GuardedBy("mLock")
     private void processNullResponseLocked(int flags) {
-        if (sVerbose) Slog.v(TAG, "canceling session " + id + " when server returned null");
         if ((flags & FLAG_MANUAL_REQUEST) != 0) {
             getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this);
         }
         mService.resetLastResponse();
-        // Nothing to be done, but need to notify client.
-        notifyUnavailableToClient(AutofillManager.STATE_FINISHED);
-        removeSelf();
+
+        // The default autofill service cannot fullfill the request, let's check if the intelligence
+        // service can.
+        mAugmentedAutofillCallback = triggerAugmentedAutofillLocked();
+        if (mAugmentedAutofillCallback == null) {
+            if (sVerbose) {
+                Slog.v(TAG, "canceling session " + id + " when server returned null and there is no"
+                        + " AugmentedAutofill for user");
+            }
+            // Nothing to be done, but need to notify client.
+            notifyUnavailableToClient(AutofillManager.STATE_FINISHED);
+            removeSelf();
+        } else {
+            // TODO(b/111330312, b/119638958): must set internal state so when user focus other
+            // fields it does not generate a new call to the standard autofill service (right now
+            // it does). Must also add CTS tests to exercise this scenario.
+            if (sVerbose) {
+                Slog.v(TAG, "keeping session " + id + " when server returned null but "
+                        + "there is an AugmentedAutofill for user");
+            }
+        }
+    }
+
+    /**
+     * Tries to trigger Augmented Autofill when the standard service could not fulfill a request.
+     *
+     * @return callback to the Augmented Autofill service, or {@code null} if not supported.
+     */
+    // TODO(b/111330312): might need to call it in other places, like when the service returns a
+    // non-null response but without datasets (for example, just SaveInfo)
+    @GuardedBy("mLock")
+    private AugmentedAutofillCallback triggerAugmentedAutofillLocked() {
+        // Check if Smart Suggestions is supported...
+        final @SmartSuggestionMode int supportedModes = mService
+                .getSupportedSmartSuggestionModesLocked();
+        if (supportedModes == 0) return null;
+
+        // ...then if the service is set for the user
+        final IntelligenceManagerInternal intelligenceManagerInternal = mService
+                .getMaster().mIntelligenceManagerInternal;
+        if (intelligenceManagerInternal == null) return null;
+
+        // Define which mode will be used
+        final int mode;
+        if ((supportedModes & FLAG_SMART_SUGGESTION_IME) != 0) {
+            // TODO(b/111330312): support it :-)
+            Slog.w(TAG, "Smart Suggestions on IME not supported yet");
+            return null;
+        } else if ((supportedModes & FLAG_SMART_SUGGESTION_SYSTEM) != 0) {
+            mode = FLAG_SMART_SUGGESTION_SYSTEM;
+        } else {
+            Slog.w(TAG, "Unsupported Smart Suggestion Mode: " + supportedModes);
+            return null;
+        }
+
+        if (mCurrentViewId == null) {
+            Slog.w(TAG, "triggerAugmentedAutofillLocked(): no view currently focused");
+            return null;
+        }
+
+        if (sVerbose) {
+            Slog.v(TAG, "calling IntelligenseService on view " + mCurrentViewId
+                    + " using suggestion mode " + smartSuggestionFlagsToString(mode)
+                    + " when server returned null for session " + this.id);
+        }
+
+        // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize
+        // furgher AFM -> AFMS calls.
+        // TODO(b/119638958): add CTS tests
+        return intelligenceManagerInternal.requestAutofill(mService.getUserId(), mClient,
+                mActivityToken, this.id, mCurrentViewId);
     }
 
     @GuardedBy("mLock")
@@ -2786,6 +2864,9 @@
         pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
                 mSaveOnAllViewsInvisible);
         pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
+        if (mAugmentedAutofillCallback != null) {
+            pw.print(prefix); pw.println("has AugmentedAutofillCallback");
+        }
         mRemoteFillService.dump(prefix, pw);
     }
 
@@ -2957,6 +3038,14 @@
                 Slog.e(TAG, "Error notifying client to finish session", e);
             }
         }
+        destroyAugmentedAutofillWindowsLocked();
+    }
+
+    @GuardedBy("mLock")
+    void destroyAugmentedAutofillWindowsLocked() {
+        if (mAugmentedAutofillCallback != null) {
+            mAugmentedAutofillCallback.destroy();
+        }
     }
 
     /**
diff --git a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
index df46d260b..2bca34d 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
@@ -16,12 +16,15 @@
 
 package com.android.server.backup;
 
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
 import android.content.ContentResolver;
 import android.os.Handler;
 import android.provider.Settings;
 import android.util.KeyValueListParser;
 import android.util.KeyValueSettingObserver;
 import android.util.Slog;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -137,7 +140,7 @@
 
     public long getKvBackupAgentTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(TAG, "getKvBackupAgentTimeoutMillis(): " + mKvBackupAgentTimeoutMillis);
             }
             return mKvBackupAgentTimeoutMillis;
@@ -146,7 +149,7 @@
 
     public long getFullBackupAgentTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(TAG, "getFullBackupAgentTimeoutMillis(): " + mFullBackupAgentTimeoutMillis);
             }
             return mFullBackupAgentTimeoutMillis;
@@ -155,7 +158,7 @@
 
     public long getSharedBackupAgentTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(
                         TAG,
                         "getSharedBackupAgentTimeoutMillis(): " + mSharedBackupAgentTimeoutMillis);
@@ -166,7 +169,7 @@
 
     public long getRestoreAgentTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(TAG, "getRestoreAgentTimeoutMillis(): " + mRestoreAgentTimeoutMillis);
             }
             return mRestoreAgentTimeoutMillis;
@@ -175,7 +178,7 @@
 
     public long getRestoreAgentFinishedTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(
                         TAG,
                         "getRestoreAgentFinishedTimeoutMillis(): "
@@ -187,7 +190,7 @@
 
     public long getQuotaExceededTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(
                         TAG,
                         "getQuotaExceededTimeoutMillis(): "
diff --git a/services/backup/java/com/android/server/backup/BackupManagerConstants.java b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
index ec21961..785d3ca 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerConstants.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
@@ -16,6 +16,8 @@
 
 package com.android.server.backup;
 
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
 import android.app.AlarmManager;
 import android.content.ContentResolver;
 import android.os.Handler;
@@ -24,6 +26,7 @@
 import android.util.KeyValueListParser;
 import android.util.KeyValueSettingObserver;
 import android.util.Slog;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
@@ -151,7 +154,7 @@
     // group the calls of these methods in a block syncrhonized on
     // a reference of this object.
     public synchronized long getKeyValueBackupIntervalMilliseconds() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getKeyValueBackupIntervalMilliseconds(...) returns "
@@ -161,7 +164,7 @@
     }
 
     public synchronized long getKeyValueBackupFuzzMilliseconds() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getKeyValueBackupFuzzMilliseconds(...) returns "
@@ -171,7 +174,7 @@
     }
 
     public synchronized boolean getKeyValueBackupRequireCharging() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getKeyValueBackupRequireCharging(...) returns "
@@ -181,7 +184,7 @@
     }
 
     public synchronized int getKeyValueBackupRequiredNetworkType() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getKeyValueBackupRequiredNetworkType(...) returns "
@@ -191,7 +194,7 @@
     }
 
     public synchronized long getFullBackupIntervalMilliseconds() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getFullBackupIntervalMilliseconds(...) returns "
@@ -201,14 +204,14 @@
     }
 
     public synchronized boolean getFullBackupRequireCharging() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(TAG, "getFullBackupRequireCharging(...) returns " + mFullBackupRequireCharging);
         }
         return mFullBackupRequireCharging;
     }
 
     public synchronized int getFullBackupRequiredNetworkType() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getFullBackupRequiredNetworkType(...) returns "
@@ -219,7 +222,7 @@
 
     /** Returns an array of package names that should be notified whenever a backup finishes. */
     public synchronized String[] getBackupFinishedNotificationReceivers() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getBackupFinishedNotificationReceivers(...) returns "
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index e4b4bc5..0b06f28 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,239 +16,62 @@
 
 package com.android.server.backup;
 
-import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
-
-import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_FULL_CONFIRMATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_OP_COMPLETE;
-import static com.android.server.backup.internal.BackupHandler.MSG_REQUEST_BACKUP;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
-import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
-
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.AlarmManager;
-import android.app.AppGlobals;
-import android.app.IActivityManager;
-import android.app.IBackupAgent;
-import android.app.PendingIntent;
-import android.app.backup.BackupAgent;
-import android.app.backup.BackupManager;
-import android.app.backup.BackupManagerMonitor;
-import android.app.backup.FullBackup;
-import android.app.backup.IBackupManager;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
 import android.app.backup.IFullBackupRestoreObserver;
 import android.app.backup.IRestoreSession;
 import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
+import android.app.job.JobParameters;
+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.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.ContentObserver;
-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;
-import android.os.Message;
 import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
-import android.os.PowerManager.ServiceType;
-import android.os.PowerSaveState;
-import android.os.Process;
 import android.os.RemoteException;
-import android.os.SELinux;
-import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.os.WorkSource;
-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;
-import android.util.Pair;
 import android.util.Slog;
-import android.util.SparseArray;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.backup.IBackupTransport;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
-import com.android.server.AppWidgetBackupBridge;
-import com.android.server.EventLogTags;
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
-import com.android.server.backup.fullbackup.FullBackupEntry;
-import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
-import com.android.server.backup.internal.BackupHandler;
-import com.android.server.backup.internal.ClearDataObserver;
-import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.Operation;
-import com.android.server.backup.internal.PerformInitializeTask;
-import com.android.server.backup.internal.ProvisionedObserver;
-import com.android.server.backup.internal.RunBackupReceiver;
-import com.android.server.backup.internal.RunInitializeReceiver;
-import com.android.server.backup.keyvalue.BackupRequest;
-import com.android.server.backup.params.AdbBackupParams;
-import com.android.server.backup.params.AdbParams;
-import com.android.server.backup.params.AdbRestoreParams;
-import com.android.server.backup.params.BackupParams;
-import com.android.server.backup.params.ClearParams;
-import com.android.server.backup.params.ClearRetryParams;
-import com.android.server.backup.params.RestoreParams;
-import com.android.server.backup.restore.ActiveRestoreSession;
-import com.android.server.backup.restore.PerformUnifiedRestoreTask;
-import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.transport.TransportNotRegisteredException;
-import com.android.server.backup.utils.AppBackupUtils;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
-import com.android.server.backup.utils.BackupObserverUtils;
-import com.android.server.backup.utils.SparseArrayUtils;
 
-import com.google.android.collect.Sets;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.io.RandomAccessFile;
-import java.security.SecureRandom;
-import java.text.SimpleDateFormat;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-import java.util.Random;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
 
-/** System service that performs backup/restore operations. */
+/**
+ * Definition of the system service that performs backup/restore operations.
+ *
+ * <p>This class is responsible for handling user-aware operations and acts as a delegator, routing
+ * incoming calls to the appropriate per-user {@link UserBackupManagerService} to handle the
+ * corresponding backup/restore operation.
+ */
 public class BackupManagerService {
     public static final String TAG = "BackupManagerService";
     public static final boolean DEBUG = true;
     public static final boolean MORE_DEBUG = false;
     public static final boolean DEBUG_SCHEDULING = true;
 
-    // File containing backup-enabled state.  Contains a single byte;
-    // nonzero == enabled.  File missing or contains a zero byte == disabled.
+    // File containing backup-enabled state. Contains a single byte to denote enabled status.
+    // Nonzero is enabled; file missing or a zero byte is disabled.
     private static final String BACKUP_ENABLE_FILE = "backup_enabled";
 
-    // Persistently track the need to do a full init.
-    private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
-
-    // 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).
-    public static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
-
-    // Name and current contents version of the full-backup manifest file
-    //
-    // Manifest version history:
-    //
-    // 1 : initial release
-    public static final String BACKUP_MANIFEST_FILENAME = "_manifest";
-    public static final int BACKUP_MANIFEST_VERSION = 1;
-
-    // External archive format version history:
-    //
-    // 1 : initial release
-    // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
-    // 3 : introduced "_meta" metadata file; no other format change per se
-    // 4 : added support for new device-encrypted storage locations
-    // 5 : added support for key-value packages
-    public static final int BACKUP_FILE_VERSION = 5;
-    public static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
-    public static final String BACKUP_METADATA_FILENAME = "_meta";
-    public static final int BACKUP_METADATA_VERSION = 1;
-    public static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
-
-    private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
-
-    // Round-robin queue for scheduling full backup passes.
-    private static final int SCHEDULE_FILE_VERSION = 1;
-
-    public static final String SETTINGS_PACKAGE = "com.android.providers.settings";
-    public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
-
-    // Pseudoname that we use for the Package Manager metadata "package".
-    public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
-
-    // Retry interval for clear/init when the transport is unavailable
-    private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
-
-    public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
-    public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
-    public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
-    public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
-
-    // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
-    // pending operations list.
-    public static final int OP_PENDING = 0;
-    private static final int OP_ACKNOWLEDGED = 1;
-    private static final int OP_TIMEOUT = -1;
-
-    // Waiting for backup agent to respond during backup operation.
-    public static final int OP_TYPE_BACKUP_WAIT = 0;
-
-    // Waiting for backup agent to respond during restore operation.
-    public static final int OP_TYPE_RESTORE_WAIT = 1;
-
-    // An entire backup operation spanning multiple packages.
-    public static final int OP_TYPE_BACKUP = 2;
-
-    // Time delay for initialization operations that can be delayed so as not to consume too much
-    // CPU on bring-up and increase time-to-UI.
-    private static final long INITIALIZATION_DELAY_MILLIS = 3000;
-
-    // Timeout interval for deciding that a bind or clear-data has taken too long
-    private static final long TIMEOUT_INTERVAL = 10 * 1000;
-
-    // User confirmation timeout for a full backup/restore operation.  It's this long in
-    // order to give them time to enter the backup password.
-    private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
-
-    // If an app is busy when we want to do a full-data backup, how long to defer the retry.
-    // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
-    private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
-    private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
-
     // The published binder is a singleton Trampoline object that calls through to the proper code.
     // This indirection lets us turn down the heavy implementation object on the fly without
     // disturbing binders that have been cached elsewhere in the system.
@@ -302,483 +125,25 @@
                 transportManager);
     }
 
-    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
-    private final TransportManager mTransportManager;
+    private UserBackupManagerService mUserBackupManagerService;
 
-    private Context mContext;
-    private PackageManager mPackageManager;
-    private IPackageManager mPackageManagerBinder;
-    private IActivityManager mActivityManager;
-    private PowerManager mPowerManager;
-    private AlarmManager mAlarmManager;
-    private IStorageManager mStorageManager;
-    private BackupManagerConstants mConstants;
-    private PowerManager.WakeLock mWakelock;
-    private BackupHandler mBackupHandler;
-
-    private IBackupManager mBackupManagerBinder;
-
-    private boolean mEnabled;   // access to this is synchronized on 'this'
-    private boolean mProvisioned;
-    private boolean mAutoRestore;
-
-    private PendingIntent mRunBackupIntent;
-    private PendingIntent mRunInitIntent;
-
-    private final ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
-
-    // map UIDs to the set of participating packages under that UID
-    private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
-
-    // Backups that we haven't started yet.  Keys are package names.
-    private HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
-
-    // locking around the pending-backup management
-    private final Object mQueueLock = new Object();
-
-    // The thread performing the sequence of queued backups binds to each app's agent
-    // in succession.  Bind notifications are asynchronously delivered through the
-    // Activity Manager; use this lock object to signal when a requested binding has
-    // completed.
-    private final Object mAgentConnectLock = new Object();
-    private IBackupAgent mConnectedAgent;
-    private volatile boolean mConnecting;
-
-    private volatile boolean mBackupRunning;
-    private volatile long mLastBackupPass;
-
-    // A similar synchronization mechanism around clearing apps' data for restore
-    private final Object mClearDataLock = new Object();
-    private volatile boolean mClearingData;
-
-    // Used by ADB.
-    private final BackupPasswordManager mBackupPasswordManager;
-    private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
-    private final SecureRandom mRng = new SecureRandom();
-
-    // Time when we post the transport registration operation
-    private final long mRegisterTransportsRequestedTime;
-
-    @GuardedBy("mQueueLock")
-    private PerformFullTransportBackupTask mRunningFullBackupTask;
-
-    @GuardedBy("mQueueLock")
-    private ArrayList<FullBackupEntry> mFullBackupQueue;
-
-    @GuardedBy("mPendingRestores")
-    private boolean mIsRestoreInProgress;
-
-    @GuardedBy("mPendingRestores")
-    private final Queue<PerformUnifiedRestoreTask> mPendingRestores = new ArrayDeque<>();
-
-    private ActiveRestoreSession mActiveRestoreSession;
-
-    // Watch the device provisioning operation during setup
-    private ContentObserver mProvisionedObserver;
-
-    /**
-     * mCurrentOperations contains the list of currently active operations.
-     *
-     * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
-     * An operation wraps a BackupRestoreTask within it.
-     * It's the responsibility of this task to remove the operation from this array.
-     *
-     * A BackupRestore task gets notified of ack/timeout for the operation via
-     * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
-     * on the mCurrentOpLock.
-     * {@link BackupManagerService#waitUntilOperationComplete(int)} is
-     * used in various places to 'wait' for notifyAll and detect change of pending state of an
-     * operation. So typically, an operation will be removed from this array by:
-     * - BackupRestoreTask#handleCancel and
-     * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
-     * these places because waitUntilOperationComplete relies on the operation being present to
-     * determine its completion status.
-     *
-     * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
-     * cancel backup tasks.
-     */
-    @GuardedBy("mCurrentOpLock")
-    private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
-    private final Object mCurrentOpLock = new Object();
-    private final Random mTokenGenerator = new Random();
-    final AtomicInteger mNextToken = new AtomicInteger();
-
-    // Where we keep our journal files and other bookkeeping.
-    private File mBaseStateDir;
-    private File mDataDir;
-    private File mJournalDir;
-    @Nullable
-    private DataChangedJournal mJournal;
-    private File mFullBackupScheduleFile;
-
-    // Keep a log of all the apps we've ever backed up.
-    private ProcessedPackagesJournal mProcessedPackagesJournal;
-
-    private File mTokenFile;
-    private Set<String> mAncestralPackages = null;
-    private long mAncestralToken = 0;
-    private long mCurrentToken = 0;
-
-    @VisibleForTesting
+    /** Instantiate a new instance of {@link BackupManagerService}. */
     public BackupManagerService(
             Context context,
-            Trampoline parent,
+            Trampoline trampoline,
             HandlerThread backupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
-        mContext = context;
-        mPackageManager = context.getPackageManager();
-        mPackageManagerBinder = AppGlobals.getPackageManager();
-        mActivityManager = ActivityManager.getService();
-
-        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
-
-        mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
-
-        mAgentTimeoutParameters = new
-                BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
-        mAgentTimeoutParameters.start();
-
-        // spin up the backup/restore handler thread
-        mBackupHandler = new BackupHandler(this, backupThread.getLooper());
-
-        // Set up our bookkeeping
-        final ContentResolver resolver = context.getContentResolver();
-        mProvisioned = Settings.Global.getInt(resolver,
-                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
-        mAutoRestore = Settings.Secure.getInt(resolver,
-                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
-
-        mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
-        resolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
-                false, mProvisionedObserver);
-
-        mBaseStateDir = baseStateDir;
-        mBaseStateDir.mkdirs();
-        if (!SELinux.restorecon(mBaseStateDir)) {
-            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
-        }
-
-        mDataDir = dataDir;
-
-        mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
-
-        // Alarm receivers for scheduled backups & initialization operations
-        BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(RUN_BACKUP_ACTION);
-        context.registerReceiver(mRunBackupReceiver, filter,
-                android.Manifest.permission.BACKUP, null);
-
-        BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
-        filter = new IntentFilter();
-        filter.addAction(RUN_INITIALIZE_ACTION);
-        context.registerReceiver(mRunInitReceiver, filter,
-                android.Manifest.permission.BACKUP, null);
-
-        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
-        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
-
-        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
-        initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
-
-        // Set up the backup-request journaling
-        mJournalDir = new File(mBaseStateDir, "pending");
-        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
-        mJournal = null;        // will be created on first use
-
-        mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
-        // We are observing changes to the constants throughout the lifecycle of BMS. This is
-        // because we reference the constants in multiple areas of BMS, which otherwise would
-        // require frequent starting and stopping.
-        mConstants.start();
-
-        // Set up the various sorts of package tracking we do
-        mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
-        initPackageTracking();
-
-        // Build our mapping of uid to backup client services.  This implicitly
-        // schedules a backup pass on the Package Manager metadata the first
-        // time anything needs to be backed up.
-        synchronized (mBackupParticipants) {
-            addPackageParticipantsLocked(null);
-        }
-
-        mTransportManager = transportManager;
-        mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
-        mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
-        mBackupHandler.postDelayed(
-                mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
-
-        // Now that we know about valid backup participants, parse any leftover journal files into
-        // the pending backup set
-        mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
-
-        // Power management
-        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+        mUserBackupManagerService =
+                new UserBackupManagerService(
+                        context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
     }
 
-
-    public BackupManagerConstants getConstants() {
-        return mConstants;
-    }
-
-    public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
-        return mAgentTimeoutParameters;
-    }
-
-    public Context getContext() {
-        return mContext;
-    }
-
-    public void setContext(Context context) {
-        mContext = context;
-    }
-
-    public PackageManager getPackageManager() {
-        return mPackageManager;
-    }
-
-    public void setPackageManager(PackageManager packageManager) {
-        mPackageManager = packageManager;
-    }
-
-    public IPackageManager getPackageManagerBinder() {
-        return mPackageManagerBinder;
-    }
-
-    public void setPackageManagerBinder(IPackageManager packageManagerBinder) {
-        mPackageManagerBinder = packageManagerBinder;
-    }
-
-    public IActivityManager getActivityManager() {
-        return mActivityManager;
-    }
-
-    public void setActivityManager(IActivityManager activityManager) {
-        mActivityManager = activityManager;
-    }
-
-    public AlarmManager getAlarmManager() {
-        return mAlarmManager;
-    }
-
-    public void setAlarmManager(AlarmManager alarmManager) {
-        mAlarmManager = alarmManager;
-    }
-
+    // TODO(b/118520567): Remove when tests are modified to use per-user instance.
     @VisibleForTesting
-    void setPowerManager(PowerManager powerManager) {
-        mPowerManager = powerManager;
-    }
-
-    public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
-        mBackupManagerBinder = backupManagerBinder;
-    }
-
-    public TransportManager getTransportManager() {
-        return mTransportManager;
-    }
-
-    public boolean isEnabled() {
-        return mEnabled;
-    }
-
-    public void setEnabled(boolean enabled) {
-        mEnabled = enabled;
-    }
-
-    public boolean isProvisioned() {
-        return mProvisioned;
-    }
-
-    public void setProvisioned(boolean provisioned) {
-        mProvisioned = provisioned;
-    }
-
-    public PowerManager.WakeLock getWakelock() {
-        return mWakelock;
-    }
-
-    /**
-     * Sets the {@link WorkSource} of the {@link PowerManager.WakeLock} returned by {@link
-     * #getWakelock()}.
-     */
-    @VisibleForTesting
-    public void setWorkSource(@Nullable WorkSource workSource) {
-        // TODO: This is for testing, unfortunately WakeLock is final and WorkSource is not exposed
-        mWakelock.setWorkSource(workSource);
-    }
-
-    public void setWakelock(PowerManager.WakeLock wakelock) {
-        mWakelock = wakelock;
-    }
-
-    public Handler getBackupHandler() {
-        return mBackupHandler;
-    }
-
-    public void setBackupHandler(BackupHandler backupHandler) {
-        mBackupHandler = backupHandler;
-    }
-
-    public PendingIntent getRunInitIntent() {
-        return mRunInitIntent;
-    }
-
-    public void setRunInitIntent(PendingIntent runInitIntent) {
-        mRunInitIntent = runInitIntent;
-    }
-
-    public HashMap<String, BackupRequest> getPendingBackups() {
-        return mPendingBackups;
-    }
-
-    public void setPendingBackups(
-            HashMap<String, BackupRequest> pendingBackups) {
-        mPendingBackups = pendingBackups;
-    }
-
-    public Object getQueueLock() {
-        return mQueueLock;
-    }
-
-    public boolean isBackupRunning() {
-        return mBackupRunning;
-    }
-
-    public void setBackupRunning(boolean backupRunning) {
-        mBackupRunning = backupRunning;
-    }
-
-    public long getLastBackupPass() {
-        return mLastBackupPass;
-    }
-
-    public void setLastBackupPass(long lastBackupPass) {
-        mLastBackupPass = lastBackupPass;
-    }
-
-    public Object getClearDataLock() {
-        return mClearDataLock;
-    }
-
-    public boolean isClearingData() {
-        return mClearingData;
-    }
-
-    public void setClearingData(boolean clearingData) {
-        mClearingData = clearingData;
-    }
-
-    public boolean isRestoreInProgress() {
-        return mIsRestoreInProgress;
-    }
-
-    public void setRestoreInProgress(boolean restoreInProgress) {
-        mIsRestoreInProgress = restoreInProgress;
-    }
-
-    public Queue<PerformUnifiedRestoreTask> getPendingRestores() {
-        return mPendingRestores;
-    }
-
-    public ActiveRestoreSession getActiveRestoreSession() {
-        return mActiveRestoreSession;
-    }
-
-    public void setActiveRestoreSession(
-            ActiveRestoreSession activeRestoreSession) {
-        mActiveRestoreSession = activeRestoreSession;
-    }
-
-    public SparseArray<Operation> getCurrentOperations() {
-        return mCurrentOperations;
-    }
-
-    public Object getCurrentOpLock() {
-        return mCurrentOpLock;
-    }
-
-    public SparseArray<AdbParams> getAdbBackupRestoreConfirmations() {
-        return mAdbBackupRestoreConfirmations;
-    }
-
-    public File getBaseStateDir() {
-        return mBaseStateDir;
-    }
-
-    public void setBaseStateDir(File baseStateDir) {
-        mBaseStateDir = baseStateDir;
-    }
-
-    public File getDataDir() {
-        return mDataDir;
-    }
-
-    public void setDataDir(File dataDir) {
-        mDataDir = dataDir;
-    }
-
-    @Nullable
-    public DataChangedJournal getJournal() {
-        return mJournal;
-    }
-
-    public void setJournal(@Nullable DataChangedJournal journal) {
-        mJournal = journal;
-    }
-
-    public SecureRandom getRng() {
-        return mRng;
-    }
-
-    public Set<String> getAncestralPackages() {
-        return mAncestralPackages;
-    }
-
-    public void setAncestralPackages(Set<String> ancestralPackages) {
-        mAncestralPackages = ancestralPackages;
-    }
-
-    public long getAncestralToken() {
-        return mAncestralToken;
-    }
-
-    public void setAncestralToken(long ancestralToken) {
-        mAncestralToken = ancestralToken;
-    }
-
-    public long getCurrentToken() {
-        return mCurrentToken;
-    }
-
-    public void setCurrentToken(long currentToken) {
-        mCurrentToken = currentToken;
-    }
-
-    public ArraySet<String> getPendingInits() {
-        return mPendingInits;
-    }
-
-    /** Clear all pending transport initializations. */
-    public void clearPendingInits() {
-        mPendingInits.clear();
-    }
-
-    public PerformFullTransportBackupTask getRunningFullBackupTask() {
-        return mRunningFullBackupTask;
-    }
-
-    public void setRunningFullBackupTask(
-            PerformFullTransportBackupTask runningFullBackupTask) {
-        mRunningFullBackupTask = runningFullBackupTask;
+    void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) {
+        mUserBackupManagerService = userBackupManagerService;
     }
 
     /**
@@ -819,2092 +184,93 @@
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    /**
-     *  Utility: build a new random integer token. The low bits are the ordinal of the operation for
-     *  near-time uniqueness, and the upper bits are random for app-side unpredictability.
+    /*
+     * The following methods are implementations of IBackupManager methods called from Trampoline.
+     * They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
+     * action on the passed in user. Currently this is a straight redirection (see TODO).
      */
-    public int generateRandomIntegerToken() {
-        int token = mTokenGenerator.nextInt();
-        if (token < 0) token = -token;
-        token &= ~0xFF;
-        token |= (mNextToken.incrementAndGet() & 0xFF);
-        return token;
+    // TODO (b/118520567): Take in user id and call per-user instance of UserBackupManagerService.
+
+    // ---------------------------------------------
+    // BACKUP AGENT OPERATIONS
+    // ---------------------------------------------
+
+    /**
+     * An app's backup agent calls this method to let the service know that there's new data to
+     * backup for their app {@code packageName}. Only used for apps participating in key-value
+     * backup.
+     */
+    public void dataChanged(String packageName) {
+        mUserBackupManagerService.dataChanged(packageName);
     }
 
     /**
-     * Construct a backup agent instance for the metadata pseudopackage. This is a process-local
-     * non-lifecycle agent instance, so we manually set up the context topology for it.
+     * Callback: a requested backup agent has been instantiated. This should only be called from the
+     * {@link ActivityManager}.
      */
-    public BackupAgent makeMetadataAgent() {
-        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
-        pmAgent.attach(mContext);
-        pmAgent.onCreate();
-        return pmAgent;
+    public void agentConnected(String packageName, IBinder agentBinder) {
+        mUserBackupManagerService.agentConnected(packageName, agentBinder);
     }
 
     /**
-     * Same as {@link #makeMetadataAgent()} but with explicit package-set configuration.
+     * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
+     * called from the {@link ActivityManager}.
      */
-    public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
-        PackageManagerBackupAgent pmAgent =
-                new PackageManagerBackupAgent(mPackageManager, packages);
-        pmAgent.attach(mContext);
-        pmAgent.onCreate();
-        return pmAgent;
-    }
-
-    private void initPackageTracking() {
-        if (MORE_DEBUG) Slog.v(TAG, "` tracking");
-
-        // Remember our ancestral dataset
-        mTokenFile = new File(mBaseStateDir, "ancestral");
-        try (DataInputStream tokenStream = new DataInputStream(new BufferedInputStream(
-                new FileInputStream(mTokenFile)))) {
-            int version = tokenStream.readInt();
-            if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
-                mAncestralToken = tokenStream.readLong();
-                mCurrentToken = tokenStream.readLong();
-
-                int numPackages = tokenStream.readInt();
-                if (numPackages >= 0) {
-                    mAncestralPackages = new HashSet<>();
-                    for (int i = 0; i < numPackages; i++) {
-                        String pkgName = tokenStream.readUTF();
-                        mAncestralPackages.add(pkgName);
-                    }
-                }
-            }
-        } catch (FileNotFoundException fnf) {
-            // Probably innocuous
-            Slog.v(TAG, "No ancestral data");
-        } catch (IOException e) {
-            Slog.w(TAG, "Unable to read token file", e);
-        }
-
-        mProcessedPackagesJournal = new ProcessedPackagesJournal(mBaseStateDir);
-        mProcessedPackagesJournal.init();
-
-        synchronized (mQueueLock) {
-            // Resume the full-data backup queue
-            mFullBackupQueue = readFullBackupSchedule();
-        }
-
-        // Register for broadcasts about package install, etc., so we can
-        // update the provider list.
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addDataScheme("package");
-        mContext.registerReceiver(mBroadcastReceiver, filter);
-        // Register for events related to sdcard installation.
-        IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
-    }
-
-    private ArrayList<FullBackupEntry> readFullBackupSchedule() {
-        boolean changed = false;
-        ArrayList<FullBackupEntry> schedule = null;
-        List<PackageInfo> apps =
-                PackageManagerBackupAgent.getStorableApplications(mPackageManager);
-
-        if (mFullBackupScheduleFile.exists()) {
-            try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
-                 BufferedInputStream bufStream = new BufferedInputStream(fstream);
-                 DataInputStream in = new DataInputStream(bufStream)) {
-                int version = in.readInt();
-                if (version != SCHEDULE_FILE_VERSION) {
-                    Slog.e(TAG, "Unknown backup schedule version " + version);
-                    return null;
-                }
-
-                final int numPackages = in.readInt();
-                schedule = new ArrayList<>(numPackages);
-
-                // HashSet instead of ArraySet specifically because we want the eventual
-                // lookups against O(hundreds) of entries to be as fast as possible, and
-                // we discard the set immediately after the scan so the extra memory
-                // overhead is transient.
-                HashSet<String> foundApps = new HashSet<>(numPackages);
-
-                for (int i = 0; i < numPackages; i++) {
-                    String pkgName = in.readUTF();
-                    long lastBackup = in.readLong();
-                    foundApps.add(pkgName); // all apps that we've addressed already
-                    try {
-                        PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
-                        if (AppBackupUtils.appGetsFullBackup(pkg)
-                                && AppBackupUtils.appIsEligibleForBackup(
-                                pkg.applicationInfo, mPackageManager)) {
-                            schedule.add(new FullBackupEntry(pkgName, lastBackup));
-                        } else {
-                            if (DEBUG) {
-                                Slog.i(TAG, "Package " + pkgName
-                                        + " no longer eligible for full backup");
-                            }
-                        }
-                    } catch (NameNotFoundException e) {
-                        if (DEBUG) {
-                            Slog.i(TAG, "Package " + pkgName
-                                    + " not installed; dropping from full backup");
-                        }
-                    }
-                }
-
-                // New apps can arrive "out of band" via OTA and similar, so we also need to
-                // scan to make sure that we're tracking all full-backup candidates properly
-                for (PackageInfo app : apps) {
-                    if (AppBackupUtils.appGetsFullBackup(app)
-                            && AppBackupUtils.appIsEligibleForBackup(
-                            app.applicationInfo, mPackageManager)) {
-                        if (!foundApps.contains(app.packageName)) {
-                            if (MORE_DEBUG) {
-                                Slog.i(TAG, "New full backup app " + app.packageName + " found");
-                            }
-                            schedule.add(new FullBackupEntry(app.packageName, 0));
-                            changed = true;
-                        }
-                    }
-                }
-
-                Collections.sort(schedule);
-            } catch (Exception e) {
-                Slog.e(TAG, "Unable to read backup schedule", e);
-                mFullBackupScheduleFile.delete();
-                schedule = null;
-            }
-        }
-
-        if (schedule == null) {
-            // no prior queue record, or unable to read it.  Set up the queue
-            // from scratch.
-            changed = true;
-            schedule = new ArrayList<>(apps.size());
-            for (PackageInfo info : apps) {
-                if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
-                        info.applicationInfo, mPackageManager)) {
-                    schedule.add(new FullBackupEntry(info.packageName, 0));
-                }
-            }
-        }
-
-        if (changed) {
-            writeFullBackupScheduleAsync();
-        }
-        return schedule;
-    }
-
-    private Runnable mFullBackupScheduleWriter = new Runnable() {
-        @Override
-        public void run() {
-            synchronized (mQueueLock) {
-                try {
-                    ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
-                    DataOutputStream bufOut = new DataOutputStream(bufStream);
-                    bufOut.writeInt(SCHEDULE_FILE_VERSION);
-
-                    // version 1:
-                    //
-                    // [int] # of packages in the queue = N
-                    // N * {
-                    //     [utf8] package name
-                    //     [long] last backup time for this package
-                    //     }
-                    int numPackages = mFullBackupQueue.size();
-                    bufOut.writeInt(numPackages);
-
-                    for (int i = 0; i < numPackages; i++) {
-                        FullBackupEntry entry = mFullBackupQueue.get(i);
-                        bufOut.writeUTF(entry.packageName);
-                        bufOut.writeLong(entry.lastBackup);
-                    }
-                    bufOut.flush();
-
-                    AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
-                    FileOutputStream out = af.startWrite();
-                    out.write(bufStream.toByteArray());
-                    af.finishWrite(out);
-                } catch (Exception e) {
-                    Slog.e(TAG, "Unable to write backup schedule!", e);
-                }
-            }
-        }
-    };
-
-    private void writeFullBackupScheduleAsync() {
-        mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
-        mBackupHandler.post(mFullBackupScheduleWriter);
-    }
-
-    private void parseLeftoverJournals() {
-        ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(mJournalDir);
-        for (DataChangedJournal journal : journals) {
-            if (!journal.equals(mJournal)) {
-                try {
-                    journal.forEach(packageName -> {
-                        Slog.i(TAG, "Found stale backup journal, scheduling");
-                        if (MORE_DEBUG) Slog.i(TAG, "  " + packageName);
-                        dataChangedImpl(packageName);
-                    });
-                } catch (IOException e) {
-                    Slog.e(TAG, "Can't read " + journal, e);
-                }
-            }
-        }
-    }
-
-    /** Used for generating random salts or passwords. */
-    public byte[] randomBytes(int bits) {
-        byte[] array = new byte[bits / 8];
-        mRng.nextBytes(array);
-        return array;
-    }
-
-    /** For adb backup/restore. */
-    public boolean setBackupPassword(String currentPw, String newPw) {
-        return mBackupPasswordManager.setBackupPassword(currentPw, newPw);
-    }
-
-    /** For adb backup/restore. */
-    public boolean hasBackupPassword() {
-        return mBackupPasswordManager.hasBackupPassword();
-    }
-
-    /** For adb backup/restore. */
-    public boolean backupPasswordMatches(String currentPw) {
-        return mBackupPasswordManager.backupPasswordMatches(currentPw);
+    public void agentDisconnected(String packageName) {
+        mUserBackupManagerService.agentDisconnected(packageName);
     }
 
     /**
-     * Maintain persistent state around whether need to do an initialize operation. This will lock
-     * on {@link #getQueueLock()}.
+     * Used by a currently-active backup agent to notify the service that it has completed its given
+     * outstanding asynchronous backup/restore operation.
      */
-    public void recordInitPending(
-            boolean isPending, String transportName, String transportDirName) {
-        synchronized (mQueueLock) {
-            if (MORE_DEBUG) {
-                Slog.i(TAG, "recordInitPending(" + isPending + ") on transport " + transportName);
-            }
-
-            File stateDir = new File(mBaseStateDir, transportDirName);
-            File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
-
-            if (isPending) {
-                // We need an init before we can proceed with sending backup data.
-                // Record that with an entry in our set of pending inits, as well as
-                // journaling it via creation of a sentinel file.
-                mPendingInits.add(transportName);
-                try {
-                    (new FileOutputStream(initPendingFile)).close();
-                } catch (IOException ioe) {
-                    // Something is badly wrong with our permissions; just try to move on
-                }
-            } else {
-                // No more initialization needed; wipe the journal and reset our state.
-                initPendingFile.delete();
-                mPendingInits.remove(transportName);
-            }
-        }
+    public void opComplete(int token, long result) {
+        mUserBackupManagerService.opComplete(token, result);
     }
 
-    /**
-     * Reset all of our bookkeeping because the backend data has been wiped (for example due to idle
-     * expiry), so we must re-upload all saved settings.
-     */
-    public void resetBackupState(File stateFileDir) {
-        synchronized (mQueueLock) {
-            mProcessedPackagesJournal.reset();
+    // ---------------------------------------------
+    // TRANSPORT OPERATIONS
+    // ---------------------------------------------
 
-            mCurrentToken = 0;
-            writeRestoreTokens();
-
-            // Remove all the state files
-            for (File sf : stateFileDir.listFiles()) {
-                // ... but don't touch the needs-init sentinel
-                if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
-                    sf.delete();
-                }
-            }
-        }
-
-        // Enqueue a new backup of every participant
-        synchronized (mBackupParticipants) {
-            final int numParticipants = mBackupParticipants.size();
-            for (int i = 0; i < numParticipants; i++) {
-                HashSet<String> participants = mBackupParticipants.valueAt(i);
-                if (participants != null) {
-                    for (String packageName : participants) {
-                        dataChangedImpl(packageName);
-                    }
-                }
-            }
-        }
-    }
-
-    private void onTransportRegistered(String transportName, String transportDirName) {
-        if (DEBUG) {
-            long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
-            Slog.d(TAG, "Transport " + transportName + " registered " + timeMs
-                    + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)");
-        }
-
-        File stateDir = new File(mBaseStateDir, transportDirName);
-        stateDir.mkdirs();
-
-        File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
-        if (initSentinel.exists()) {
-            synchronized (mQueueLock) {
-                mPendingInits.add(transportName);
-
-                // TODO: pick a better starting time than now + 1 minute
-                long delay = 1000 * 60; // one minute, in milliseconds
-                mAlarmManager.set(AlarmManager.RTC_WAKEUP,
-                        System.currentTimeMillis() + delay, mRunInitIntent);
-            }
-        }
-    }
-
-    // ----- Track installation/removal of packages -----
-    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
-
-            String action = intent.getAction();
-            boolean replacing = false;
-            boolean added = false;
-            boolean changed = false;
-            Bundle extras = intent.getExtras();
-            String[] pkgList = null;
-            if (Intent.ACTION_PACKAGE_ADDED.equals(action)
-                    || Intent.ACTION_PACKAGE_REMOVED.equals(action)
-                    || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
-                Uri uri = intent.getData();
-                if (uri == null) {
-                    return;
-                }
-                final String pkgName = uri.getSchemeSpecificPart();
-                if (pkgName != null) {
-                    pkgList = new String[]{pkgName};
-                }
-                changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
-
-                // At package-changed we only care about looking at new transport states
-                if (changed) {
-                    final String[] components =
-                            intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
-
-                    if (MORE_DEBUG) {
-                        Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
-                        for (int i = 0; i < components.length; i++) {
-                            Slog.i(TAG, "   * " + components[i]);
-                        }
-                    }
-
-                    mBackupHandler.post(
-                            () -> mTransportManager.onPackageChanged(pkgName, components));
-                    return; // nothing more to do in the PACKAGE_CHANGED case
-                }
-
-                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
-                replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
-            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
-                added = true;
-                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-                added = false;
-                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-            }
-
-            if (pkgList == null || pkgList.length == 0) {
-                return;
-            }
-
-            final int uid = extras.getInt(Intent.EXTRA_UID);
-            if (added) {
-                synchronized (mBackupParticipants) {
-                    if (replacing) {
-                        // This is the package-replaced case; we just remove the entry
-                        // under the old uid and fall through to re-add.  If an app
-                        // just added key/value backup participation, this picks it up
-                        // as a known participant.
-                        removePackageParticipantsLocked(pkgList, uid);
-                    }
-                    addPackageParticipantsLocked(pkgList);
-                }
-                // If they're full-backup candidates, add them there instead
-                final long now = System.currentTimeMillis();
-                for (final String packageName : pkgList) {
-                    try {
-                        PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
-                        if (AppBackupUtils.appGetsFullBackup(app)
-                                && AppBackupUtils.appIsEligibleForBackup(
-                                app.applicationInfo, mPackageManager)) {
-                            enqueueFullBackup(packageName, now);
-                            scheduleNextFullBackupJob(0);
-                        } else {
-                            // The app might have just transitioned out of full-data into
-                            // doing key/value backups, or might have just disabled backups
-                            // entirely.  Make sure it is no longer in the full-data queue.
-                            synchronized (mQueueLock) {
-                                dequeueFullBackupLocked(packageName);
-                            }
-                            writeFullBackupScheduleAsync();
-                        }
-
-                        mBackupHandler.post(
-                                () -> mTransportManager.onPackageAdded(packageName));
-
-                    } catch (NameNotFoundException e) {
-                        // doesn't really exist; ignore it
-                        if (DEBUG) {
-                            Slog.w(TAG, "Can't resolve new app " + packageName);
-                        }
-                    }
-                }
-
-                // Whenever a package is added or updated we need to update
-                // the package metadata bookkeeping.
-                dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
-            } else {
-                if (replacing) {
-                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
-                } else {
-                    // Outright removal.  In the full-data case, the app will be dropped
-                    // from the queue when its (now obsolete) name comes up again for
-                    // backup.
-                    synchronized (mBackupParticipants) {
-                        removePackageParticipantsLocked(pkgList, uid);
-                    }
-                }
-                for (final String pkgName : pkgList) {
-                    mBackupHandler.post(
-                            () -> mTransportManager.onPackageRemoved(pkgName));
-                }
-            }
-        }
-    };
-
-    // Add the backup agents in the given packages to our set of known backup participants.
-    // If 'packageNames' is null, adds all backup agents in the whole system.
-    private void addPackageParticipantsLocked(String[] packageNames) {
-        // Look for apps that define the android:backupAgent attribute
-        List<PackageInfo> targetApps = allAgentPackages();
-        if (packageNames != null) {
-            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
-            for (String packageName : packageNames) {
-                addPackageParticipantsLockedInner(packageName, targetApps);
-            }
-        } else {
-            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
-            addPackageParticipantsLockedInner(null, targetApps);
-        }
-    }
-
-    private void addPackageParticipantsLockedInner(String packageName,
-            List<PackageInfo> targetPkgs) {
-        if (MORE_DEBUG) {
-            Slog.v(TAG, "Examining " + packageName + " for backup agent");
-        }
-
-        for (PackageInfo pkg : targetPkgs) {
-            if (packageName == null || pkg.packageName.equals(packageName)) {
-                int uid = pkg.applicationInfo.uid;
-                HashSet<String> set = mBackupParticipants.get(uid);
-                if (set == null) {
-                    set = new HashSet<>();
-                    mBackupParticipants.put(uid, set);
-                }
-                set.add(pkg.packageName);
-                if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
-
-                // Schedule a backup for it on general principles
-                if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
-                Message msg = mBackupHandler
-                        .obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
-                mBackupHandler.sendMessage(msg);
-            }
-        }
-    }
-
-    // Remove the given packages' entries from our known active set.
-    private void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
-        if (packageNames == null) {
-            Slog.w(TAG, "removePackageParticipants with null list");
-            return;
-        }
-
-        if (MORE_DEBUG) {
-            Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
-                    + " #" + packageNames.length);
-        }
-        for (String pkg : packageNames) {
-            // Known previous UID, so we know which package set to check
-            HashSet<String> set = mBackupParticipants.get(oldUid);
-            if (set != null && set.contains(pkg)) {
-                removePackageFromSetLocked(set, pkg);
-                if (set.isEmpty()) {
-                    if (MORE_DEBUG) Slog.v(TAG, "  last one of this uid; purging set");
-                    mBackupParticipants.remove(oldUid);
-                }
-            }
-        }
-    }
-
-    private void removePackageFromSetLocked(final HashSet<String> set,
-            final String packageName) {
-        if (set.contains(packageName)) {
-            // Found it.  Remove this one package from the bookkeeping, and
-            // if it's the last participating app under this uid we drop the
-            // (now-empty) set as well.
-            // Note that we deliberately leave it 'known' in the "ever backed up"
-            // bookkeeping so that its current-dataset data will be retrieved
-            // if the app is subsequently reinstalled
-            if (MORE_DEBUG) Slog.v(TAG, "  removing participant " + packageName);
-            set.remove(packageName);
-            mPendingBackups.remove(packageName);
-        }
-    }
-
-    // Returns the set of all applications that define an android:backupAgent attribute
-    private List<PackageInfo> allAgentPackages() {
-        // !!! TODO: cache this and regenerate only when necessary
-        int flags = PackageManager.GET_SIGNING_CERTIFICATES;
-        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
-        int numPackages = packages.size();
-        for (int a = numPackages - 1; a >= 0; a--) {
-            PackageInfo pkg = packages.get(a);
-            try {
-                ApplicationInfo app = pkg.applicationInfo;
-                if (((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
-                        || app.backupAgentName == null
-                        || (app.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0) {
-                    packages.remove(a);
-                } else {
-                    // we will need the shared library path, so look that up and store it here.
-                    // This is used implicitly when we pass the PackageInfo object off to
-                    // the Activity Manager to launch the app for backup/restore purposes.
-                    app = mPackageManager.getApplicationInfo(pkg.packageName,
-                            PackageManager.GET_SHARED_LIBRARY_FILES);
-                    pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
-                    pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
-                }
-            } catch (NameNotFoundException e) {
-                packages.remove(a);
-            }
-        }
-        return packages;
-    }
-
-    /**
-     * Called from the backup tasks: record that the given app has been successfully backed up at
-     * least once. This includes both key/value and full-data backups through the transport.
-     */
-    public void logBackupComplete(String packageName) {
-        if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
-
-        for (String receiver : mConstants.getBackupFinishedNotificationReceivers()) {
-            final Intent notification = new Intent();
-            notification.setAction(BACKUP_FINISHED_ACTION);
-            notification.setPackage(receiver);
-            notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES
-                    | Intent.FLAG_RECEIVER_FOREGROUND);
-            notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
-            mContext.sendBroadcastAsUser(notification, UserHandle.OWNER);
-        }
-
-        mProcessedPackagesJournal.addPackage(packageName);
-    }
-
-    /**
-     * Persistently record the current and ancestral backup tokens, as well as the set of packages
-     * with data available in the ancestral dataset.
-     */
-    public void writeRestoreTokens() {
-        try (RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd")) {
-            // First, the version number of this record, for futureproofing
-            af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
-
-            // Write the ancestral and current tokens
-            af.writeLong(mAncestralToken);
-            af.writeLong(mCurrentToken);
-
-            // Now write the set of ancestral packages
-            if (mAncestralPackages == null) {
-                af.writeInt(-1);
-            } else {
-                af.writeInt(mAncestralPackages.size());
-                if (DEBUG) Slog.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
-                for (String pkgName : mAncestralPackages) {
-                    af.writeUTF(pkgName);
-                    if (MORE_DEBUG) Slog.v(TAG, "   " + pkgName);
-                }
-            }
-        } catch (IOException e) {
-            Slog.w(TAG, "Unable to write token file:", e);
-        }
-    }
-
-    /** Fires off a backup agent, blocking until it attaches or times out. */
-    @Nullable
-    public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
-        IBackupAgent agent = null;
-        synchronized (mAgentConnectLock) {
-            mConnecting = true;
-            mConnectedAgent = null;
-            try {
-                if (mActivityManager.bindBackupAgent(app.packageName, mode,
-                        UserHandle.USER_OWNER)) {
-                    Slog.d(TAG, "awaiting agent for " + app);
-
-                    // success; wait for the agent to arrive
-                    // only wait 10 seconds for the bind to happen
-                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
-                    while (mConnecting && mConnectedAgent == null
-                            && (System.currentTimeMillis() < timeoutMark)) {
-                        try {
-                            mAgentConnectLock.wait(5000);
-                        } catch (InterruptedException e) {
-                            // just bail
-                            Slog.w(TAG, "Interrupted: " + e);
-                            mConnecting = false;
-                            mConnectedAgent = null;
-                        }
-                    }
-
-                    // if we timed out with no connect, abort and move on
-                    if (mConnecting) {
-                        Slog.w(TAG, "Timeout waiting for agent " + app);
-                        mConnectedAgent = null;
-                    }
-                    if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
-                    agent = mConnectedAgent;
-                }
-            } catch (RemoteException e) {
-                // can't happen - ActivityManager is local
-            }
-        }
-        if (agent == null) {
-            try {
-                mActivityManager.clearPendingBackup();
-            } catch (RemoteException e) {
-                // can't happen - ActivityManager is local
-            }
-        }
-        return agent;
-    }
-
-    /** Unbind from a backup agent. */
-    public void unbindAgent(ApplicationInfo app) {
-        try {
-            mActivityManager.unbindBackupAgent(app);
-        } catch (RemoteException e) {
-            // Can't happen - activity manager is local
-        }
-    }
-
-    /**
-     * Clear an application's data, blocking until the operation completes or times out. If {@code
-     * keepSystemState} is {@code true}, we intentionally do not clear system state that would
-     * ordinarily also be cleared, because we aren't actually wiping the app back to empty; we're
-     * bringing it into the actual expected state related to the already-restored notification state
-     * etc.
-     */
-    public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
-        // Don't wipe packages marked allowClearUserData=false
-        try {
-            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
-            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
-                if (MORE_DEBUG) {
-                    Slog.i(TAG, "allowClearUserData=false so not wiping "
-                            + packageName);
-                }
-                return;
-            }
-        } catch (NameNotFoundException e) {
-            Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
-            return;
-        }
-
-        ClearDataObserver observer = new ClearDataObserver(this);
-
-        synchronized (mClearDataLock) {
-            mClearingData = true;
-            try {
-                mActivityManager.clearApplicationUserData(
-                        packageName, keepSystemState, observer, 0);
-            } catch (RemoteException e) {
-                // can't happen because the activity manager is in this process
-            }
-
-            // only wait 10 seconds for the clear data to happen
-            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
-            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
-                try {
-                    mClearDataLock.wait(5000);
-                } catch (InterruptedException e) {
-                    // won't happen, but still.
-                    mClearingData = false;
-                }
-            }
-        }
-    }
-
-    /**
-     * Get the restore-set token for the best-available restore set for this {@code packageName}:
-     * the active set if possible, else the ancestral one. Returns zero if none available.
-     */
-    public long getAvailableRestoreToken(String packageName) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "getAvailableRestoreToken");
-
-        long token = mAncestralToken;
-        synchronized (mQueueLock) {
-            if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
-                if (MORE_DEBUG) {
-                    Slog.i(TAG, "App in ever-stored, so using current token");
-                }
-                token = mCurrentToken;
-            }
-        }
-        if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
-        return token;
-    }
-
-    /**
-     * Requests a backup for the inputted {@code packages}.
-     *
-     * @see #requestBackup(String[], IBackupObserver, IBackupManagerMonitor, int).
-     */
-    public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
-        return requestBackup(packages, observer, null, flags);
-    }
-
-    /**
-     * Requests a backup for the inputted {@code packages} with a specified {@link
-     * IBackupManagerMonitor}.
-     */
-    public int requestBackup(String[] packages, IBackupObserver observer,
-            IBackupManagerMonitor monitor, int flags) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
-
-        if (packages == null || packages.length < 1) {
-            Slog.e(TAG, "No packages named for backup request");
-            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
-            monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
-                    BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
-                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
-            throw new IllegalArgumentException("No packages are provided for backup");
-        }
-
-        if (!mEnabled || !mProvisioned) {
-            Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
-            BackupObserverUtils.sendBackupFinished(observer,
-                    BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-            final int logTag = mProvisioned
-                    ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
-                    : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
-            monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
-                    BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
-            return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
-        }
-
-        final TransportClient transportClient;
-        final String transportDirName;
-        try {
-            transportDirName =
-                    mTransportManager.getTransportDirName(
-                            mTransportManager.getCurrentTransportName());
-            transportClient =
-                    mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
-        } catch (TransportNotRegisteredException e) {
-            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
-            monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
-                    BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
-                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
-            return BackupManager.ERROR_TRANSPORT_ABORTED;
-        }
-
-        OnTaskFinishedListener listener =
-                caller -> mTransportManager.disposeOfTransportClient(transportClient, caller);
-
-        ArrayList<String> fullBackupList = new ArrayList<>();
-        ArrayList<String> kvBackupList = new ArrayList<>();
-        for (String packageName : packages) {
-            if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
-                kvBackupList.add(packageName);
-                continue;
-            }
-            try {
-                PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
-                        PackageManager.GET_SIGNING_CERTIFICATES);
-                if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
-                        mPackageManager)) {
-                    BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
-                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-                    continue;
-                }
-                if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
-                    fullBackupList.add(packageInfo.packageName);
-                } else {
-                    kvBackupList.add(packageInfo.packageName);
-                }
-            } catch (NameNotFoundException e) {
-                BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
-                        BackupManager.ERROR_PACKAGE_NOT_FOUND);
-            }
-        }
-        EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
-                fullBackupList.size());
-        if (MORE_DEBUG) {
-            Slog.i(TAG, "Backup requested for " + packages.length + " packages, of them: "
-                    + fullBackupList.size() + " full backups, " + kvBackupList.size()
-                    + " k/v backups");
-        }
-
-        boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
-
-        Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
-        msg.obj = new BackupParams(transportClient, transportDirName, kvBackupList, fullBackupList,
-                observer, monitor, listener, true, nonIncrementalBackup);
-        mBackupHandler.sendMessage(msg);
-        return BackupManager.SUCCESS;
-    }
-
-    /** Cancel all running backups. */
-    public void cancelBackups() {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
-        if (MORE_DEBUG) {
-            Slog.i(TAG, "cancelBackups() called.");
-        }
-        final long oldToken = Binder.clearCallingIdentity();
-        try {
-            List<Integer> operationsToCancel = new ArrayList<>();
-            synchronized (mCurrentOpLock) {
-                for (int i = 0; i < mCurrentOperations.size(); i++) {
-                    Operation op = mCurrentOperations.valueAt(i);
-                    int token = mCurrentOperations.keyAt(i);
-                    if (op.type == OP_TYPE_BACKUP) {
-                        operationsToCancel.add(token);
-                    }
-                }
-            }
-            for (Integer token : operationsToCancel) {
-                handleCancel(token, true /* cancelAll */);
-            }
-            // We don't want the backup jobs to kick in any time soon.
-            // Reschedules them to run in the distant future.
-            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
-            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
-        } finally {
-            Binder.restoreCallingIdentity(oldToken);
-        }
-    }
-
-    /** Schedule a timeout message for the operation identified by {@code token}. */
-    public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
-            int operationType) {
-        if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
-            Slog.wtf(TAG, "prepareOperationTimeout() doesn't support operation "
-                    + Integer.toHexString(token) + " of type " + operationType);
-            return;
-        }
-        if (MORE_DEBUG) {
-            Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
-                    + " interval=" + interval + " callback=" + callback);
-        }
-
-        synchronized (mCurrentOpLock) {
-            mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
-            Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
-                    token, 0, callback);
-            mBackupHandler.sendMessageDelayed(msg, interval);
-        }
-    }
-
-    private int getMessageIdForOperationType(int operationType) {
-        switch (operationType) {
-            case OP_TYPE_BACKUP_WAIT:
-                return MSG_BACKUP_OPERATION_TIMEOUT;
-            case OP_TYPE_RESTORE_WAIT:
-                return MSG_RESTORE_OPERATION_TIMEOUT;
-            default:
-                Slog.wtf(TAG, "getMessageIdForOperationType called on invalid operation type: "
-                        + operationType);
-                return -1;
-        }
-    }
-
-    /**
-     * Add an operation to the list of currently running operations. Used for cancellation,
-     * completion and timeout callbacks that act on the operation via the {@code token}.
-     */
-    public void putOperation(int token, Operation operation) {
-        if (MORE_DEBUG) {
-            Slog.d(TAG, "Adding operation token=" + Integer.toHexString(token) + ", operation type="
-                    + operation.type);
-        }
-        synchronized (mCurrentOpLock) {
-            mCurrentOperations.put(token, operation);
-        }
-    }
-
-    /**
-     * Remove an operation from the list of currently running operations. An operation is removed
-     * when it is completed, cancelled, or timed out, and thus no longer running.
-     */
-    public void removeOperation(int token) {
-        if (MORE_DEBUG) {
-            Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
-        }
-        synchronized (mCurrentOpLock) {
-            if (mCurrentOperations.get(token) == null) {
-                Slog.w(TAG, "Duplicate remove for operation. token="
-                        + Integer.toHexString(token));
-            }
-            mCurrentOperations.remove(token);
-        }
-    }
-
-    /** Block until we received an operation complete message (from the agent or cancellation). */
-    public boolean waitUntilOperationComplete(int token) {
-        if (MORE_DEBUG) {
-            Slog.i(TAG, "Blocking until operation complete for "
-                    + Integer.toHexString(token));
-        }
-        int finalState = OP_PENDING;
-        Operation op = null;
-        synchronized (mCurrentOpLock) {
-            while (true) {
-                op = mCurrentOperations.get(token);
-                if (op == null) {
-                    // mysterious disappearance: treat as success with no callback
-                    break;
-                } else {
-                    if (op.state == OP_PENDING) {
-                        try {
-                            mCurrentOpLock.wait();
-                        } catch (InterruptedException e) {
-                        }
-                        // When the wait is notified we loop around and recheck the current state
-                    } else {
-                        if (MORE_DEBUG) {
-                            Slog.d(TAG, "Unblocked waiting for operation token="
-                                    + Integer.toHexString(token));
-                        }
-                        // No longer pending; we're done
-                        finalState = op.state;
-                        break;
-                    }
-                }
-            }
-        }
-
-        removeOperation(token);
-        if (op != null) {
-            mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
-        }
-        if (MORE_DEBUG) {
-            Slog.v(TAG, "operation " + Integer.toHexString(token)
-                    + " complete: finalState=" + finalState);
-        }
-        return finalState == OP_ACKNOWLEDGED;
-    }
-
-    /** Cancel the operation associated with {@code token}. */
-    public void handleCancel(int token, boolean cancelAll) {
-        // Notify any synchronous waiters
-        Operation op = null;
-        synchronized (mCurrentOpLock) {
-            op = mCurrentOperations.get(token);
-            if (MORE_DEBUG) {
-                if (op == null) {
-                    Slog.w(TAG, "Cancel of token " + Integer.toHexString(token)
-                            + " but no op found");
-                }
-            }
-            int state = (op != null) ? op.state : OP_TIMEOUT;
-            if (state == OP_ACKNOWLEDGED) {
-                // The operation finished cleanly, so we have nothing more to do.
-                if (DEBUG) {
-                    Slog.w(TAG, "Operation already got an ack."
-                            + "Should have been removed from mCurrentOperations.");
-                }
-                op = null;
-                mCurrentOperations.delete(token);
-            } else if (state == OP_PENDING) {
-                if (DEBUG) Slog.v(TAG, "Cancel: token=" + Integer.toHexString(token));
-                op.state = OP_TIMEOUT;
-                // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
-                // called after we receive cancel here. We need this op's state there.
-
-                // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and
-                // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
-                // doesn't require cancellation.
-                if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) {
-                    mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
-                }
-            }
-            mCurrentOpLock.notifyAll();
-        }
-
-        // If there's a TimeoutHandler for this event, call it
-        if (op != null && op.callback != null) {
-            if (MORE_DEBUG) {
-                Slog.v(TAG, "   Invoking cancel on " + op.callback);
-            }
-            op.callback.handleCancel(cancelAll);
-        }
-    }
-
-    /** Returns {@code true} if a backup is currently running, else returns {@code false}. */
-    public boolean isBackupOperationInProgress() {
-        synchronized (mCurrentOpLock) {
-            for (int i = 0; i < mCurrentOperations.size(); i++) {
-                Operation op = mCurrentOperations.valueAt(i);
-                if (op.type == OP_TYPE_BACKUP && op.state == OP_PENDING) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /** Unbind the backup agent and kill the app if it's a non-system app. */
-    public void tearDownAgentAndKill(ApplicationInfo app) {
-        if (app == null) {
-            // Null means the system package, so just quietly move on.  :)
-            return;
-        }
-
-        try {
-            // unbind and tidy up even on timeout or failure, just in case
-            mActivityManager.unbindBackupAgent(app);
-
-            // The agent was running with a stub Application object, so shut it down.
-            // !!! We hardcode the confirmation UI's package name here rather than use a
-            //     manifest flag!  TODO something less direct.
-            if (app.uid >= Process.FIRST_APPLICATION_UID
-                    && !app.packageName.equals("com.android.backupconfirm")) {
-                if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
-                mActivityManager.killApplicationProcess(app.processName, app.uid);
-            } else {
-                if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
-            }
-        } catch (RemoteException e) {
-            Slog.d(TAG, "Lost app trying to shut down");
-        }
-    }
-
-    /** For adb backup/restore. */
-    public boolean deviceIsEncrypted() {
-        try {
-            return mStorageManager.getEncryptionState()
-                    != StorageManager.ENCRYPTION_STATE_NONE
-                    && mStorageManager.getPasswordType()
-                    != StorageManager.CRYPT_TYPE_DEFAULT;
-        } catch (Exception e) {
-            // If we can't talk to the storagemanager service we have a serious problem; fail
-            // "secure" i.e. assuming that the device is encrypted.
-            Slog.e(TAG, "Unable to communicate with storagemanager service: " + e.getMessage());
-            return true;
-        }
-    }
-
-    // ----- Full-data backup scheduling -----
-
-    /**
-     * Schedule a job to tell us when it's a good time to run a full backup
-     */
-    public void scheduleNextFullBackupJob(long transportMinLatency) {
-        synchronized (mQueueLock) {
-            if (mFullBackupQueue.size() > 0) {
-                // schedule the next job at the point in the future when the least-recently
-                // backed up app comes due for backup again; or immediately if it's already
-                // due.
-                final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
-                final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
-                final long interval = mConstants.getFullBackupIntervalMilliseconds();
-                final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0;
-                final long latency = Math.max(transportMinLatency, appLatency);
-                Runnable r = new Runnable() {
-                    @Override
-                    public void run() {
-                        FullBackupJob.schedule(mContext, latency, mConstants);
-                    }
-                };
-                mBackupHandler.postDelayed(r, 2500);
-            } else {
-                if (DEBUG_SCHEDULING) {
-                    Slog.i(TAG, "Full backup queue empty; not scheduling");
-                }
-            }
-        }
-    }
-
-    /**
-     * Remove a package from the full-data queue.
-     */
-    @GuardedBy("mQueueLock")
-    private void dequeueFullBackupLocked(String packageName) {
-        final int numPackages = mFullBackupQueue.size();
-        for (int i = numPackages - 1; i >= 0; i--) {
-            final FullBackupEntry e = mFullBackupQueue.get(i);
-            if (packageName.equals(e.packageName)) {
-                mFullBackupQueue.remove(i);
-            }
-        }
-    }
-
-    /**
-     * Enqueue full backup for the given app, with a note about when it last ran.
-     */
-    public void enqueueFullBackup(String packageName, long lastBackedUp) {
-        FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
-        synchronized (mQueueLock) {
-            // First, sanity check that we aren't adding a duplicate.  Slow but
-            // straightforward; we'll have at most on the order of a few hundred
-            // items in this list.
-            dequeueFullBackupLocked(packageName);
-
-            // This is also slow but easy for modest numbers of apps: work backwards
-            // from the end of the queue until we find an item whose last backup
-            // time was before this one, then insert this new entry after it.  If we're
-            // adding something new we don't bother scanning, and just prepend.
-            int which = -1;
-            if (lastBackedUp > 0) {
-                for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
-                    final FullBackupEntry entry = mFullBackupQueue.get(which);
-                    if (entry.lastBackup <= lastBackedUp) {
-                        mFullBackupQueue.add(which + 1, newEntry);
-                        break;
-                    }
-                }
-            }
-            if (which < 0) {
-                // this one is earlier than any existing one, so prepend
-                mFullBackupQueue.add(0, newEntry);
-            }
-        }
-        writeFullBackupScheduleAsync();
-    }
-
-    private boolean fullBackupAllowable(String transportName) {
-        if (!mTransportManager.isTransportRegistered(transportName)) {
-            Slog.w(TAG, "Transport not registered; full data backup not performed");
-            return false;
-        }
-
-        // Don't proceed unless we have already established package metadata
-        // for the current dataset via a key/value backup pass.
-        try {
-            String transportDirName = mTransportManager.getTransportDirName(transportName);
-            File stateDir = new File(mBaseStateDir, transportDirName);
-            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
-            if (pmState.length() <= 0) {
-                if (DEBUG) {
-                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
-                }
-                return false;
-            }
-        } catch (Exception e) {
-            Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Conditions are right for a full backup operation, so run one.  The model we use is
-     * to perform one app backup per scheduled job execution, and to reschedule the job
-     * with zero latency as long as conditions remain right and we still have work to do.
-     *
-     * <p>This is the "start a full backup operation" entry point called by the scheduled job.
-     *
-     * @return Whether ongoing work will continue.  The return value here will be passed
-     * along as the return value to the scheduled job's onStartJob() callback.
-     */
-    public boolean beginFullBackup(FullBackupJob scheduledJob) {
-        final long now = System.currentTimeMillis();
-        final long fullBackupInterval;
-        final long keyValueBackupInterval;
-        synchronized (mConstants) {
-            fullBackupInterval = mConstants.getFullBackupIntervalMilliseconds();
-            keyValueBackupInterval = mConstants.getKeyValueBackupIntervalMilliseconds();
-        }
-        FullBackupEntry entry = null;
-        long latency = fullBackupInterval;
-
-        if (!mEnabled || !mProvisioned) {
-            // Backups are globally disabled, so don't proceed.  We also don't reschedule
-            // the job driving automatic backups; that job will be scheduled again when
-            // the user enables backup.
-            if (MORE_DEBUG) {
-                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
-                        + " p=" + mProvisioned + "; ignoring");
-            }
-            return false;
-        }
-
-        // Don't run the backup if we're in battery saver mode, but reschedule
-        // to try again in the not-so-distant future.
-        final PowerSaveState result =
-                mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
-        if (result.batterySaverEnabled) {
-            if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
-            FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
-            return false;
-        }
-
-        if (DEBUG_SCHEDULING) {
-            Slog.i(TAG, "Beginning scheduled full backup operation");
-        }
-
-        // Great; we're able to run full backup jobs now.  See if we have any work to do.
-        synchronized (mQueueLock) {
-            if (mRunningFullBackupTask != null) {
-                Slog.e(TAG, "Backup triggered but one already/still running!");
-                return false;
-            }
-
-            // At this point we think that we have work to do, but possibly not right now.
-            // Any exit without actually running backups will also require that we
-            // reschedule the job.
-            boolean runBackup = true;
-            boolean headBusy;
-
-            do {
-                // Recheck each time, because culling due to ineligibility may
-                // have emptied the queue.
-                if (mFullBackupQueue.size() == 0) {
-                    // no work to do so just bow out
-                    if (DEBUG) {
-                        Slog.i(TAG, "Backup queue empty; doing nothing");
-                    }
-                    runBackup = false;
-                    break;
-                }
-
-                headBusy = false;
-
-                String transportName = mTransportManager.getCurrentTransportName();
-                if (!fullBackupAllowable(transportName)) {
-                    if (MORE_DEBUG) {
-                        Slog.i(TAG, "Preconditions not met; not running full backup");
-                    }
-                    runBackup = false;
-                    // Typically this means we haven't run a key/value backup yet.  Back off
-                    // full-backup operations by the key/value job's run interval so that
-                    // next time we run, we are likely to be able to make progress.
-                    latency = keyValueBackupInterval;
-                }
-
-                if (runBackup) {
-                    entry = mFullBackupQueue.get(0);
-                    long timeSinceRun = now - entry.lastBackup;
-                    runBackup = (timeSinceRun >= fullBackupInterval);
-                    if (!runBackup) {
-                        // It's too early to back up the next thing in the queue, so bow out
-                        if (MORE_DEBUG) {
-                            Slog.i(TAG, "Device ready but too early to back up next app");
-                        }
-                        // Wait until the next app in the queue falls due for a full data backup
-                        latency = fullBackupInterval - timeSinceRun;
-                        break;  // we know we aren't doing work yet, so bail.
-                    }
-
-                    try {
-                        PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
-                        if (!AppBackupUtils.appGetsFullBackup(appInfo)) {
-                            // The head app isn't supposed to get full-data backups [any more];
-                            // so we cull it and force a loop around to consider the new head
-                            // app.
-                            if (MORE_DEBUG) {
-                                Slog.i(TAG, "Culling package " + entry.packageName
-                                        + " in full-backup queue but not eligible");
-                            }
-                            mFullBackupQueue.remove(0);
-                            headBusy = true; // force the while() condition
-                            continue;
-                        }
-
-                        final int privFlags = appInfo.applicationInfo.privateFlags;
-                        headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
-                                && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
-
-                        if (headBusy) {
-                            final long nextEligible = System.currentTimeMillis()
-                                    + BUSY_BACKOFF_MIN_MILLIS
-                                    + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
-                            if (DEBUG_SCHEDULING) {
-                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-                                Slog.i(TAG, "Full backup time but " + entry.packageName
-                                        + " is busy; deferring to "
-                                        + sdf.format(new Date(nextEligible)));
-                            }
-                            // This relocates the app's entry from the head of the queue to
-                            // its order-appropriate position further down, so upon looping
-                            // a new candidate will be considered at the head.
-                            enqueueFullBackup(entry.packageName, nextEligible - fullBackupInterval);
-                        }
-                    } catch (NameNotFoundException nnf) {
-                        // So, we think we want to back this up, but it turns out the package
-                        // in question is no longer installed.  We want to drop it from the
-                        // queue entirely and move on, but if there's nothing else in the queue
-                        // we should bail entirely.  headBusy cannot have been set to true yet.
-                        runBackup = (mFullBackupQueue.size() > 1);
-                    } catch (RemoteException e) {
-                        // Cannot happen; the Activity Manager is in the same process
-                    }
-                }
-            } while (headBusy);
-
-            if (!runBackup) {
-                if (DEBUG_SCHEDULING) {
-                    Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
-                }
-                final long deferTime = latency;     // pin for the closure
-                mBackupHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        FullBackupJob.schedule(mContext, deferTime, mConstants);
-                    }
-                });
-                return false;
-            }
-
-            // Okay, the top thing is ready for backup now.  Do it.
-            mFullBackupQueue.remove(0);
-            CountDownLatch latch = new CountDownLatch(1);
-            String[] pkg = new String[]{entry.packageName};
-            mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
-                    this,
-                    /* observer */ null,
-                    pkg,
-                    /* updateSchedule */ true,
-                    scheduledJob,
-                    latch,
-                    /* backupObserver */ null,
-                    /* monitor */ null,
-                    /* userInitiated */ false,
-                    "BMS.beginFullBackup()");
-            // Acquiring wakelock for PerformFullTransportBackupTask before its start.
-            mWakelock.acquire();
-            (new Thread(mRunningFullBackupTask)).start();
-        }
-
-        return true;
-    }
-
-    /**
-     * The job scheduler says our constraints don't hold anymore, so tear down any ongoing backup
-     * task right away.
-     */
-    public void endFullBackup() {
-        // offload the mRunningFullBackupTask.handleCancel() call to another thread,
-        // as we might have to wait for mCancelLock
-        Runnable endFullBackupRunnable = new Runnable() {
-            @Override
-            public void run() {
-                PerformFullTransportBackupTask pftbt = null;
-                synchronized (mQueueLock) {
-                    if (mRunningFullBackupTask != null) {
-                        pftbt = mRunningFullBackupTask;
-                    }
-                }
-                if (pftbt != null) {
-                    if (DEBUG_SCHEDULING) {
-                        Slog.i(TAG, "Telling running backup to stop");
-                    }
-                    pftbt.handleCancel(true);
-                }
-            }
-        };
-        new Thread(endFullBackupRunnable, "end-full-backup").start();
-    }
-
-    /** Used by both incremental and full restore to restore widget data. */
-    public void restoreWidgetData(String packageName, byte[] widgetData) {
-        // Apply the restored widget state and generate the ID update for the app
-        // TODO: http://b/22388012
-        if (MORE_DEBUG) {
-            Slog.i(TAG, "Incorporating restored widget data");
-        }
-        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
-    }
-
-    // *****************************
-    // NEW UNIFIED RESTORE IMPLEMENTATION
-    // *****************************
-
-    /** Schedule a backup pass for {@code packageName}. */
-    public void dataChangedImpl(String packageName) {
-        HashSet<String> targets = dataChangedTargets(packageName);
-        dataChangedImpl(packageName, targets);
-    }
-
-    private void dataChangedImpl(String packageName, HashSet<String> targets) {
-        // Record that we need a backup pass for the caller.  Since multiple callers
-        // may share a uid, we need to note all candidates within that uid and schedule
-        // a backup pass for each of them.
-        if (targets == null) {
-            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
-                    + " uid=" + Binder.getCallingUid());
-            return;
-        }
-
-        synchronized (mQueueLock) {
-            // Note that this client has made data changes that need to be backed up
-            if (targets.contains(packageName)) {
-                // Add the caller to the set of pending backups.  If there is
-                // one already there, then overwrite it, but no harm done.
-                BackupRequest req = new BackupRequest(packageName);
-                if (mPendingBackups.put(packageName, req) == null) {
-                    if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
-
-                    // Journal this request in case of crash.  The put()
-                    // operation returned null when this package was not already
-                    // in the set; we want to avoid touching the disk redundantly.
-                    writeToJournalLocked(packageName);
-                }
-            }
-        }
-
-        // ...and schedule a backup pass if necessary
-        KeyValueBackupJob.schedule(mContext, mConstants);
-    }
-
-    // Note: packageName is currently unused, but may be in the future
-    private HashSet<String> dataChangedTargets(String packageName) {
-        // If the caller does not hold the BACKUP permission, it can only request a
-        // backup of its own data.
-        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
-                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
-            synchronized (mBackupParticipants) {
-                return mBackupParticipants.get(Binder.getCallingUid());
-            }
-        }
-
-        // a caller with full permission can ask to back up any participating app
-        if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
-            return Sets.newHashSet(PACKAGE_MANAGER_SENTINEL);
-        } else {
-            synchronized (mBackupParticipants) {
-                return SparseArrayUtils.union(mBackupParticipants);
-            }
-        }
-    }
-
-    private void writeToJournalLocked(String str) {
-        try {
-            if (mJournal == null) mJournal = DataChangedJournal.newJournal(mJournalDir);
-            mJournal.addPackage(str);
-        } catch (IOException e) {
-            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
-            mJournal = null;
-        }
-    }
-
-    // ----- IBackupManager binder interface -----
-
-    /** Sent from an app's backup agent to let the service know that there's new data to backup. */
-    public void dataChanged(final String packageName) {
-        final int callingUserHandle = UserHandle.getCallingUserId();
-        if (callingUserHandle != UserHandle.USER_SYSTEM) {
-            // TODO: http://b/22388012
-            // App is running under a non-owner user profile.  For now, we do not back
-            // up data from secondary user profiles.
-            // TODO: backups for all user profiles although don't add backup for profiles
-            // without adding admin control in DevicePolicyManager.
-            if (MORE_DEBUG) {
-                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
-                        + callingUserHandle);
-            }
-            return;
-        }
-
-        final HashSet<String> targets = dataChangedTargets(packageName);
-        if (targets == null) {
-            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
-                    + " uid=" + Binder.getCallingUid());
-            return;
-        }
-
-        mBackupHandler.post(new Runnable() {
-            public void run() {
-                dataChangedImpl(packageName, targets);
-            }
-        });
-    }
-
-    /** Run an initialize operation for the given transport. */
+    /** Run an initialize operation for the given transports {@code transportNames}. */
     public void initializeTransports(String[] transportNames, IBackupObserver observer) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
-                "initializeTransport");
-        Slog.v(TAG, "initializeTransport(): " + Arrays.asList(transportNames));
-
-        final long oldId = Binder.clearCallingIdentity();
-        try {
-            mWakelock.acquire();
-            OnTaskFinishedListener listener = caller -> mWakelock.release();
-            mBackupHandler.post(
-                    new PerformInitializeTask(this, transportNames, observer, listener));
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
+        mUserBackupManagerService.initializeTransports(transportNames, observer);
     }
 
-    /** Clear the given package's backup data from the current transport. */
+    /**
+     * Clear the given package {@code packageName}'s backup data from the transport {@code
+     * transportName}.
+     */
     public void clearBackupData(String transportName, String packageName) {
-        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
-        PackageInfo info;
-        try {
-            info = mPackageManager.getPackageInfo(packageName,
-                    PackageManager.GET_SIGNING_CERTIFICATES);
-        } catch (NameNotFoundException e) {
-            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
-            return;
-        }
-
-        // If the caller does not hold the BACKUP permission, it can only request a
-        // wipe of its own backed-up data.
-        Set<String> apps;
-        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
-                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
-            apps = mBackupParticipants.get(Binder.getCallingUid());
-        } else {
-            // a caller with full permission can ask to back up any participating app
-            // !!! TODO: allow data-clear of ANY app?
-            if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
-            apps = mProcessedPackagesJournal.getPackagesCopy();
-        }
-
-        if (apps.contains(packageName)) {
-            // found it; fire off the clear request
-            if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
-            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
-            synchronized (mQueueLock) {
-                TransportClient transportClient =
-                        mTransportManager
-                                .getTransportClient(transportName, "BMS.clearBackupData()");
-                if (transportClient == null) {
-                    // transport is currently unregistered -- make sure to retry
-                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
-                            new ClearRetryParams(transportName, packageName));
-                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
-                    return;
-                }
-                long oldId = Binder.clearCallingIdentity();
-                OnTaskFinishedListener listener =
-                        caller ->
-                                mTransportManager.disposeOfTransportClient(transportClient, caller);
-                mWakelock.acquire();
-                Message msg = mBackupHandler.obtainMessage(
-                        MSG_RUN_CLEAR,
-                        new ClearParams(transportClient, info, listener));
-                mBackupHandler.sendMessage(msg);
-                Binder.restoreCallingIdentity(oldId);
-            }
-        }
+        mUserBackupManagerService.clearBackupData(transportName, packageName);
     }
 
-    /**
-     * Run a backup pass immediately for any applications that have declared that they have pending
-     * updates.
-     */
-    public void backupNow() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
-
-        long oldId = Binder.clearCallingIdentity();
-        try {
-            final PowerSaveState result =
-                    mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
-            if (result.batterySaverEnabled) {
-                if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
-                KeyValueBackupJob.schedule(mContext, mConstants);   // try again in several hours
-            } else {
-                if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
-                synchronized (mQueueLock) {
-                    // Fire the intent that kicks off the whole shebang...
-                    try {
-                        mRunBackupIntent.send();
-                    } catch (PendingIntent.CanceledException e) {
-                        // should never happen
-                        Slog.e(TAG, "run-backup intent cancelled!");
-                    }
-
-                    // ...and cancel any pending scheduled job, because we've just superseded it
-                    KeyValueBackupJob.cancel(mContext);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
-    /** Returns {@code true} if the system user has gone through SUW. */
-    public boolean deviceIsProvisioned() {
-        final ContentResolver resolver = mContext.getContentResolver();
-        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
-    }
-
-    /**
-     * Used by 'adb backup' to run a backup pass for packages supplied via the command line, writing
-     * the resulting data stream to the supplied {@code fd}. This method is synchronous and does not
-     * return to the caller until the backup has been completed. It requires on-screen confirmation
-     * by the user.
-     */
-    public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
-            boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
-            boolean compress, boolean doKeyValue, String[] pkgList) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");
-
-        final int callingUserHandle = UserHandle.getCallingUserId();
-        // TODO: http://b/22388012
-        if (callingUserHandle != UserHandle.USER_SYSTEM) {
-            throw new IllegalStateException("Backup supported only for the device owner");
-        }
-
-        // Validate
-        if (!doAllApps) {
-            if (!includeShared) {
-                // If we're backing up shared data (sdcard or equivalent), then we can run
-                // without any supplied app names.  Otherwise, we'd be doing no work, so
-                // report the error.
-                if (pkgList == null || pkgList.length == 0) {
-                    throw new IllegalArgumentException(
-                            "Backup requested but neither shared nor any apps named");
-                }
-            }
-        }
-
-        long oldId = Binder.clearCallingIdentity();
-        try {
-            // Doesn't make sense to do a full backup prior to setup
-            if (!deviceIsProvisioned()) {
-                Slog.i(TAG, "Backup not supported before setup");
-                return;
-            }
-
-            if (DEBUG) {
-                Slog.v(TAG, "Requesting backup: apks=" + includeApks + " obb=" + includeObbs
-                        + " shared=" + includeShared + " all=" + doAllApps + " system="
-                        + includeSystem + " includekeyvalue=" + doKeyValue + " pkgs=" + pkgList);
-            }
-            Slog.i(TAG, "Beginning adb backup...");
-
-            AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
-                    includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
-                    pkgList);
-            final int token = generateRandomIntegerToken();
-            synchronized (mAdbBackupRestoreConfirmations) {
-                mAdbBackupRestoreConfirmations.put(token, params);
-            }
-
-            // start up the confirmation UI
-            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
-            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
-                Slog.e(TAG, "Unable to launch backup confirmation UI");
-                mAdbBackupRestoreConfirmations.delete(token);
-                return;
-            }
-
-            // make sure the screen is lit for the user interaction
-            mPowerManager.userActivity(SystemClock.uptimeMillis(),
-                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
-                    0);
-
-            // start the confirmation countdown
-            startConfirmationTimeout(token, params);
-
-            // wait for the backup to be performed
-            if (DEBUG) Slog.d(TAG, "Waiting for backup completion...");
-            waitForCompletion(params);
-        } finally {
-            try {
-                fd.close();
-            } catch (IOException e) {
-                Slog.e(TAG, "IO error closing output for adb backup: " + e.getMessage());
-            }
-            Binder.restoreCallingIdentity(oldId);
-            Slog.d(TAG, "Adb backup processing complete.");
-        }
-    }
-
-    /** Run a full backup pass for the given packages. Used by 'adb shell bmgr'. */
-    public void fullTransportBackup(String[] pkgNames) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
-                "fullTransportBackup");
-
-        final int callingUserHandle = UserHandle.getCallingUserId();
-        // TODO: http://b/22388012
-        if (callingUserHandle != UserHandle.USER_SYSTEM) {
-            throw new IllegalStateException("Restore supported only for the device owner");
-        }
-
-        String transportName = mTransportManager.getCurrentTransportName();
-        if (!fullBackupAllowable(transportName)) {
-            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
-        } else {
-            if (DEBUG) {
-                Slog.d(TAG, "fullTransportBackup()");
-            }
-
-            final long oldId = Binder.clearCallingIdentity();
-            try {
-                CountDownLatch latch = new CountDownLatch(1);
-                Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport(
-                        this,
-                        /* observer */ null,
-                        pkgNames,
-                        /* updateSchedule */ false,
-                        /* runningJob */ null,
-                        latch,
-                        /* backupObserver */ null,
-                        /* monitor */ null,
-                        /* userInitiated */ false,
-                        "BMS.fullTransportBackup()");
-                // Acquiring wakelock for PerformFullTransportBackupTask before its start.
-                mWakelock.acquire();
-                (new Thread(task, "full-transport-master")).start();
-                do {
-                    try {
-                        latch.await();
-                        break;
-                    } catch (InterruptedException e) {
-                        // Just go back to waiting for the latch to indicate completion
-                    }
-                } while (true);
-
-                // We just ran a backup on these packages, so kick them to the end of the queue
-                final long now = System.currentTimeMillis();
-                for (String pkg : pkgNames) {
-                    enqueueFullBackup(pkg, now);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(oldId);
-            }
-        }
-
-        if (DEBUG) {
-            Slog.d(TAG, "Done with full transport backup.");
-        }
-    }
-
-    /**
-     * Used by 'adb restore' to run a restore pass, blocking until completion. Requires user
-     * confirmation.
-     */
-    public void adbRestore(ParcelFileDescriptor fd) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore");
-
-        final int callingUserHandle = UserHandle.getCallingUserId();
-        // TODO: http://b/22388012
-        if (callingUserHandle != UserHandle.USER_SYSTEM) {
-            throw new IllegalStateException("Restore supported only for the device owner");
-        }
-
-        long oldId = Binder.clearCallingIdentity();
-
-        try {
-            // Check whether the device has been provisioned -- we don't handle
-            // full restores prior to completing the setup process.
-            if (!deviceIsProvisioned()) {
-                Slog.i(TAG, "Full restore not permitted before setup");
-                return;
-            }
-
-            Slog.i(TAG, "Beginning restore...");
-
-            AdbRestoreParams params = new AdbRestoreParams(fd);
-            final int token = generateRandomIntegerToken();
-            synchronized (mAdbBackupRestoreConfirmations) {
-                mAdbBackupRestoreConfirmations.put(token, params);
-            }
-
-            // start up the confirmation UI
-            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
-            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
-                Slog.e(TAG, "Unable to launch restore confirmation");
-                mAdbBackupRestoreConfirmations.delete(token);
-                return;
-            }
-
-            // make sure the screen is lit for the user interaction
-            mPowerManager.userActivity(SystemClock.uptimeMillis(),
-                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
-                    0);
-
-            // start the confirmation countdown
-            startConfirmationTimeout(token, params);
-
-            // wait for the restore to be performed
-            if (DEBUG) Slog.d(TAG, "Waiting for restore completion...");
-            waitForCompletion(params);
-        } finally {
-            try {
-                fd.close();
-            } catch (IOException e) {
-                Slog.w(TAG, "Error trying to close fd after adb restore: " + e);
-            }
-            Binder.restoreCallingIdentity(oldId);
-            Slog.i(TAG, "adb restore processing complete.");
-        }
-    }
-
-    private boolean startConfirmationUi(int token, String action) {
-        try {
-            Intent confIntent = new Intent(action);
-            confIntent.setClassName("com.android.backupconfirm",
-                    "com.android.backupconfirm.BackupRestoreConfirmation");
-            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
-            confIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-            mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
-        } catch (ActivityNotFoundException e) {
-            return false;
-        }
-        return true;
-    }
-
-    private void startConfirmationTimeout(int token, AdbParams params) {
-        if (MORE_DEBUG) {
-            Slog.d(TAG, "Posting conf timeout msg after "
-                    + TIMEOUT_FULL_CONFIRMATION + " millis");
-        }
-        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
-                token, 0, params);
-        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
-    }
-
-    private void waitForCompletion(AdbParams params) {
-        synchronized (params.latch) {
-            while (!params.latch.get()) {
-                try {
-                    params.latch.wait();
-                } catch (InterruptedException e) { /* never interrupted */ }
-            }
-        }
-    }
-
-    /** Called when adb backup/restore has completed. */
-    public void signalAdbBackupRestoreCompletion(AdbParams params) {
-        synchronized (params.latch) {
-            params.latch.set(true);
-            params.latch.notifyAll();
-        }
-    }
-
-    /**
-     * Confirm that the previously-requested full backup/restore operation can proceed. This is used
-     * to require a user-facing disclosure about the operation.
-     */
-    public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
-            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
-        if (DEBUG) {
-            Slog.d(TAG, "acknowledgeAdbBackupOrRestore : token=" + token
-                    + " allow=" + allow);
-        }
-
-        // TODO: possibly require not just this signature-only permission, but even
-        // require that the specific designated confirmation-UI app uid is the caller?
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
-                "acknowledgeAdbBackupOrRestore");
-
-        long oldId = Binder.clearCallingIdentity();
-        try {
-
-            AdbParams params;
-            synchronized (mAdbBackupRestoreConfirmations) {
-                params = mAdbBackupRestoreConfirmations.get(token);
-                if (params != null) {
-                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
-                    mAdbBackupRestoreConfirmations.delete(token);
-
-                    if (allow) {
-                        final int verb = params instanceof AdbBackupParams
-                                ? MSG_RUN_ADB_BACKUP
-                                : MSG_RUN_ADB_RESTORE;
-
-                        params.observer = observer;
-                        params.curPassword = curPassword;
-
-                        params.encryptPassword = encPpassword;
-
-                        if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
-                        mWakelock.acquire();
-                        Message msg = mBackupHandler.obtainMessage(verb, params);
-                        mBackupHandler.sendMessage(msg);
-                    } else {
-                        Slog.w(TAG, "User rejected full backup/restore operation");
-                        // indicate completion without having actually transferred any data
-                        signalAdbBackupRestoreCompletion(params);
-                    }
-                } else {
-                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
-    /** User-configurable enabling/disabling of backups. */
-    public void setBackupEnabled(boolean enable) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "setBackupEnabled");
-
-        Slog.i(TAG, "Backup enabled => " + enable);
-
-        long oldId = Binder.clearCallingIdentity();
-        try {
-            boolean wasEnabled = mEnabled;
-            synchronized (this) {
-                writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
-                mEnabled = enable;
-            }
-
-            synchronized (mQueueLock) {
-                if (enable && !wasEnabled && mProvisioned) {
-                    // if we've just been enabled, start scheduling backup passes
-                    KeyValueBackupJob.schedule(mContext, mConstants);
-                    scheduleNextFullBackupJob(0);
-                } else if (!enable) {
-                    // No longer enabled, so stop running backups
-                    if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
-
-                    KeyValueBackupJob.cancel(mContext);
-
-                    // This also constitutes an opt-out, so we wipe any data for
-                    // this device from the backend.  We start that process with
-                    // an alarm in order to guarantee wakelock states.
-                    if (wasEnabled && mProvisioned) {
-                        // NOTE: we currently flush every registered transport, not just
-                        // the currently-active one.
-                        List<String> transportNames = new ArrayList<>();
-                        List<String> transportDirNames = new ArrayList<>();
-                        mTransportManager.forEachRegisteredTransport(
-                                name -> {
-                                    final String dirName;
-                                    try {
-                                        dirName =
-                                                mTransportManager
-                                                        .getTransportDirName(name);
-                                    } catch (TransportNotRegisteredException e) {
-                                        // Should never happen
-                                        Slog.e(TAG, "Unexpected unregistered transport", e);
-                                        return;
-                                    }
-                                    transportNames.add(name);
-                                    transportDirNames.add(dirName);
-                                });
-
-                        // build the set of transports for which we are posting an init
-                        for (int i = 0; i < transportNames.size(); i++) {
-                            recordInitPending(
-                                    true,
-                                    transportNames.get(i),
-                                    transportDirNames.get(i));
-                        }
-                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
-                                mRunInitIntent);
-                    }
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
-    /** Enable/disable automatic restore of app data at install time. */
-    public void setAutoRestore(boolean doAutoRestore) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "setAutoRestore");
-
-        Slog.i(TAG, "Auto restore => " + doAutoRestore);
-
-        final long oldId = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                Settings.Secure.putInt(mContext.getContentResolver(),
-                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
-                mAutoRestore = doAutoRestore;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
-    /** Mark the backup service as having been provisioned. */
-    public void setBackupProvisioned(boolean available) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "setBackupProvisioned");
-        /*
-         * This is now a no-op; provisioning is simply the device's own setup state.
-         */
-    }
-
-    /** Report whether the backup mechanism is currently enabled. */
-    public boolean isBackupEnabled() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "isBackupEnabled");
-        return mEnabled;    // no need to synchronize just to read it
-    }
-
-    /** Report the name of the currently active transport. */
+    /** Return the name of the currently active transport. */
     public String getCurrentTransport() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "getCurrentTransport");
-        String currentTransport = mTransportManager.getCurrentTransportName();
-        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
-        return currentTransport;
+        return mUserBackupManagerService.getCurrentTransport();
     }
 
     /**
      * Returns the {@link ComponentName} of the host service of the selected transport or {@code
      * null} if no transport selected or if the transport selected is not registered.
      */
-    @Nullable
     public ComponentName getCurrentTransportComponent() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "getCurrentTransportComponent");
-        long oldId = Binder.clearCallingIdentity();
-        try {
-            return mTransportManager.getCurrentTransportComponent();
-        } catch (TransportNotRegisteredException e) {
-            return null;
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
+        return mUserBackupManagerService.getCurrentTransportComponent();
     }
 
     /** Report all known, available backup transports by name. */
     public String[] listAllTransports() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "listAllTransports");
-
-        return mTransportManager.getRegisteredTransportNames();
+        return mUserBackupManagerService.listAllTransports();
     }
 
-    /** Report all known, available backup transports by component. */
+    /** Report all known, available backup transports by {@link ComponentName}. */
     public ComponentName[] listAllTransportComponents() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "listAllTransportComponents");
-        return mTransportManager.getRegisteredTransportComponents();
+        return mUserBackupManagerService.listAllTransportComponents();
     }
 
     /** Report all system whitelisted transports. */
     public String[] getTransportWhitelist() {
-        // No permission check, intentionally.
-        Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
-        String[] whitelistedTransports = new String[whitelistedComponents.size()];
-        int i = 0;
-        for (ComponentName component : whitelistedComponents) {
-            whitelistedTransports[i] = component.flattenToShortString();
-            i++;
-        }
-        return whitelistedTransports;
+        return mUserBackupManagerService.getTransportWhitelist();
     }
 
     /**
@@ -2916,18 +282,17 @@
      * @param transportComponent The identity of the transport being described.
      * @param name A {@link String} with the new name for the transport. This is NOT for
      *     identification. MUST NOT be {@code null}.
-     * @param configurationIntent An {@link Intent} that can be passed to
-     *     {@link Context#startActivity} in order to launch the transport's configuration UI. It may
-     *     be {@code null} if the transport does not offer any user-facing configuration UI.
+     * @param configurationIntent An {@link Intent} that can be passed to {@link
+     *     Context#startActivity} in order to launch the transport's configuration UI. It may be
+     *     {@code null} if the transport does not offer any user-facing configuration UI.
      * @param currentDestinationString A {@link String} describing the destination to which the
      *     transport is currently sending data. MUST NOT be {@code null}.
-     * @param dataManagementIntent An {@link Intent} that can be passed to
-     *     {@link Context#startActivity} in order to launch the transport's data-management UI. It
-     *     may be {@code null} if the transport does not offer any user-facing data
-     *     management UI.
+     * @param dataManagementIntent An {@link Intent} that can be passed to {@link
+     *     Context#startActivity} in order to launch the transport's data-management UI. It may be
+     *     {@code null} if the transport does not offer any user-facing data management UI.
      * @param dataManagementLabel A {@link String} to be used as the label for the transport's data
-     *     management affordance. This MUST be {@code null} when dataManagementIntent is
-     *     {@code null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
+     *     management affordance. This MUST be {@code null} when dataManagementIntent is {@code
+     *     null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
      * @throws SecurityException If the UID of the calling process differs from the package UID of
      *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
      */
@@ -2937,9 +302,8 @@
             @Nullable Intent configurationIntent,
             String currentDestinationString,
             @Nullable Intent dataManagementIntent,
-            @Nullable String dataManagementLabel) {
-        updateTransportAttributes(
-                Binder.getCallingUid(),
+            String dataManagementLabel) {
+        mUserBackupManagerService.updateTransportAttributes(
                 transportComponent,
                 name,
                 configurationIntent,
@@ -2948,73 +312,16 @@
                 dataManagementLabel);
     }
 
-    @VisibleForTesting
-    void updateTransportAttributes(
-            int callingUid,
-            ComponentName transportComponent,
-            String name,
-            @Nullable Intent configurationIntent,
-            String currentDestinationString,
-            @Nullable Intent dataManagementIntent,
-            @Nullable String dataManagementLabel) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "updateTransportAttributes");
-
-        Preconditions.checkNotNull(transportComponent, "transportComponent can't be null");
-        Preconditions.checkNotNull(name, "name can't be null");
-        Preconditions.checkNotNull(
-                currentDestinationString, "currentDestinationString can't be null");
-        Preconditions.checkArgument(
-                (dataManagementIntent == null) == (dataManagementLabel == null),
-                "dataManagementLabel should be null iff dataManagementIntent is null");
-
-        try {
-            int transportUid =
-                    mContext.getPackageManager()
-                            .getPackageUid(transportComponent.getPackageName(), 0);
-            if (callingUid != transportUid) {
-                throw new SecurityException("Only the transport can change its description");
-            }
-        } catch (NameNotFoundException e) {
-            throw new SecurityException("Transport package not found", e);
-        }
-
-        final long oldId = Binder.clearCallingIdentity();
-        try {
-            mTransportManager.updateTransportAttributes(
-                    transportComponent,
-                    name,
-                    configurationIntent,
-                    currentDestinationString,
-                    dataManagementIntent,
-                    dataManagementLabel);
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
     /**
-     * Selects transport {@code transportName} and returns previously selected transport.
+     * Selects transport {@code transportName} and returns the previously selected transport.
      *
      * @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
-     * ISelectBackupTransportCallback)} instead.
+     *     ISelectBackupTransportCallback)} instead.
      */
     @Deprecated
     @Nullable
     public String selectBackupTransport(String transportName) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "selectBackupTransport");
-
-        final long oldId = Binder.clearCallingIdentity();
-        try {
-            String previousTransportName = mTransportManager.selectTransport(transportName);
-            updateStateForTransport(transportName);
-            Slog.v(TAG, "selectBackupTransport(transport = " + transportName
-                    + "): previous transport = " + previousTransportName);
-            return previousTransportName;
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
+        return mUserBackupManagerService.selectBackupTransport(transportName);
     }
 
     /**
@@ -3023,70 +330,7 @@
      */
     public void selectBackupTransportAsync(
             ComponentName transportComponent, ISelectBackupTransportCallback listener) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
-
-        final long oldId = Binder.clearCallingIdentity();
-        try {
-            String transportString = transportComponent.flattenToShortString();
-            Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")");
-            mBackupHandler.post(
-                    () -> {
-                        String transportName = null;
-                        int result =
-                                mTransportManager.registerAndSelectTransport(transportComponent);
-                        if (result == BackupManager.SUCCESS) {
-                            try {
-                                transportName =
-                                        mTransportManager.getTransportName(transportComponent);
-                                updateStateForTransport(transportName);
-                            } catch (TransportNotRegisteredException e) {
-                                Slog.e(TAG, "Transport got unregistered");
-                                result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
-                            }
-                        }
-
-                        try {
-                            if (transportName != null) {
-                                listener.onSuccess(transportName);
-                            } else {
-                                listener.onFailure(result);
-                            }
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
-                        }
-                    });
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
-    private void updateStateForTransport(String newTransportName) {
-        // Publish the name change
-        Settings.Secure.putString(mContext.getContentResolver(),
-                Settings.Secure.BACKUP_TRANSPORT, newTransportName);
-
-        // And update our current-dataset bookkeeping
-        String callerLogString = "BMS.updateStateForTransport()";
-        TransportClient transportClient =
-                mTransportManager.getTransportClient(newTransportName, callerLogString);
-        if (transportClient != null) {
-            try {
-                IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
-                mCurrentToken = transport.getCurrentRestoreSet();
-            } catch (Exception e) {
-                // Oops.  We can't know the current dataset token, so reset and figure it out
-                // when we do the next k/v backup operation on this transport.
-                mCurrentToken = 0;
-                Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
-            }
-            mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
-        } else {
-            Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
-            // The named transport isn't registered, so we can't know what its current dataset token
-            // is. Reset as above.
-            mCurrentToken = 0;
-        }
+        mUserBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
     }
 
     /**
@@ -3095,18 +339,7 @@
      * returns {@code null}.
      */
     public Intent getConfigurationIntent(String transportName) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "getConfigurationIntent");
-        try {
-            Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
-            if (MORE_DEBUG) {
-                Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
-            }
-            return intent;
-        } catch (TransportNotRegisteredException e) {
-            Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
-            return null;
-        }
+        return mUserBackupManagerService.getConfigurationIntent(transportName);
     }
 
     /**
@@ -3119,36 +352,12 @@
      * @return The current destination string or null if the transport is not registered.
      */
     public String getDestinationString(String transportName) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "getDestinationString");
-
-        try {
-            String string = mTransportManager.getTransportCurrentDestinationString(transportName);
-            if (MORE_DEBUG) {
-                Slog.d(TAG, "getDestinationString() returning " + string);
-            }
-            return string;
-        } catch (TransportNotRegisteredException e) {
-            Slog.e(TAG, "Unable to get destination string from transport: " + e.getMessage());
-            return null;
-        }
+        return mUserBackupManagerService.getDestinationString(transportName);
     }
 
     /** Supply the manage-data intent for the given transport. */
     public Intent getDataManagementIntent(String transportName) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "getDataManagementIntent");
-
-        try {
-            Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
-            if (MORE_DEBUG) {
-                Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
-            }
-            return intent;
-        } catch (TransportNotRegisteredException e) {
-            Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
-            return null;
-        }
+        return mUserBackupManagerService.getDataManagementIntent(transportName);
     }
 
     /**
@@ -3156,443 +365,202 @@
      * transport.
      */
     public String getDataManagementLabel(String transportName) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "getDataManagementLabel");
+        return mUserBackupManagerService.getDataManagementLabel(transportName);
+    }
 
-        try {
-            String label = mTransportManager.getTransportDataManagementLabel(transportName);
-            if (MORE_DEBUG) {
-                Slog.d(TAG, "getDataManagementLabel() returning " + label);
-            }
-            return label;
-        } catch (TransportNotRegisteredException e) {
-            Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
-            return null;
-        }
+    // ---------------------------------------------
+    // SETTINGS OPERATIONS
+    // ---------------------------------------------
+
+    /** Enable/disable the backup service. This is user-configurable via backup settings. */
+    public void setBackupEnabled(boolean enable) {
+        mUserBackupManagerService.setBackupEnabled(enable);
+    }
+
+    /** Enable/disable automatic restore of app data at install time. */
+    public void setAutoRestore(boolean autoRestore) {
+        mUserBackupManagerService.setAutoRestore(autoRestore);
+    }
+
+    /** Mark the backup service as having been provisioned (device has gone through SUW). */
+    public void setBackupProvisioned(boolean provisioned) {
+        mUserBackupManagerService.setBackupProvisioned(provisioned);
     }
 
     /**
-     * Callback: a requested backup agent has been instantiated. This should only be called from the
-     * {@link ActivityManager}.
+     * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
      */
-    public void agentConnected(String packageName, IBinder agentBinder) {
-        synchronized (mAgentConnectLock) {
-            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
-                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
-                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
-                mConnectedAgent = agent;
-                mConnecting = false;
-            } else {
-                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
-                        + " claiming agent connected");
-            }
-            mAgentConnectLock.notifyAll();
-        }
+    public boolean isBackupEnabled() {
+        return mUserBackupManagerService.isBackupEnabled();
+    }
+
+    // ---------------------------------------------
+    // BACKUP OPERATIONS
+    // ---------------------------------------------
+
+    /** Checks if the given package {@code packageName} is eligible for backup. */
+    public boolean isAppEligibleForBackup(String packageName) {
+        return mUserBackupManagerService.isAppEligibleForBackup(packageName);
     }
 
     /**
-     * Callback: a backup agent has failed to come up, or has unexpectedly quit. If the agent failed
-     * to come up in the first place, the agentBinder argument will be {@code null}. This should
-     * only be called from the {@link ActivityManager}.
+     * Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
      */
-    public void agentDisconnected(String packageName) {
-        // TODO: handle backup being interrupted
-        synchronized (mAgentConnectLock) {
-            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
-                mConnectedAgent = null;
-                mConnecting = false;
-            } else {
-                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
-                        + " claiming agent disconnected");
-            }
-            mAgentConnectLock.notifyAll();
-        }
+    public String[] filterAppsEligibleForBackup(String[] packages) {
+        return mUserBackupManagerService.filterAppsEligibleForBackup(packages);
     }
 
     /**
-     * An application being installed will need a restore pass, then the {@link PackageManager} will
-     * need to be told when the restore is finished.
+     * Run a backup pass immediately for any key-value backup applications that have declared that
+     * they have pending updates.
+     */
+    public void backupNow() {
+        mUserBackupManagerService.backupNow();
+    }
+
+    /**
+     * Requests a backup for the inputted {@code packages} with a specified callback {@link
+     * IBackupManagerMonitor} for receiving events during the operation.
+     */
+    public int requestBackup(
+            String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) {
+        return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags);
+    }
+
+    /** Cancel all running backup operations. */
+    public void cancelBackups() {
+        mUserBackupManagerService.cancelBackups();
+    }
+
+    /**
+     * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we
+     * use is to perform one app backup per scheduled job execution, and to reschedule the job with
+     * zero latency as long as conditions remain right and we still have work to do.
+     *
+     * @return Whether ongoing work will continue. The return value here will be passed along as the
+     *     return value to the callback {@link JobService#onStartJob(JobParameters)}.
+     */
+    public boolean beginFullBackup(FullBackupJob scheduledJob) {
+        return mUserBackupManagerService.beginFullBackup(scheduledJob);
+    }
+
+    /**
+     * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
+     * longer met for running the full backup job.
+     */
+    public void endFullBackup() {
+        mUserBackupManagerService.endFullBackup();
+    }
+
+    /**
+     * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
+     */
+    public void fullTransportBackup(String[] packageNames) {
+        mUserBackupManagerService.fullTransportBackup(packageNames);
+    }
+
+    // ---------------------------------------------
+    // RESTORE OPERATIONS
+    // ---------------------------------------------
+
+    /**
+     * Used to run a restore pass for an application that is being installed. This should only be
+     * called from the {@link PackageManager}.
      */
     public void restoreAtInstall(String packageName, int token) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
-                    + " attemping install-time restore");
-            return;
-        }
-
-        boolean skip = false;
-
-        long restoreSet = getAvailableRestoreToken(packageName);
-        if (DEBUG) {
-            Slog.v(TAG, "restoreAtInstall pkg=" + packageName
-                    + " token=" + Integer.toHexString(token)
-                    + " restoreSet=" + Long.toHexString(restoreSet));
-        }
-        if (restoreSet == 0) {
-            if (MORE_DEBUG) Slog.i(TAG, "No restore set");
-            skip = true;
-        }
-
-        TransportClient transportClient =
-                mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
-        if (transportClient == null) {
-            if (DEBUG) Slog.w(TAG, "No transport client");
-            skip = true;
-        }
-
-        if (!mAutoRestore) {
-            if (DEBUG) {
-                Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
-            }
-            skip = true;
-        }
-
-        if (!skip) {
-            try {
-                // okay, we're going to attempt a restore of this package from this restore set.
-                // The eventual message back into the Package Manager to run the post-install
-                // steps for 'token' will be issued from the restore handling code.
-
-                mWakelock.acquire();
-
-                OnTaskFinishedListener listener = caller -> {
-                    mTransportManager.disposeOfTransportClient(transportClient, caller);
-                    mWakelock.release();
-                };
-
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "Restore at install of " + packageName);
-                }
-                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
-                msg.obj =
-                        RestoreParams.createForRestoreAtInstall(
-                                transportClient,
-                                /* observer */ null,
-                                /* monitor */ null,
-                                restoreSet,
-                                packageName,
-                                token,
-                                listener);
-                mBackupHandler.sendMessage(msg);
-            } catch (Exception e) {
-                // Calling into the transport broke; back off and proceed with the installation.
-                Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
-                skip = true;
-            }
-        }
-
-        if (skip) {
-            // Auto-restore disabled or no way to attempt a restore
-
-            if (transportClient != null) {
-                mTransportManager.disposeOfTransportClient(
-                        transportClient, "BMS.restoreAtInstall()");
-            }
-
-            // Tell the PackageManager to proceed with the post-install handling for this package.
-            if (DEBUG) Slog.v(TAG, "Finishing install immediately");
-            try {
-                mPackageManagerBinder.finishPackageInstall(token, false);
-            } catch (RemoteException e) { /* can't happen */ }
-        }
-    }
-
-    /** Hand off a restore session. */
-    public IRestoreSession beginRestoreSession(String packageName, String transport) {
-        if (DEBUG) {
-            Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
-                    + " transport=" + transport);
-        }
-
-        boolean needPermission = true;
-        if (transport == null) {
-            transport = mTransportManager.getCurrentTransportName();
-
-            if (packageName != null) {
-                PackageInfo app = null;
-                try {
-                    app = mPackageManager.getPackageInfo(packageName, 0);
-                } catch (NameNotFoundException nnf) {
-                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
-                    throw new IllegalArgumentException("Package " + packageName + " not found");
-                }
-
-                if (app.applicationInfo.uid == Binder.getCallingUid()) {
-                    // So: using the current active transport, and the caller has asked
-                    // that its own package will be restored.  In this narrow use case
-                    // we do not require the caller to hold the permission.
-                    needPermission = false;
-                }
-            }
-        }
-
-        if (needPermission) {
-            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                    "beginRestoreSession");
-        } else {
-            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
-        }
-
-        synchronized (this) {
-            if (mActiveRestoreSession != null) {
-                Slog.i(TAG, "Restore session requested but one already active");
-                return null;
-            }
-            if (mBackupRunning) {
-                Slog.i(TAG, "Restore session requested but currently running backups");
-                return null;
-            }
-            mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
-            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
-                    mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
-        }
-        return mActiveRestoreSession;
-    }
-
-    /** Clear the specified restore session. */
-    public void clearRestoreSession(ActiveRestoreSession currentSession) {
-        synchronized (this) {
-            if (currentSession != mActiveRestoreSession) {
-                Slog.e(TAG, "ending non-current restore session");
-            } else {
-                if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
-                mActiveRestoreSession = null;
-                mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
-            }
-        }
+        mUserBackupManagerService.restoreAtInstall(packageName, token);
     }
 
     /**
-     * Note that a currently-active backup agent has notified us that it has completed the given
-     * outstanding asynchronous backup/restore operation.
+     * Begin a restore for the specified package {@code packageName} using the specified transport
+     * {@code transportName}.
      */
-    public void opComplete(int token, long result) {
-        if (MORE_DEBUG) {
-            Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
-        }
-        Operation op = null;
-        synchronized (mCurrentOpLock) {
-            op = mCurrentOperations.get(token);
-            if (op != null) {
-                if (op.state == OP_TIMEOUT) {
-                    // The operation already timed out, and this is a late response.  Tidy up
-                    // and ignore it; we've already dealt with the timeout.
-                    op = null;
-                    mCurrentOperations.delete(token);
-                } else if (op.state == OP_ACKNOWLEDGED) {
-                    if (DEBUG) {
-                        Slog.w(TAG, "Received duplicate ack for token="
-                                + Integer.toHexString(token));
-                    }
-                    op = null;
-                    mCurrentOperations.remove(token);
-                } else if (op.state == OP_PENDING) {
-                    // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
-                    // called after we we receive this call.
-                    op.state = OP_ACKNOWLEDGED;
-                }
-            }
-            mCurrentOpLock.notifyAll();
-        }
-
-        // The completion callback, if any, is invoked on the handler
-        if (op != null && op.callback != null) {
-            Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
-            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
-            mBackupHandler.sendMessage(msg);
-        }
+    public IRestoreSession beginRestoreSession(String packageName, String transportName) {
+        return mUserBackupManagerService.beginRestoreSession(packageName, transportName);
     }
 
-    /** Checks if the package is eligible for backup. */
-    public boolean isAppEligibleForBackup(String packageName) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
-
-        long oldToken = Binder.clearCallingIdentity();
-        try {
-            String callerLogString = "BMS.isAppEligibleForBackup";
-            TransportClient transportClient =
-                    mTransportManager.getCurrentTransportClient(callerLogString);
-            boolean eligible =
-                    AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
-                            transportClient, packageName, mPackageManager);
-            if (transportClient != null) {
-                mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
-            }
-            return eligible;
-        } finally {
-            Binder.restoreCallingIdentity(oldToken);
-        }
+    /**
+     * Get the restore-set token for the best-available restore set for this {@code packageName}:
+     * the active set if possible, else the ancestral one. Returns zero if none available.
+     */
+    public long getAvailableRestoreToken(String packageName) {
+        return mUserBackupManagerService.getAvailableRestoreToken(packageName);
     }
 
-    /** Returns the inputted packages that are eligible for backup. */
-    public String[] filterAppsEligibleForBackup(String[] packages) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
+    // ---------------------------------------------
+    // ADB BACKUP/RESTORE OPERATIONS
+    // ---------------------------------------------
 
-        long oldToken = Binder.clearCallingIdentity();
-        try {
-            String callerLogString = "BMS.filterAppsEligibleForBackup";
-            TransportClient transportClient =
-                    mTransportManager.getCurrentTransportClient(callerLogString);
-            List<String> eligibleApps = new LinkedList<>();
-            for (String packageName : packages) {
-                if (AppBackupUtils
-                        .appIsRunningAndEligibleForBackupWithTransport(
-                                transportClient, packageName, mPackageManager)) {
-                    eligibleApps.add(packageName);
-                }
-            }
-            if (transportClient != null) {
-                mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
-            }
-            return eligibleApps.toArray(new String[eligibleApps.size()]);
-        } finally {
-            Binder.restoreCallingIdentity(oldToken);
-        }
+    /** Sets the backup password used when running adb backup. */
+    public boolean setBackupPassword(String currentPassword, String newPassword) {
+        return mUserBackupManagerService.setBackupPassword(currentPassword, newPassword);
     }
 
+    /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
+    public boolean hasBackupPassword() {
+        return mUserBackupManagerService.hasBackupPassword();
+    }
+
+    /**
+     * Used by 'adb backup' to run a backup pass for packages {@code packageNames} supplied via the
+     * command line, writing the resulting data stream to the supplied {@code fd}. This method is
+     * synchronous and does not return to the caller until the backup has been completed. It
+     * requires on-screen confirmation by the user.
+     */
+    public void adbBackup(
+            ParcelFileDescriptor fd,
+            boolean includeApks,
+            boolean includeObbs,
+            boolean includeShared,
+            boolean doWidgets,
+            boolean doAllApps,
+            boolean includeSystem,
+            boolean doCompress,
+            boolean doKeyValue,
+            String[] packageNames) {
+        mUserBackupManagerService.adbBackup(
+                fd,
+                includeApks,
+                includeObbs,
+                includeShared,
+                doWidgets,
+                doAllApps,
+                includeSystem,
+                doCompress,
+                doKeyValue,
+                packageNames);
+    }
+
+    /**
+     * Used by 'adb restore' to run a restore pass reading from the supplied {@code fd}. This method
+     * is synchronous and does not return to the caller until the restore has been completed. It
+     * requires on-screen confirmation by the user.
+     */
+    public void adbRestore(ParcelFileDescriptor fd) {
+        mUserBackupManagerService.adbRestore(fd);
+    }
+
+    /**
+     * Confirm that the previously requested adb backup/restore operation can proceed. This is used
+     * to require a user-facing disclosure about the operation.
+     */
+    public void acknowledgeAdbBackupOrRestore(
+            int token,
+            boolean allow,
+            String currentPassword,
+            String encryptionPassword,
+            IFullBackupRestoreObserver observer) {
+        mUserBackupManagerService.acknowledgeAdbBackupOrRestore(
+                token, allow, currentPassword, encryptionPassword, observer);
+    }
+
+    // ---------------------------------------------
+    //  SERVICE OPERATIONS
+    // ---------------------------------------------
+
     /** Prints service state for 'dumpsys backup'. */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
-
-        long identityToken = Binder.clearCallingIdentity();
-        try {
-            if (args != null) {
-                for (String arg : args) {
-                    if ("-h".equals(arg)) {
-                        pw.println("'dumpsys backup' optional arguments:");
-                        pw.println("  -h       : this help text");
-                        pw.println("  a[gents] : dump information about defined backup agents");
-                        return;
-                    } else if ("agents".startsWith(arg)) {
-                        dumpAgents(pw);
-                        return;
-                    } else if ("transportclients".equals(arg.toLowerCase())) {
-                        mTransportManager.dumpTransportClients(pw);
-                        return;
-                    } else if ("transportstats".equals(arg.toLowerCase())) {
-                        mTransportManager.dumpTransportStats(pw);
-                        return;
-                    }
-                }
-            }
-            dumpInternal(pw);
-        } finally {
-            Binder.restoreCallingIdentity(identityToken);
-        }
-    }
-
-    private void dumpAgents(PrintWriter pw) {
-        List<PackageInfo> agentPackages = allAgentPackages();
-        pw.println("Defined backup agents:");
-        for (PackageInfo pkg : agentPackages) {
-            pw.print("  ");
-            pw.print(pkg.packageName);
-            pw.println(':');
-            pw.print("      ");
-            pw.println(pkg.applicationInfo.backupAgentName);
-        }
-    }
-
-    private void dumpInternal(PrintWriter pw) {
-        synchronized (mQueueLock) {
-            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
-                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
-                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
-            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
-            if (mBackupRunning) pw.println("Backup currently running");
-            pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
-            pw.println("Last backup pass started: " + mLastBackupPass
-                    + " (now = " + System.currentTimeMillis() + ')');
-            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
-
-            pw.println("Transport whitelist:");
-            for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
-                pw.print("    ");
-                pw.println(transport.flattenToShortString());
-            }
-
-            pw.println("Available transports:");
-            final String[] transports = listAllTransports();
-            if (transports != null) {
-                for (String t : transports) {
-                    pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? "  * "
-                            : "    ") + t);
-                    try {
-                        File dir = new File(mBaseStateDir,
-                                mTransportManager.getTransportDirName(t));
-                        pw.println("       destination: "
-                                + mTransportManager.getTransportCurrentDestinationString(t));
-                        pw.println("       intent: "
-                                + mTransportManager.getTransportConfigurationIntent(t));
-                        for (File f : dir.listFiles()) {
-                            pw.println(
-                                    "       " + f.getName() + " - " + f.length() + " state bytes");
-                        }
-                    } catch (Exception e) {
-                        Slog.e(TAG, "Error in transport", e);
-                        pw.println("        Error: " + e);
-                    }
-                }
-            }
-
-            mTransportManager.dumpTransportClients(pw);
-
-            pw.println("Pending init: " + mPendingInits.size());
-            for (String s : mPendingInits) {
-                pw.println("    " + s);
-            }
-
-            pw.print("Ancestral: ");
-            pw.println(Long.toHexString(mAncestralToken));
-            pw.print("Current:   ");
-            pw.println(Long.toHexString(mCurrentToken));
-
-            int numPackages = mBackupParticipants.size();
-            pw.println("Participants:");
-            for (int i = 0; i < numPackages; i++) {
-                int uid = mBackupParticipants.keyAt(i);
-                pw.print("  uid: ");
-                pw.println(uid);
-                HashSet<String> participants = mBackupParticipants.valueAt(i);
-                for (String app : participants) {
-                    pw.println("    " + app);
-                }
-            }
-
-            pw.println("Ancestral packages: "
-                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
-            if (mAncestralPackages != null) {
-                for (String pkg : mAncestralPackages) {
-                    pw.println("    " + pkg);
-                }
-            }
-
-            Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy();
-            pw.println("Ever backed up: " + processedPackages.size());
-            for (String pkg : processedPackages) {
-                pw.println("    " + pkg);
-            }
-
-            pw.println("Pending key/value backup: " + mPendingBackups.size());
-            for (BackupRequest req : mPendingBackups.values()) {
-                pw.println("    " + req);
-            }
-
-            pw.println("Full backup queue:" + mFullBackupQueue.size());
-            for (FullBackupEntry entry : mFullBackupQueue) {
-                pw.print("    ");
-                pw.print(entry.lastBackup);
-                pw.print(" : ");
-                pw.println(entry.packageName);
-            }
-        }
-    }
-
-
-    public IBackupManager getBackupManagerBinder() {
-        return mBackupManagerBinder;
+        mUserBackupManagerService.dump(fd, pw, args);
     }
 
     private static boolean backupSettingMigrated(int userId) {
@@ -3620,7 +588,7 @@
         return false;
     }
 
-    private static void writeBackupEnableState(boolean enable, int userId) {
+    static void writeBackupEnableState(boolean enable, int userId) {
         File base = new File(Environment.getDataDirectory(), "backup");
         File enableFile = new File(base, BACKUP_ENABLE_FILE);
         File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 125c225..92c2ee4 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -5,7 +5,8 @@
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
 
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
@@ -50,7 +51,7 @@
     private static final String BACKUP_KEY_VALUE_BACKUP_DATA_FILENAME_SUFFIX = ".data";
     private static final String BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX = ".new";
 
-    private BackupManagerService mBackupManagerService;
+    private UserBackupManagerService mBackupManagerService;
     private final PackageManager mPackageManager;
     private final OutputStream mOutput;
     private final PackageInfo mCurrentPackage;
@@ -66,7 +67,7 @@
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
-            BackupManagerService backupManagerService, PackageManager packageManager,
+            UserBackupManagerService backupManagerService, PackageManager packageManager,
             File baseStateDir, File dataDir) {
         mOutput = output;
         mCurrentPackage = packageInfo;
@@ -85,7 +86,7 @@
         mNewStateName = new File(mStateDir,
                 pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
 
-        mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
+        mManifestFile = new File(mDataDir, BACKUP_MANIFEST_FILENAME);
         mAgentTimeoutParameters = Preconditions.checkNotNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index bb14576..bed520e 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -39,7 +39,7 @@
     private static final String TAG = "KeyValueAdbRestoreEngine";
     private static final boolean DEBUG = false;
 
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final File mDataDir;
 
     FileMetadata mInfo;
@@ -48,7 +48,7 @@
     IBackupAgent mAgent;
     int mToken;
 
-    public KeyValueAdbRestoreEngine(BackupManagerService backupManagerService,
+    public KeyValueAdbRestoreEngine(UserBackupManagerService backupManagerService,
             File dataDir, FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent,
             int token) {
         mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index c805783..f2e7435 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -16,6 +16,8 @@
 
 package com.android.server.backup;
 
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
 import android.app.AlarmManager;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
@@ -71,7 +73,7 @@
             if (delay <= 0) {
                 delay = interval + new Random().nextInt((int) fuzz);
             }
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
             }
             JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
diff --git a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
index b5db5e2..edc2379 100644
--- a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
+++ b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
@@ -23,8 +23,8 @@
 import java.io.BufferedInputStream;
 import java.io.DataInputStream;
 import java.io.EOFException;
-import java.io.FileInputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.util.HashSet;
@@ -46,7 +46,7 @@
 final class ProcessedPackagesJournal {
     private static final String TAG = "ProcessedPackagesJournal";
     private static final String JOURNAL_FILE_NAME = "processed";
-    private static final boolean DEBUG = BackupManagerService.DEBUG || false;
+    private static final boolean DEBUG = BackupManagerService.DEBUG;
 
     // using HashSet instead of ArraySet since we expect 100-500 elements range
     @GuardedBy("mProcessedPackages")
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 32fd7e0..59629aa 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -42,18 +42,20 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.Slog;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
 
 /**
- * A proxy to BackupManagerService implementation.
+ * A proxy to the {@link BackupManagerService} implementation.
  *
- * <p>This is an external interface to the BackupManagerService which is being accessed via
- * published binder (see BackupManagerService$Lifecycle). This lets us turn down the heavy
+ * <p>This is an external interface to the {@link BackupManagerService} which is being accessed via
+ * published binder {@link BackupManagerService.Lifecycle}. This lets us turn down the heavy
  * implementation object on the fly without disturbing binders that have been cached somewhere in
  * the system.
  *
@@ -143,7 +145,7 @@
     }
 
     /**
-     * Called from {@link BackupManagerService$Lifecycle} when the system user is unlocked. Attempts
+     * 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()
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
new file mode 100644
index 0000000..fe16afe
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -0,0 +1,3501 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
+
+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;
+import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_FULL_CONFIRMATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_OP_COMPLETE;
+import static com.android.server.backup.internal.BackupHandler.MSG_REQUEST_BACKUP;
+import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
+import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import android.app.IBackupAgent;
+import android.app.PendingIntent;
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManagerMonitor;
+import android.app.backup.FullBackup;
+import android.app.backup.IBackupManager;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.IRestoreSession;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.PowerManager.ServiceType;
+import android.os.PowerSaveState;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.WorkSource;
+import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.EventLog;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.AppWidgetBackupBridge;
+import com.android.server.EventLogTags;
+import com.android.server.backup.fullbackup.FullBackupEntry;
+import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
+import com.android.server.backup.internal.BackupHandler;
+import com.android.server.backup.internal.ClearDataObserver;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.internal.Operation;
+import com.android.server.backup.internal.PerformInitializeTask;
+import com.android.server.backup.internal.ProvisionedObserver;
+import com.android.server.backup.internal.RunBackupReceiver;
+import com.android.server.backup.internal.RunInitializeReceiver;
+import com.android.server.backup.keyvalue.BackupRequest;
+import com.android.server.backup.params.AdbBackupParams;
+import com.android.server.backup.params.AdbParams;
+import com.android.server.backup.params.AdbRestoreParams;
+import com.android.server.backup.params.BackupParams;
+import com.android.server.backup.params.ClearParams;
+import com.android.server.backup.params.ClearRetryParams;
+import com.android.server.backup.params.RestoreParams;
+import com.android.server.backup.restore.ActiveRestoreSession;
+import com.android.server.backup.restore.PerformUnifiedRestoreTask;
+import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupObserverUtils;
+import com.android.server.backup.utils.SparseArrayUtils;
+
+import com.google.android.collect.Sets;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.RandomAccessFile;
+import java.security.SecureRandom;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** System service that performs backup/restore operations. */
+public class UserBackupManagerService {
+    // File containing backup-enabled state.  Contains a single byte;
+    // nonzero == enabled.  File missing or contains a zero byte == disabled.
+    private static final String BACKUP_ENABLE_FILE = "backup_enabled";
+
+    // Persistently track the need to do a full init.
+    private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
+
+    // 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).
+    public static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
+
+    // Name and current contents version of the full-backup manifest file
+    //
+    // Manifest version history:
+    //
+    // 1 : initial release
+    public static final String BACKUP_MANIFEST_FILENAME = "_manifest";
+    public static final int BACKUP_MANIFEST_VERSION = 1;
+
+    // External archive format version history:
+    //
+    // 1 : initial release
+    // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
+    // 3 : introduced "_meta" metadata file; no other format change per se
+    // 4 : added support for new device-encrypted storage locations
+    // 5 : added support for key-value packages
+    public static final int BACKUP_FILE_VERSION = 5;
+    public static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
+    public static final String BACKUP_METADATA_FILENAME = "_meta";
+    public static final int BACKUP_METADATA_VERSION = 1;
+    public static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
+
+    private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
+
+    // Round-robin queue for scheduling full backup passes.
+    private static final int SCHEDULE_FILE_VERSION = 1;
+
+    public static final String SETTINGS_PACKAGE = "com.android.providers.settings";
+    public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
+
+    // Pseudoname that we use for the Package Manager metadata "package".
+    public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
+
+    // Retry interval for clear/init when the transport is unavailable
+    private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
+
+    public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
+    public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
+    public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
+    public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+
+    // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
+    // pending operations list.
+    public static final int OP_PENDING = 0;
+    private static final int OP_ACKNOWLEDGED = 1;
+    private static final int OP_TIMEOUT = -1;
+
+    // Waiting for backup agent to respond during backup operation.
+    public static final int OP_TYPE_BACKUP_WAIT = 0;
+
+    // Waiting for backup agent to respond during restore operation.
+    public static final int OP_TYPE_RESTORE_WAIT = 1;
+
+    // An entire backup operation spanning multiple packages.
+    public static final int OP_TYPE_BACKUP = 2;
+
+    // Time delay for initialization operations that can be delayed so as not to consume too much
+    // CPU on bring-up and increase time-to-UI.
+    private static final long INITIALIZATION_DELAY_MILLIS = 3000;
+
+    // Timeout interval for deciding that a bind or clear-data has taken too long
+    private static final long TIMEOUT_INTERVAL = 10 * 1000;
+
+    // User confirmation timeout for a full backup/restore operation.  It's this long in
+    // order to give them time to enter the backup password.
+    private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
+
+    // If an app is busy when we want to do a full-data backup, how long to defer the retry.
+    // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
+    private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
+    private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
+
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+    private final TransportManager mTransportManager;
+
+    private Context mContext;
+    private PackageManager mPackageManager;
+    private IPackageManager mPackageManagerBinder;
+    private IActivityManager mActivityManager;
+    private PowerManager mPowerManager;
+    private AlarmManager mAlarmManager;
+    private IStorageManager mStorageManager;
+    private BackupManagerConstants mConstants;
+    private PowerManager.WakeLock mWakelock;
+    private BackupHandler mBackupHandler;
+
+    private IBackupManager mBackupManagerBinder;
+
+    private boolean mEnabled;   // access to this is synchronized on 'this'
+    private boolean mProvisioned;
+    private boolean mAutoRestore;
+
+    private PendingIntent mRunBackupIntent;
+    private PendingIntent mRunInitIntent;
+
+    private final ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
+
+    // map UIDs to the set of participating packages under that UID
+    private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
+
+    // Backups that we haven't started yet.  Keys are package names.
+    private HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
+
+    // locking around the pending-backup management
+    private final Object mQueueLock = new Object();
+
+    // The thread performing the sequence of queued backups binds to each app's agent
+    // in succession.  Bind notifications are asynchronously delivered through the
+    // Activity Manager; use this lock object to signal when a requested binding has
+    // completed.
+    private final Object mAgentConnectLock = new Object();
+    private IBackupAgent mConnectedAgent;
+    private volatile boolean mConnecting;
+
+    private volatile boolean mBackupRunning;
+    private volatile long mLastBackupPass;
+
+    // A similar synchronization mechanism around clearing apps' data for restore
+    private final Object mClearDataLock = new Object();
+    private volatile boolean mClearingData;
+
+    // Used by ADB.
+    private final BackupPasswordManager mBackupPasswordManager;
+    private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
+    private final SecureRandom mRng = new SecureRandom();
+
+    // Time when we post the transport registration operation
+    private final long mRegisterTransportsRequestedTime;
+
+    @GuardedBy("mQueueLock")
+    private PerformFullTransportBackupTask mRunningFullBackupTask;
+
+    @GuardedBy("mQueueLock")
+    private ArrayList<FullBackupEntry> mFullBackupQueue;
+
+    @GuardedBy("mPendingRestores")
+    private boolean mIsRestoreInProgress;
+
+    @GuardedBy("mPendingRestores")
+    private final Queue<PerformUnifiedRestoreTask> mPendingRestores = new ArrayDeque<>();
+
+    private ActiveRestoreSession mActiveRestoreSession;
+
+    // Watch the device provisioning operation during setup
+    private ContentObserver mProvisionedObserver;
+
+    /**
+     * mCurrentOperations contains the list of currently active operations.
+     *
+     * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
+     * An operation wraps a BackupRestoreTask within it.
+     * It's the responsibility of this task to remove the operation from this array.
+     *
+     * A BackupRestore task gets notified of ack/timeout for the operation via
+     * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
+     * on the mCurrentOpLock.
+     * {@link UserBackupManagerService#waitUntilOperationComplete(int)} is
+     * used in various places to 'wait' for notifyAll and detect change of pending state of an
+     * operation. So typically, an operation will be removed from this array by:
+     * - BackupRestoreTask#handleCancel and
+     * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
+     * these places because waitUntilOperationComplete relies on the operation being present to
+     * determine its completion status.
+     *
+     * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
+     * cancel backup tasks.
+     */
+    @GuardedBy("mCurrentOpLock")
+    private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
+    private final Object mCurrentOpLock = new Object();
+    private final Random mTokenGenerator = new Random();
+    final AtomicInteger mNextToken = new AtomicInteger();
+
+    // Where we keep our journal files and other bookkeeping.
+    private File mBaseStateDir;
+    private File mDataDir;
+    private File mJournalDir;
+    @Nullable
+    private DataChangedJournal mJournal;
+    private File mFullBackupScheduleFile;
+
+    // Keep a log of all the apps we've ever backed up.
+    private ProcessedPackagesJournal mProcessedPackagesJournal;
+
+    private File mTokenFile;
+    private Set<String> mAncestralPackages = null;
+    private long mAncestralToken = 0;
+    private long mCurrentToken = 0;
+
+    @VisibleForTesting
+    public UserBackupManagerService(
+            Context context,
+            Trampoline parent,
+            HandlerThread backupThread,
+            File baseStateDir,
+            File dataDir,
+            TransportManager transportManager) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mPackageManagerBinder = AppGlobals.getPackageManager();
+        mActivityManager = ActivityManager.getService();
+
+        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
+
+        mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
+
+        mAgentTimeoutParameters = new
+                BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
+        mAgentTimeoutParameters.start();
+
+        // spin up the backup/restore handler thread
+        mBackupHandler = new BackupHandler(this, backupThread.getLooper());
+
+        // Set up our bookkeeping
+        final ContentResolver resolver = context.getContentResolver();
+        mProvisioned = Settings.Global.getInt(resolver,
+                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+        mAutoRestore = Settings.Secure.getInt(resolver,
+                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
+
+        mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
+        resolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+                false, mProvisionedObserver);
+
+        mBaseStateDir = baseStateDir;
+        mBaseStateDir.mkdirs();
+        if (!SELinux.restorecon(mBaseStateDir)) {
+            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+        }
+
+        mDataDir = dataDir;
+
+        mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
+
+        // Alarm receivers for scheduled backups & initialization operations
+        BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(RUN_BACKUP_ACTION);
+        context.registerReceiver(mRunBackupReceiver, filter,
+                android.Manifest.permission.BACKUP, null);
+
+        BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
+        filter = new IntentFilter();
+        filter.addAction(RUN_INITIALIZE_ACTION);
+        context.registerReceiver(mRunInitReceiver, filter,
+                android.Manifest.permission.BACKUP, null);
+
+        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
+        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
+
+        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
+        initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
+
+        // Set up the backup-request journaling
+        mJournalDir = new File(mBaseStateDir, "pending");
+        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
+        mJournal = null;        // will be created on first use
+
+        mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
+        // We are observing changes to the constants throughout the lifecycle of BMS. This is
+        // because we reference the constants in multiple areas of BMS, which otherwise would
+        // require frequent starting and stopping.
+        mConstants.start();
+
+        // Set up the various sorts of package tracking we do
+        mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
+        initPackageTracking();
+
+        // Build our mapping of uid to backup client services.  This implicitly
+        // schedules a backup pass on the Package Manager metadata the first
+        // time anything needs to be backed up.
+        synchronized (mBackupParticipants) {
+            addPackageParticipantsLocked(null);
+        }
+
+        mTransportManager = transportManager;
+        mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
+        mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
+        mBackupHandler.postDelayed(
+                mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
+
+        // Now that we know about valid backup participants, parse any leftover journal files into
+        // the pending backup set
+        mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
+
+        // Power management
+        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+    }
+
+
+    public BackupManagerConstants getConstants() {
+        return mConstants;
+    }
+
+    public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
+        return mAgentTimeoutParameters;
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    public void setContext(Context context) {
+        mContext = context;
+    }
+
+    public PackageManager getPackageManager() {
+        return mPackageManager;
+    }
+
+    public void setPackageManager(PackageManager packageManager) {
+        mPackageManager = packageManager;
+    }
+
+    public IPackageManager getPackageManagerBinder() {
+        return mPackageManagerBinder;
+    }
+
+    public void setPackageManagerBinder(IPackageManager packageManagerBinder) {
+        mPackageManagerBinder = packageManagerBinder;
+    }
+
+    public IActivityManager getActivityManager() {
+        return mActivityManager;
+    }
+
+    public void setActivityManager(IActivityManager activityManager) {
+        mActivityManager = activityManager;
+    }
+
+    public AlarmManager getAlarmManager() {
+        return mAlarmManager;
+    }
+
+    public void setAlarmManager(AlarmManager alarmManager) {
+        mAlarmManager = alarmManager;
+    }
+
+    @VisibleForTesting
+    void setPowerManager(PowerManager powerManager) {
+        mPowerManager = powerManager;
+    }
+
+    public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
+        mBackupManagerBinder = backupManagerBinder;
+    }
+
+    public TransportManager getTransportManager() {
+        return mTransportManager;
+    }
+
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        mEnabled = enabled;
+    }
+
+    public boolean isProvisioned() {
+        return mProvisioned;
+    }
+
+    public void setProvisioned(boolean provisioned) {
+        mProvisioned = provisioned;
+    }
+
+    public PowerManager.WakeLock getWakelock() {
+        return mWakelock;
+    }
+
+    /**
+     * Sets the {@link WorkSource} of the {@link PowerManager.WakeLock} returned by {@link
+     * #getWakelock()}.
+     */
+    @VisibleForTesting
+    public void setWorkSource(@Nullable WorkSource workSource) {
+        // TODO: This is for testing, unfortunately WakeLock is final and WorkSource is not exposed
+        mWakelock.setWorkSource(workSource);
+    }
+
+    public void setWakelock(PowerManager.WakeLock wakelock) {
+        mWakelock = wakelock;
+    }
+
+    public Handler getBackupHandler() {
+        return mBackupHandler;
+    }
+
+    public void setBackupHandler(BackupHandler backupHandler) {
+        mBackupHandler = backupHandler;
+    }
+
+    public PendingIntent getRunInitIntent() {
+        return mRunInitIntent;
+    }
+
+    public void setRunInitIntent(PendingIntent runInitIntent) {
+        mRunInitIntent = runInitIntent;
+    }
+
+    public HashMap<String, BackupRequest> getPendingBackups() {
+        return mPendingBackups;
+    }
+
+    public void setPendingBackups(
+            HashMap<String, BackupRequest> pendingBackups) {
+        mPendingBackups = pendingBackups;
+    }
+
+    public Object getQueueLock() {
+        return mQueueLock;
+    }
+
+    public boolean isBackupRunning() {
+        return mBackupRunning;
+    }
+
+    public void setBackupRunning(boolean backupRunning) {
+        mBackupRunning = backupRunning;
+    }
+
+    public long getLastBackupPass() {
+        return mLastBackupPass;
+    }
+
+    public void setLastBackupPass(long lastBackupPass) {
+        mLastBackupPass = lastBackupPass;
+    }
+
+    public Object getClearDataLock() {
+        return mClearDataLock;
+    }
+
+    public boolean isClearingData() {
+        return mClearingData;
+    }
+
+    public void setClearingData(boolean clearingData) {
+        mClearingData = clearingData;
+    }
+
+    public boolean isRestoreInProgress() {
+        return mIsRestoreInProgress;
+    }
+
+    public void setRestoreInProgress(boolean restoreInProgress) {
+        mIsRestoreInProgress = restoreInProgress;
+    }
+
+    public Queue<PerformUnifiedRestoreTask> getPendingRestores() {
+        return mPendingRestores;
+    }
+
+    public ActiveRestoreSession getActiveRestoreSession() {
+        return mActiveRestoreSession;
+    }
+
+    public void setActiveRestoreSession(
+            ActiveRestoreSession activeRestoreSession) {
+        mActiveRestoreSession = activeRestoreSession;
+    }
+
+    public SparseArray<Operation> getCurrentOperations() {
+        return mCurrentOperations;
+    }
+
+    public Object getCurrentOpLock() {
+        return mCurrentOpLock;
+    }
+
+    public SparseArray<AdbParams> getAdbBackupRestoreConfirmations() {
+        return mAdbBackupRestoreConfirmations;
+    }
+
+    public File getBaseStateDir() {
+        return mBaseStateDir;
+    }
+
+    public void setBaseStateDir(File baseStateDir) {
+        mBaseStateDir = baseStateDir;
+    }
+
+    public File getDataDir() {
+        return mDataDir;
+    }
+
+    public void setDataDir(File dataDir) {
+        mDataDir = dataDir;
+    }
+
+    @Nullable
+    public DataChangedJournal getJournal() {
+        return mJournal;
+    }
+
+    public void setJournal(@Nullable DataChangedJournal journal) {
+        mJournal = journal;
+    }
+
+    public SecureRandom getRng() {
+        return mRng;
+    }
+
+    public Set<String> getAncestralPackages() {
+        return mAncestralPackages;
+    }
+
+    public void setAncestralPackages(Set<String> ancestralPackages) {
+        mAncestralPackages = ancestralPackages;
+    }
+
+    public long getAncestralToken() {
+        return mAncestralToken;
+    }
+
+    public void setAncestralToken(long ancestralToken) {
+        mAncestralToken = ancestralToken;
+    }
+
+    public long getCurrentToken() {
+        return mCurrentToken;
+    }
+
+    public void setCurrentToken(long currentToken) {
+        mCurrentToken = currentToken;
+    }
+
+    public ArraySet<String> getPendingInits() {
+        return mPendingInits;
+    }
+
+    /** Clear all pending transport initializations. */
+    public void clearPendingInits() {
+        mPendingInits.clear();
+    }
+
+    public PerformFullTransportBackupTask getRunningFullBackupTask() {
+        return mRunningFullBackupTask;
+    }
+
+    public void setRunningFullBackupTask(
+            PerformFullTransportBackupTask runningFullBackupTask) {
+        mRunningFullBackupTask = runningFullBackupTask;
+    }
+
+    /**
+     *  Utility: build a new random integer token. The low bits are the ordinal of the operation for
+     *  near-time uniqueness, and the upper bits are random for app-side unpredictability.
+     */
+    public int generateRandomIntegerToken() {
+        int token = mTokenGenerator.nextInt();
+        if (token < 0) token = -token;
+        token &= ~0xFF;
+        token |= (mNextToken.incrementAndGet() & 0xFF);
+        return token;
+    }
+
+    /**
+     * Construct a backup agent instance for the metadata pseudopackage. This is a process-local
+     * non-lifecycle agent instance, so we manually set up the context topology for it.
+     */
+    public BackupAgent makeMetadataAgent() {
+        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
+        pmAgent.attach(mContext);
+        pmAgent.onCreate();
+        return pmAgent;
+    }
+
+    /**
+     * Same as {@link #makeMetadataAgent()} but with explicit package-set configuration.
+     */
+    public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
+        PackageManagerBackupAgent pmAgent =
+                new PackageManagerBackupAgent(mPackageManager, packages);
+        pmAgent.attach(mContext);
+        pmAgent.onCreate();
+        return pmAgent;
+    }
+
+    private void initPackageTracking() {
+        if (MORE_DEBUG) Slog.v(TAG, "` tracking");
+
+        // Remember our ancestral dataset
+        mTokenFile = new File(mBaseStateDir, "ancestral");
+        try (DataInputStream tokenStream = new DataInputStream(new BufferedInputStream(
+                new FileInputStream(mTokenFile)))) {
+            int version = tokenStream.readInt();
+            if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
+                mAncestralToken = tokenStream.readLong();
+                mCurrentToken = tokenStream.readLong();
+
+                int numPackages = tokenStream.readInt();
+                if (numPackages >= 0) {
+                    mAncestralPackages = new HashSet<>();
+                    for (int i = 0; i < numPackages; i++) {
+                        String pkgName = tokenStream.readUTF();
+                        mAncestralPackages.add(pkgName);
+                    }
+                }
+            }
+        } catch (FileNotFoundException fnf) {
+            // Probably innocuous
+            Slog.v(TAG, "No ancestral data");
+        } catch (IOException e) {
+            Slog.w(TAG, "Unable to read token file", e);
+        }
+
+        mProcessedPackagesJournal = new ProcessedPackagesJournal(mBaseStateDir);
+        mProcessedPackagesJournal.init();
+
+        synchronized (mQueueLock) {
+            // Resume the full-data backup queue
+            mFullBackupQueue = readFullBackupSchedule();
+        }
+
+        // Register for broadcasts about package install, etc., so we can
+        // update the provider list.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(mBroadcastReceiver, filter);
+        // Register for events related to sdcard installation.
+        IntentFilter sdFilter = new IntentFilter();
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+    }
+
+    private ArrayList<FullBackupEntry> readFullBackupSchedule() {
+        boolean changed = false;
+        ArrayList<FullBackupEntry> schedule = null;
+        List<PackageInfo> apps =
+                PackageManagerBackupAgent.getStorableApplications(mPackageManager);
+
+        if (mFullBackupScheduleFile.exists()) {
+            try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
+                 BufferedInputStream bufStream = new BufferedInputStream(fstream);
+                 DataInputStream in = new DataInputStream(bufStream)) {
+                int version = in.readInt();
+                if (version != SCHEDULE_FILE_VERSION) {
+                    Slog.e(TAG, "Unknown backup schedule version " + version);
+                    return null;
+                }
+
+                final int numPackages = in.readInt();
+                schedule = new ArrayList<>(numPackages);
+
+                // HashSet instead of ArraySet specifically because we want the eventual
+                // lookups against O(hundreds) of entries to be as fast as possible, and
+                // we discard the set immediately after the scan so the extra memory
+                // overhead is transient.
+                HashSet<String> foundApps = new HashSet<>(numPackages);
+
+                for (int i = 0; i < numPackages; i++) {
+                    String pkgName = in.readUTF();
+                    long lastBackup = in.readLong();
+                    foundApps.add(pkgName); // all apps that we've addressed already
+                    try {
+                        PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
+                        if (AppBackupUtils.appGetsFullBackup(pkg)
+                                && AppBackupUtils.appIsEligibleForBackup(
+                                pkg.applicationInfo, mPackageManager)) {
+                            schedule.add(new FullBackupEntry(pkgName, lastBackup));
+                        } else {
+                            if (DEBUG) {
+                                Slog.i(TAG, "Package " + pkgName
+                                        + " no longer eligible for full backup");
+                            }
+                        }
+                    } catch (NameNotFoundException e) {
+                        if (DEBUG) {
+                            Slog.i(TAG, "Package " + pkgName
+                                    + " not installed; dropping from full backup");
+                        }
+                    }
+                }
+
+                // New apps can arrive "out of band" via OTA and similar, so we also need to
+                // scan to make sure that we're tracking all full-backup candidates properly
+                for (PackageInfo app : apps) {
+                    if (AppBackupUtils.appGetsFullBackup(app)
+                            && AppBackupUtils.appIsEligibleForBackup(
+                            app.applicationInfo, mPackageManager)) {
+                        if (!foundApps.contains(app.packageName)) {
+                            if (MORE_DEBUG) {
+                                Slog.i(TAG, "New full backup app " + app.packageName + " found");
+                            }
+                            schedule.add(new FullBackupEntry(app.packageName, 0));
+                            changed = true;
+                        }
+                    }
+                }
+
+                Collections.sort(schedule);
+            } catch (Exception e) {
+                Slog.e(TAG, "Unable to read backup schedule", e);
+                mFullBackupScheduleFile.delete();
+                schedule = null;
+            }
+        }
+
+        if (schedule == null) {
+            // no prior queue record, or unable to read it.  Set up the queue
+            // from scratch.
+            changed = true;
+            schedule = new ArrayList<>(apps.size());
+            for (PackageInfo info : apps) {
+                if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
+                        info.applicationInfo, mPackageManager)) {
+                    schedule.add(new FullBackupEntry(info.packageName, 0));
+                }
+            }
+        }
+
+        if (changed) {
+            writeFullBackupScheduleAsync();
+        }
+        return schedule;
+    }
+
+    private Runnable mFullBackupScheduleWriter = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mQueueLock) {
+                try {
+                    ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
+                    DataOutputStream bufOut = new DataOutputStream(bufStream);
+                    bufOut.writeInt(SCHEDULE_FILE_VERSION);
+
+                    // version 1:
+                    //
+                    // [int] # of packages in the queue = N
+                    // N * {
+                    //     [utf8] package name
+                    //     [long] last backup time for this package
+                    //     }
+                    int numPackages = mFullBackupQueue.size();
+                    bufOut.writeInt(numPackages);
+
+                    for (int i = 0; i < numPackages; i++) {
+                        FullBackupEntry entry = mFullBackupQueue.get(i);
+                        bufOut.writeUTF(entry.packageName);
+                        bufOut.writeLong(entry.lastBackup);
+                    }
+                    bufOut.flush();
+
+                    AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
+                    FileOutputStream out = af.startWrite();
+                    out.write(bufStream.toByteArray());
+                    af.finishWrite(out);
+                } catch (Exception e) {
+                    Slog.e(TAG, "Unable to write backup schedule!", e);
+                }
+            }
+        }
+    };
+
+    private void writeFullBackupScheduleAsync() {
+        mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
+        mBackupHandler.post(mFullBackupScheduleWriter);
+    }
+
+    private void parseLeftoverJournals() {
+        ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(mJournalDir);
+        for (DataChangedJournal journal : journals) {
+            if (!journal.equals(mJournal)) {
+                try {
+                    journal.forEach(packageName -> {
+                        Slog.i(TAG, "Found stale backup journal, scheduling");
+                        if (MORE_DEBUG) Slog.i(TAG, "  " + packageName);
+                        dataChangedImpl(packageName);
+                    });
+                } catch (IOException e) {
+                    Slog.e(TAG, "Can't read " + journal, e);
+                }
+            }
+        }
+    }
+
+    /** Used for generating random salts or passwords. */
+    public byte[] randomBytes(int bits) {
+        byte[] array = new byte[bits / 8];
+        mRng.nextBytes(array);
+        return array;
+    }
+
+    /** For adb backup/restore. */
+    public boolean setBackupPassword(String currentPw, String newPw) {
+        return mBackupPasswordManager.setBackupPassword(currentPw, newPw);
+    }
+
+    /** For adb backup/restore. */
+    public boolean hasBackupPassword() {
+        return mBackupPasswordManager.hasBackupPassword();
+    }
+
+    /** For adb backup/restore. */
+    public boolean backupPasswordMatches(String currentPw) {
+        return mBackupPasswordManager.backupPasswordMatches(currentPw);
+    }
+
+    /**
+     * Maintain persistent state around whether need to do an initialize operation. This will lock
+     * on {@link #getQueueLock()}.
+     */
+    public void recordInitPending(
+            boolean isPending, String transportName, String transportDirName) {
+        synchronized (mQueueLock) {
+            if (MORE_DEBUG) {
+                Slog.i(TAG, "recordInitPending(" + isPending + ") on transport " + transportName);
+            }
+
+            File stateDir = new File(mBaseStateDir, transportDirName);
+            File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+
+            if (isPending) {
+                // We need an init before we can proceed with sending backup data.
+                // Record that with an entry in our set of pending inits, as well as
+                // journaling it via creation of a sentinel file.
+                mPendingInits.add(transportName);
+                try {
+                    (new FileOutputStream(initPendingFile)).close();
+                } catch (IOException ioe) {
+                    // Something is badly wrong with our permissions; just try to move on
+                }
+            } else {
+                // No more initialization needed; wipe the journal and reset our state.
+                initPendingFile.delete();
+                mPendingInits.remove(transportName);
+            }
+        }
+    }
+
+    /**
+     * Reset all of our bookkeeping because the backend data has been wiped (for example due to idle
+     * expiry), so we must re-upload all saved settings.
+     */
+    public void resetBackupState(File stateFileDir) {
+        synchronized (mQueueLock) {
+            mProcessedPackagesJournal.reset();
+
+            mCurrentToken = 0;
+            writeRestoreTokens();
+
+            // Remove all the state files
+            for (File sf : stateFileDir.listFiles()) {
+                // ... but don't touch the needs-init sentinel
+                if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
+                    sf.delete();
+                }
+            }
+        }
+
+        // Enqueue a new backup of every participant
+        synchronized (mBackupParticipants) {
+            final int numParticipants = mBackupParticipants.size();
+            for (int i = 0; i < numParticipants; i++) {
+                HashSet<String> participants = mBackupParticipants.valueAt(i);
+                if (participants != null) {
+                    for (String packageName : participants) {
+                        dataChangedImpl(packageName);
+                    }
+                }
+            }
+        }
+    }
+
+    private void onTransportRegistered(String transportName, String transportDirName) {
+        if (DEBUG) {
+            long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
+            Slog.d(TAG, "Transport " + transportName + " registered " + timeMs
+                    + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)");
+        }
+
+        File stateDir = new File(mBaseStateDir, transportDirName);
+        stateDir.mkdirs();
+
+        File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+        if (initSentinel.exists()) {
+            synchronized (mQueueLock) {
+                mPendingInits.add(transportName);
+
+                // TODO: pick a better starting time than now + 1 minute
+                long delay = 1000 * 60; // one minute, in milliseconds
+                mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+                        System.currentTimeMillis() + delay, mRunInitIntent);
+            }
+        }
+    }
+
+    // ----- Track installation/removal of packages -----
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
+
+            String action = intent.getAction();
+            boolean replacing = false;
+            boolean added = false;
+            boolean changed = false;
+            Bundle extras = intent.getExtras();
+            String[] pkgList = null;
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+                    || Intent.ACTION_PACKAGE_REMOVED.equals(action)
+                    || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+                Uri uri = intent.getData();
+                if (uri == null) {
+                    return;
+                }
+                final String pkgName = uri.getSchemeSpecificPart();
+                if (pkgName != null) {
+                    pkgList = new String[]{pkgName};
+                }
+                changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
+
+                // At package-changed we only care about looking at new transport states
+                if (changed) {
+                    final String[] components =
+                            intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
+                        for (int i = 0; i < components.length; i++) {
+                            Slog.i(TAG, "   * " + components[i]);
+                        }
+                    }
+
+                    mBackupHandler.post(
+                            () -> mTransportManager.onPackageChanged(pkgName, components));
+                    return; // nothing more to do in the PACKAGE_CHANGED case
+                }
+
+                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+                replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+                added = true;
+                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                added = false;
+                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            }
+
+            if (pkgList == null || pkgList.length == 0) {
+                return;
+            }
+
+            final int uid = extras.getInt(Intent.EXTRA_UID);
+            if (added) {
+                synchronized (mBackupParticipants) {
+                    if (replacing) {
+                        // This is the package-replaced case; we just remove the entry
+                        // under the old uid and fall through to re-add.  If an app
+                        // just added key/value backup participation, this picks it up
+                        // as a known participant.
+                        removePackageParticipantsLocked(pkgList, uid);
+                    }
+                    addPackageParticipantsLocked(pkgList);
+                }
+                // If they're full-backup candidates, add them there instead
+                final long now = System.currentTimeMillis();
+                for (final String packageName : pkgList) {
+                    try {
+                        PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
+                        if (AppBackupUtils.appGetsFullBackup(app)
+                                && AppBackupUtils.appIsEligibleForBackup(
+                                app.applicationInfo, mPackageManager)) {
+                            enqueueFullBackup(packageName, now);
+                            scheduleNextFullBackupJob(0);
+                        } else {
+                            // The app might have just transitioned out of full-data into
+                            // doing key/value backups, or might have just disabled backups
+                            // entirely.  Make sure it is no longer in the full-data queue.
+                            synchronized (mQueueLock) {
+                                dequeueFullBackupLocked(packageName);
+                            }
+                            writeFullBackupScheduleAsync();
+                        }
+
+                        mBackupHandler.post(
+                                () -> mTransportManager.onPackageAdded(packageName));
+
+                    } catch (NameNotFoundException e) {
+                        // doesn't really exist; ignore it
+                        if (DEBUG) {
+                            Slog.w(TAG, "Can't resolve new app " + packageName);
+                        }
+                    }
+                }
+
+                // Whenever a package is added or updated we need to update
+                // the package metadata bookkeeping.
+                dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
+            } else {
+                if (replacing) {
+                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
+                } else {
+                    // Outright removal.  In the full-data case, the app will be dropped
+                    // from the queue when its (now obsolete) name comes up again for
+                    // backup.
+                    synchronized (mBackupParticipants) {
+                        removePackageParticipantsLocked(pkgList, uid);
+                    }
+                }
+                for (final String pkgName : pkgList) {
+                    mBackupHandler.post(
+                            () -> mTransportManager.onPackageRemoved(pkgName));
+                }
+            }
+        }
+    };
+
+    // Add the backup agents in the given packages to our set of known backup participants.
+    // If 'packageNames' is null, adds all backup agents in the whole system.
+    private void addPackageParticipantsLocked(String[] packageNames) {
+        // Look for apps that define the android:backupAgent attribute
+        List<PackageInfo> targetApps = allAgentPackages();
+        if (packageNames != null) {
+            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
+            for (String packageName : packageNames) {
+                addPackageParticipantsLockedInner(packageName, targetApps);
+            }
+        } else {
+            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
+            addPackageParticipantsLockedInner(null, targetApps);
+        }
+    }
+
+    private void addPackageParticipantsLockedInner(String packageName,
+            List<PackageInfo> targetPkgs) {
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "Examining " + packageName + " for backup agent");
+        }
+
+        for (PackageInfo pkg : targetPkgs) {
+            if (packageName == null || pkg.packageName.equals(packageName)) {
+                int uid = pkg.applicationInfo.uid;
+                HashSet<String> set = mBackupParticipants.get(uid);
+                if (set == null) {
+                    set = new HashSet<>();
+                    mBackupParticipants.put(uid, set);
+                }
+                set.add(pkg.packageName);
+                if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
+
+                // Schedule a backup for it on general principles
+                if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
+                Message msg = mBackupHandler
+                        .obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
+                mBackupHandler.sendMessage(msg);
+            }
+        }
+    }
+
+    // Remove the given packages' entries from our known active set.
+    private void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
+        if (packageNames == null) {
+            Slog.w(TAG, "removePackageParticipants with null list");
+            return;
+        }
+
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
+                    + " #" + packageNames.length);
+        }
+        for (String pkg : packageNames) {
+            // Known previous UID, so we know which package set to check
+            HashSet<String> set = mBackupParticipants.get(oldUid);
+            if (set != null && set.contains(pkg)) {
+                removePackageFromSetLocked(set, pkg);
+                if (set.isEmpty()) {
+                    if (MORE_DEBUG) Slog.v(TAG, "  last one of this uid; purging set");
+                    mBackupParticipants.remove(oldUid);
+                }
+            }
+        }
+    }
+
+    private void removePackageFromSetLocked(final HashSet<String> set,
+            final String packageName) {
+        if (set.contains(packageName)) {
+            // Found it.  Remove this one package from the bookkeeping, and
+            // if it's the last participating app under this uid we drop the
+            // (now-empty) set as well.
+            // Note that we deliberately leave it 'known' in the "ever backed up"
+            // bookkeeping so that its current-dataset data will be retrieved
+            // if the app is subsequently reinstalled
+            if (MORE_DEBUG) Slog.v(TAG, "  removing participant " + packageName);
+            set.remove(packageName);
+            mPendingBackups.remove(packageName);
+        }
+    }
+
+    // Returns the set of all applications that define an android:backupAgent attribute
+    private List<PackageInfo> allAgentPackages() {
+        // !!! TODO: cache this and regenerate only when necessary
+        int flags = PackageManager.GET_SIGNING_CERTIFICATES;
+        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
+        int numPackages = packages.size();
+        for (int a = numPackages - 1; a >= 0; a--) {
+            PackageInfo pkg = packages.get(a);
+            try {
+                ApplicationInfo app = pkg.applicationInfo;
+                if (((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
+                        || app.backupAgentName == null
+                        || (app.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0) {
+                    packages.remove(a);
+                } else {
+                    // we will need the shared library path, so look that up and store it here.
+                    // This is used implicitly when we pass the PackageInfo object off to
+                    // the Activity Manager to launch the app for backup/restore purposes.
+                    app = mPackageManager.getApplicationInfo(pkg.packageName,
+                            PackageManager.GET_SHARED_LIBRARY_FILES);
+                    pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
+                    pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
+                }
+            } catch (NameNotFoundException e) {
+                packages.remove(a);
+            }
+        }
+        return packages;
+    }
+
+    /**
+     * Called from the backup tasks: record that the given app has been successfully backed up at
+     * least once. This includes both key/value and full-data backups through the transport.
+     */
+    public void logBackupComplete(String packageName) {
+        if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
+
+        for (String receiver : mConstants.getBackupFinishedNotificationReceivers()) {
+            final Intent notification = new Intent();
+            notification.setAction(BACKUP_FINISHED_ACTION);
+            notification.setPackage(receiver);
+            notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES
+                    | Intent.FLAG_RECEIVER_FOREGROUND);
+            notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
+            mContext.sendBroadcastAsUser(notification, UserHandle.OWNER);
+        }
+
+        mProcessedPackagesJournal.addPackage(packageName);
+    }
+
+    /**
+     * Persistently record the current and ancestral backup tokens, as well as the set of packages
+     * with data available in the ancestral dataset.
+     */
+    public void writeRestoreTokens() {
+        try (RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd")) {
+            // First, the version number of this record, for futureproofing
+            af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
+
+            // Write the ancestral and current tokens
+            af.writeLong(mAncestralToken);
+            af.writeLong(mCurrentToken);
+
+            // Now write the set of ancestral packages
+            if (mAncestralPackages == null) {
+                af.writeInt(-1);
+            } else {
+                af.writeInt(mAncestralPackages.size());
+                if (DEBUG) Slog.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
+                for (String pkgName : mAncestralPackages) {
+                    af.writeUTF(pkgName);
+                    if (MORE_DEBUG) Slog.v(TAG, "   " + pkgName);
+                }
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Unable to write token file:", e);
+        }
+    }
+
+    /** Fires off a backup agent, blocking until it attaches or times out. */
+    @Nullable
+    public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
+        IBackupAgent agent = null;
+        synchronized (mAgentConnectLock) {
+            mConnecting = true;
+            mConnectedAgent = null;
+            try {
+                if (mActivityManager.bindBackupAgent(app.packageName, mode,
+                        UserHandle.USER_OWNER)) {
+                    Slog.d(TAG, "awaiting agent for " + app);
+
+                    // success; wait for the agent to arrive
+                    // only wait 10 seconds for the bind to happen
+                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+                    while (mConnecting && mConnectedAgent == null
+                            && (System.currentTimeMillis() < timeoutMark)) {
+                        try {
+                            mAgentConnectLock.wait(5000);
+                        } catch (InterruptedException e) {
+                            // just bail
+                            Slog.w(TAG, "Interrupted: " + e);
+                            mConnecting = false;
+                            mConnectedAgent = null;
+                        }
+                    }
+
+                    // if we timed out with no connect, abort and move on
+                    if (mConnecting) {
+                        Slog.w(TAG, "Timeout waiting for agent " + app);
+                        mConnectedAgent = null;
+                    }
+                    if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
+                    agent = mConnectedAgent;
+                }
+            } catch (RemoteException e) {
+                // can't happen - ActivityManager is local
+            }
+        }
+        if (agent == null) {
+            try {
+                mActivityManager.clearPendingBackup();
+            } catch (RemoteException e) {
+                // can't happen - ActivityManager is local
+            }
+        }
+        return agent;
+    }
+
+    /** Unbind from a backup agent. */
+    public void unbindAgent(ApplicationInfo app) {
+        try {
+            mActivityManager.unbindBackupAgent(app);
+        } catch (RemoteException e) {
+            // Can't happen - activity manager is local
+        }
+    }
+
+    /**
+     * Clear an application's data, blocking until the operation completes or times out. If {@code
+     * keepSystemState} is {@code true}, we intentionally do not clear system state that would
+     * ordinarily also be cleared, because we aren't actually wiping the app back to empty; we're
+     * bringing it into the actual expected state related to the already-restored notification state
+     * etc.
+     */
+    public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
+        // Don't wipe packages marked allowClearUserData=false
+        try {
+            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
+                if (MORE_DEBUG) {
+                    Slog.i(TAG, "allowClearUserData=false so not wiping "
+                            + packageName);
+                }
+                return;
+            }
+        } catch (NameNotFoundException e) {
+            Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
+            return;
+        }
+
+        ClearDataObserver observer = new ClearDataObserver(this);
+
+        synchronized (mClearDataLock) {
+            mClearingData = true;
+            try {
+                mActivityManager.clearApplicationUserData(
+                        packageName, keepSystemState, observer, 0);
+            } catch (RemoteException e) {
+                // can't happen because the activity manager is in this process
+            }
+
+            // only wait 10 seconds for the clear data to happen
+            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
+                try {
+                    mClearDataLock.wait(5000);
+                } catch (InterruptedException e) {
+                    // won't happen, but still.
+                    mClearingData = false;
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the restore-set token for the best-available restore set for this {@code packageName}:
+     * the active set if possible, else the ancestral one. Returns zero if none available.
+     */
+    public long getAvailableRestoreToken(String packageName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "getAvailableRestoreToken");
+
+        long token = mAncestralToken;
+        synchronized (mQueueLock) {
+            if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
+                if (MORE_DEBUG) {
+                    Slog.i(TAG, "App in ever-stored, so using current token");
+                }
+                token = mCurrentToken;
+            }
+        }
+        if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
+        return token;
+    }
+
+    /**
+     * Requests a backup for the inputted {@code packages}.
+     *
+     * @see #requestBackup(String[], IBackupObserver, IBackupManagerMonitor, int).
+     */
+    public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
+        return requestBackup(packages, observer, null, flags);
+    }
+
+    /**
+     * Requests a backup for the inputted {@code packages} with a specified {@link
+     * IBackupManagerMonitor}.
+     */
+    public int requestBackup(String[] packages, IBackupObserver observer,
+            IBackupManagerMonitor monitor, int flags) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
+
+        if (packages == null || packages.length < 1) {
+            Slog.e(TAG, "No packages named for backup request");
+            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
+            monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
+                    BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
+                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+            throw new IllegalArgumentException("No packages are provided for backup");
+        }
+
+        if (!mEnabled || !mProvisioned) {
+            Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
+            BackupObserverUtils.sendBackupFinished(observer,
+                    BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+            final int logTag = mProvisioned
+                    ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
+                    : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
+            monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
+                    BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
+            return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
+        }
+
+        final TransportClient transportClient;
+        final String transportDirName;
+        try {
+            transportDirName =
+                    mTransportManager.getTransportDirName(
+                            mTransportManager.getCurrentTransportName());
+            transportClient =
+                    mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
+        } catch (TransportNotRegisteredException e) {
+            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
+            monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
+                    BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
+                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+            return BackupManager.ERROR_TRANSPORT_ABORTED;
+        }
+
+        OnTaskFinishedListener listener =
+                caller -> mTransportManager.disposeOfTransportClient(transportClient, caller);
+
+        ArrayList<String> fullBackupList = new ArrayList<>();
+        ArrayList<String> kvBackupList = new ArrayList<>();
+        for (String packageName : packages) {
+            if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
+                kvBackupList.add(packageName);
+                continue;
+            }
+            try {
+                PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
+                        PackageManager.GET_SIGNING_CERTIFICATES);
+                if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
+                        mPackageManager)) {
+                    BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
+                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+                    continue;
+                }
+                if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
+                    fullBackupList.add(packageInfo.packageName);
+                } else {
+                    kvBackupList.add(packageInfo.packageName);
+                }
+            } catch (NameNotFoundException e) {
+                BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
+                        BackupManager.ERROR_PACKAGE_NOT_FOUND);
+            }
+        }
+        EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
+                fullBackupList.size());
+        if (MORE_DEBUG) {
+            Slog.i(TAG, "Backup requested for " + packages.length + " packages, of them: "
+                    + fullBackupList.size() + " full backups, " + kvBackupList.size()
+                    + " k/v backups");
+        }
+
+        boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
+
+        Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
+        msg.obj = new BackupParams(transportClient, transportDirName, kvBackupList, fullBackupList,
+                observer, monitor, listener, true, nonIncrementalBackup);
+        mBackupHandler.sendMessage(msg);
+        return BackupManager.SUCCESS;
+    }
+
+    /** Cancel all running backups. */
+    public void cancelBackups() {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
+        if (MORE_DEBUG) {
+            Slog.i(TAG, "cancelBackups() called.");
+        }
+        final long oldToken = Binder.clearCallingIdentity();
+        try {
+            List<Integer> operationsToCancel = new ArrayList<>();
+            synchronized (mCurrentOpLock) {
+                for (int i = 0; i < mCurrentOperations.size(); i++) {
+                    Operation op = mCurrentOperations.valueAt(i);
+                    int token = mCurrentOperations.keyAt(i);
+                    if (op.type == OP_TYPE_BACKUP) {
+                        operationsToCancel.add(token);
+                    }
+                }
+            }
+            for (Integer token : operationsToCancel) {
+                handleCancel(token, true /* cancelAll */);
+            }
+            // We don't want the backup jobs to kick in any time soon.
+            // Reschedules them to run in the distant future.
+            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
+            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
+        } finally {
+            Binder.restoreCallingIdentity(oldToken);
+        }
+    }
+
+    /** Schedule a timeout message for the operation identified by {@code token}. */
+    public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
+            int operationType) {
+        if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
+            Slog.wtf(TAG, "prepareOperationTimeout() doesn't support operation "
+                    + Integer.toHexString(token) + " of type " + operationType);
+            return;
+        }
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
+                    + " interval=" + interval + " callback=" + callback);
+        }
+
+        synchronized (mCurrentOpLock) {
+            mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
+            Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
+                    token, 0, callback);
+            mBackupHandler.sendMessageDelayed(msg, interval);
+        }
+    }
+
+    private int getMessageIdForOperationType(int operationType) {
+        switch (operationType) {
+            case OP_TYPE_BACKUP_WAIT:
+                return MSG_BACKUP_OPERATION_TIMEOUT;
+            case OP_TYPE_RESTORE_WAIT:
+                return MSG_RESTORE_OPERATION_TIMEOUT;
+            default:
+                Slog.wtf(TAG, "getMessageIdForOperationType called on invalid operation type: "
+                        + operationType);
+                return -1;
+        }
+    }
+
+    /**
+     * Add an operation to the list of currently running operations. Used for cancellation,
+     * completion and timeout callbacks that act on the operation via the {@code token}.
+     */
+    public void putOperation(int token, Operation operation) {
+        if (MORE_DEBUG) {
+            Slog.d(TAG, "Adding operation token=" + Integer.toHexString(token) + ", operation type="
+                    + operation.type);
+        }
+        synchronized (mCurrentOpLock) {
+            mCurrentOperations.put(token, operation);
+        }
+    }
+
+    /**
+     * Remove an operation from the list of currently running operations. An operation is removed
+     * when it is completed, cancelled, or timed out, and thus no longer running.
+     */
+    public void removeOperation(int token) {
+        if (MORE_DEBUG) {
+            Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
+        }
+        synchronized (mCurrentOpLock) {
+            if (mCurrentOperations.get(token) == null) {
+                Slog.w(TAG, "Duplicate remove for operation. token="
+                        + Integer.toHexString(token));
+            }
+            mCurrentOperations.remove(token);
+        }
+    }
+
+    /** Block until we received an operation complete message (from the agent or cancellation). */
+    public boolean waitUntilOperationComplete(int token) {
+        if (MORE_DEBUG) {
+            Slog.i(TAG, "Blocking until operation complete for "
+                    + Integer.toHexString(token));
+        }
+        int finalState = OP_PENDING;
+        Operation op = null;
+        synchronized (mCurrentOpLock) {
+            while (true) {
+                op = mCurrentOperations.get(token);
+                if (op == null) {
+                    // mysterious disappearance: treat as success with no callback
+                    break;
+                } else {
+                    if (op.state == OP_PENDING) {
+                        try {
+                            mCurrentOpLock.wait();
+                        } catch (InterruptedException e) {
+                        }
+                        // When the wait is notified we loop around and recheck the current state
+                    } else {
+                        if (MORE_DEBUG) {
+                            Slog.d(TAG, "Unblocked waiting for operation token="
+                                    + Integer.toHexString(token));
+                        }
+                        // No longer pending; we're done
+                        finalState = op.state;
+                        break;
+                    }
+                }
+            }
+        }
+
+        removeOperation(token);
+        if (op != null) {
+            mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
+        }
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "operation " + Integer.toHexString(token)
+                    + " complete: finalState=" + finalState);
+        }
+        return finalState == OP_ACKNOWLEDGED;
+    }
+
+    /** Cancel the operation associated with {@code token}. */
+    public void handleCancel(int token, boolean cancelAll) {
+        // Notify any synchronous waiters
+        Operation op = null;
+        synchronized (mCurrentOpLock) {
+            op = mCurrentOperations.get(token);
+            if (MORE_DEBUG) {
+                if (op == null) {
+                    Slog.w(TAG, "Cancel of token " + Integer.toHexString(token)
+                            + " but no op found");
+                }
+            }
+            int state = (op != null) ? op.state : OP_TIMEOUT;
+            if (state == OP_ACKNOWLEDGED) {
+                // The operation finished cleanly, so we have nothing more to do.
+                if (DEBUG) {
+                    Slog.w(TAG, "Operation already got an ack."
+                            + "Should have been removed from mCurrentOperations.");
+                }
+                op = null;
+                mCurrentOperations.delete(token);
+            } else if (state == OP_PENDING) {
+                if (DEBUG) Slog.v(TAG, "Cancel: token=" + Integer.toHexString(token));
+                op.state = OP_TIMEOUT;
+                // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
+                // called after we receive cancel here. We need this op's state there.
+
+                // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and
+                // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
+                // doesn't require cancellation.
+                if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) {
+                    mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
+                }
+            }
+            mCurrentOpLock.notifyAll();
+        }
+
+        // If there's a TimeoutHandler for this event, call it
+        if (op != null && op.callback != null) {
+            if (MORE_DEBUG) {
+                Slog.v(TAG, "   Invoking cancel on " + op.callback);
+            }
+            op.callback.handleCancel(cancelAll);
+        }
+    }
+
+    /** Returns {@code true} if a backup is currently running, else returns {@code false}. */
+    public boolean isBackupOperationInProgress() {
+        synchronized (mCurrentOpLock) {
+            for (int i = 0; i < mCurrentOperations.size(); i++) {
+                Operation op = mCurrentOperations.valueAt(i);
+                if (op.type == OP_TYPE_BACKUP && op.state == OP_PENDING) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Unbind the backup agent and kill the app if it's a non-system app. */
+    public void tearDownAgentAndKill(ApplicationInfo app) {
+        if (app == null) {
+            // Null means the system package, so just quietly move on.  :)
+            return;
+        }
+
+        try {
+            // unbind and tidy up even on timeout or failure, just in case
+            mActivityManager.unbindBackupAgent(app);
+
+            // The agent was running with a stub Application object, so shut it down.
+            // !!! We hardcode the confirmation UI's package name here rather than use a
+            //     manifest flag!  TODO something less direct.
+            if (app.uid >= Process.FIRST_APPLICATION_UID
+                    && !app.packageName.equals("com.android.backupconfirm")) {
+                if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
+                mActivityManager.killApplicationProcess(app.processName, app.uid);
+            } else {
+                if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
+            }
+        } catch (RemoteException e) {
+            Slog.d(TAG, "Lost app trying to shut down");
+        }
+    }
+
+    /** For adb backup/restore. */
+    public boolean deviceIsEncrypted() {
+        try {
+            return mStorageManager.getEncryptionState()
+                    != StorageManager.ENCRYPTION_STATE_NONE
+                    && mStorageManager.getPasswordType()
+                    != StorageManager.CRYPT_TYPE_DEFAULT;
+        } catch (Exception e) {
+            // If we can't talk to the storagemanager service we have a serious problem; fail
+            // "secure" i.e. assuming that the device is encrypted.
+            Slog.e(TAG, "Unable to communicate with storagemanager service: " + e.getMessage());
+            return true;
+        }
+    }
+
+    // ----- Full-data backup scheduling -----
+
+    /**
+     * Schedule a job to tell us when it's a good time to run a full backup
+     */
+    public void scheduleNextFullBackupJob(long transportMinLatency) {
+        synchronized (mQueueLock) {
+            if (mFullBackupQueue.size() > 0) {
+                // schedule the next job at the point in the future when the least-recently
+                // backed up app comes due for backup again; or immediately if it's already
+                // due.
+                final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
+                final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
+                final long interval = mConstants.getFullBackupIntervalMilliseconds();
+                final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0;
+                final long latency = Math.max(transportMinLatency, appLatency);
+                Runnable r = new Runnable() {
+                    @Override
+                    public void run() {
+                        FullBackupJob.schedule(mContext, latency, mConstants);
+                    }
+                };
+                mBackupHandler.postDelayed(r, 2500);
+            } else {
+                if (DEBUG_SCHEDULING) {
+                    Slog.i(TAG, "Full backup queue empty; not scheduling");
+                }
+            }
+        }
+    }
+
+    /**
+     * Remove a package from the full-data queue.
+     */
+    @GuardedBy("mQueueLock")
+    private void dequeueFullBackupLocked(String packageName) {
+        final int numPackages = mFullBackupQueue.size();
+        for (int i = numPackages - 1; i >= 0; i--) {
+            final FullBackupEntry e = mFullBackupQueue.get(i);
+            if (packageName.equals(e.packageName)) {
+                mFullBackupQueue.remove(i);
+            }
+        }
+    }
+
+    /**
+     * Enqueue full backup for the given app, with a note about when it last ran.
+     */
+    public void enqueueFullBackup(String packageName, long lastBackedUp) {
+        FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
+        synchronized (mQueueLock) {
+            // First, sanity check that we aren't adding a duplicate.  Slow but
+            // straightforward; we'll have at most on the order of a few hundred
+            // items in this list.
+            dequeueFullBackupLocked(packageName);
+
+            // This is also slow but easy for modest numbers of apps: work backwards
+            // from the end of the queue until we find an item whose last backup
+            // time was before this one, then insert this new entry after it.  If we're
+            // adding something new we don't bother scanning, and just prepend.
+            int which = -1;
+            if (lastBackedUp > 0) {
+                for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
+                    final FullBackupEntry entry = mFullBackupQueue.get(which);
+                    if (entry.lastBackup <= lastBackedUp) {
+                        mFullBackupQueue.add(which + 1, newEntry);
+                        break;
+                    }
+                }
+            }
+            if (which < 0) {
+                // this one is earlier than any existing one, so prepend
+                mFullBackupQueue.add(0, newEntry);
+            }
+        }
+        writeFullBackupScheduleAsync();
+    }
+
+    private boolean fullBackupAllowable(String transportName) {
+        if (!mTransportManager.isTransportRegistered(transportName)) {
+            Slog.w(TAG, "Transport not registered; full data backup not performed");
+            return false;
+        }
+
+        // Don't proceed unless we have already established package metadata
+        // for the current dataset via a key/value backup pass.
+        try {
+            String transportDirName = mTransportManager.getTransportDirName(transportName);
+            File stateDir = new File(mBaseStateDir, transportDirName);
+            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
+            if (pmState.length() <= 0) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
+                }
+                return false;
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Conditions are right for a full backup operation, so run one.  The model we use is
+     * to perform one app backup per scheduled job execution, and to reschedule the job
+     * with zero latency as long as conditions remain right and we still have work to do.
+     *
+     * <p>This is the "start a full backup operation" entry point called by the scheduled job.
+     *
+     * @return Whether ongoing work will continue.  The return value here will be passed
+     * along as the return value to the scheduled job's onStartJob() callback.
+     */
+    public boolean beginFullBackup(FullBackupJob scheduledJob) {
+        final long now = System.currentTimeMillis();
+        final long fullBackupInterval;
+        final long keyValueBackupInterval;
+        synchronized (mConstants) {
+            fullBackupInterval = mConstants.getFullBackupIntervalMilliseconds();
+            keyValueBackupInterval = mConstants.getKeyValueBackupIntervalMilliseconds();
+        }
+        FullBackupEntry entry = null;
+        long latency = fullBackupInterval;
+
+        if (!mEnabled || !mProvisioned) {
+            // Backups are globally disabled, so don't proceed.  We also don't reschedule
+            // the job driving automatic backups; that job will be scheduled again when
+            // the user enables backup.
+            if (MORE_DEBUG) {
+                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
+                        + " p=" + mProvisioned + "; ignoring");
+            }
+            return false;
+        }
+
+        // Don't run the backup if we're in battery saver mode, but reschedule
+        // to try again in the not-so-distant future.
+        final PowerSaveState result =
+                mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
+        if (result.batterySaverEnabled) {
+            if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
+            FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
+            return false;
+        }
+
+        if (DEBUG_SCHEDULING) {
+            Slog.i(TAG, "Beginning scheduled full backup operation");
+        }
+
+        // Great; we're able to run full backup jobs now.  See if we have any work to do.
+        synchronized (mQueueLock) {
+            if (mRunningFullBackupTask != null) {
+                Slog.e(TAG, "Backup triggered but one already/still running!");
+                return false;
+            }
+
+            // At this point we think that we have work to do, but possibly not right now.
+            // Any exit without actually running backups will also require that we
+            // reschedule the job.
+            boolean runBackup = true;
+            boolean headBusy;
+
+            do {
+                // Recheck each time, because culling due to ineligibility may
+                // have emptied the queue.
+                if (mFullBackupQueue.size() == 0) {
+                    // no work to do so just bow out
+                    if (DEBUG) {
+                        Slog.i(TAG, "Backup queue empty; doing nothing");
+                    }
+                    runBackup = false;
+                    break;
+                }
+
+                headBusy = false;
+
+                String transportName = mTransportManager.getCurrentTransportName();
+                if (!fullBackupAllowable(transportName)) {
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "Preconditions not met; not running full backup");
+                    }
+                    runBackup = false;
+                    // Typically this means we haven't run a key/value backup yet.  Back off
+                    // full-backup operations by the key/value job's run interval so that
+                    // next time we run, we are likely to be able to make progress.
+                    latency = keyValueBackupInterval;
+                }
+
+                if (runBackup) {
+                    entry = mFullBackupQueue.get(0);
+                    long timeSinceRun = now - entry.lastBackup;
+                    runBackup = (timeSinceRun >= fullBackupInterval);
+                    if (!runBackup) {
+                        // It's too early to back up the next thing in the queue, so bow out
+                        if (MORE_DEBUG) {
+                            Slog.i(TAG, "Device ready but too early to back up next app");
+                        }
+                        // Wait until the next app in the queue falls due for a full data backup
+                        latency = fullBackupInterval - timeSinceRun;
+                        break;  // we know we aren't doing work yet, so bail.
+                    }
+
+                    try {
+                        PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
+                        if (!AppBackupUtils.appGetsFullBackup(appInfo)) {
+                            // The head app isn't supposed to get full-data backups [any more];
+                            // so we cull it and force a loop around to consider the new head
+                            // app.
+                            if (MORE_DEBUG) {
+                                Slog.i(TAG, "Culling package " + entry.packageName
+                                        + " in full-backup queue but not eligible");
+                            }
+                            mFullBackupQueue.remove(0);
+                            headBusy = true; // force the while() condition
+                            continue;
+                        }
+
+                        final int privFlags = appInfo.applicationInfo.privateFlags;
+                        headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
+                                && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
+
+                        if (headBusy) {
+                            final long nextEligible = System.currentTimeMillis()
+                                    + BUSY_BACKOFF_MIN_MILLIS
+                                    + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
+                            if (DEBUG_SCHEDULING) {
+                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                                Slog.i(TAG, "Full backup time but " + entry.packageName
+                                        + " is busy; deferring to "
+                                        + sdf.format(new Date(nextEligible)));
+                            }
+                            // This relocates the app's entry from the head of the queue to
+                            // its order-appropriate position further down, so upon looping
+                            // a new candidate will be considered at the head.
+                            enqueueFullBackup(entry.packageName, nextEligible - fullBackupInterval);
+                        }
+                    } catch (NameNotFoundException nnf) {
+                        // So, we think we want to back this up, but it turns out the package
+                        // in question is no longer installed.  We want to drop it from the
+                        // queue entirely and move on, but if there's nothing else in the queue
+                        // we should bail entirely.  headBusy cannot have been set to true yet.
+                        runBackup = (mFullBackupQueue.size() > 1);
+                    } catch (RemoteException e) {
+                        // Cannot happen; the Activity Manager is in the same process
+                    }
+                }
+            } while (headBusy);
+
+            if (!runBackup) {
+                if (DEBUG_SCHEDULING) {
+                    Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
+                }
+                final long deferTime = latency;     // pin for the closure
+                mBackupHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        FullBackupJob.schedule(mContext, deferTime, mConstants);
+                    }
+                });
+                return false;
+            }
+
+            // Okay, the top thing is ready for backup now.  Do it.
+            mFullBackupQueue.remove(0);
+            CountDownLatch latch = new CountDownLatch(1);
+            String[] pkg = new String[]{entry.packageName};
+            mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
+                    this,
+                    /* observer */ null,
+                    pkg,
+                    /* updateSchedule */ true,
+                    scheduledJob,
+                    latch,
+                    /* backupObserver */ null,
+                    /* monitor */ null,
+                    /* userInitiated */ false,
+                    "BMS.beginFullBackup()");
+            // Acquiring wakelock for PerformFullTransportBackupTask before its start.
+            mWakelock.acquire();
+            (new Thread(mRunningFullBackupTask)).start();
+        }
+
+        return true;
+    }
+
+    /**
+     * The job scheduler says our constraints don't hold anymore, so tear down any ongoing backup
+     * task right away.
+     */
+    public void endFullBackup() {
+        // offload the mRunningFullBackupTask.handleCancel() call to another thread,
+        // as we might have to wait for mCancelLock
+        Runnable endFullBackupRunnable = new Runnable() {
+            @Override
+            public void run() {
+                PerformFullTransportBackupTask pftbt = null;
+                synchronized (mQueueLock) {
+                    if (mRunningFullBackupTask != null) {
+                        pftbt = mRunningFullBackupTask;
+                    }
+                }
+                if (pftbt != null) {
+                    if (DEBUG_SCHEDULING) {
+                        Slog.i(TAG, "Telling running backup to stop");
+                    }
+                    pftbt.handleCancel(true);
+                }
+            }
+        };
+        new Thread(endFullBackupRunnable, "end-full-backup").start();
+    }
+
+    /** Used by both incremental and full restore to restore widget data. */
+    public void restoreWidgetData(String packageName, byte[] widgetData) {
+        // Apply the restored widget state and generate the ID update for the app
+        // TODO: http://b/22388012
+        if (MORE_DEBUG) {
+            Slog.i(TAG, "Incorporating restored widget data");
+        }
+        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
+    }
+
+    // *****************************
+    // NEW UNIFIED RESTORE IMPLEMENTATION
+    // *****************************
+
+    /** Schedule a backup pass for {@code packageName}. */
+    public void dataChangedImpl(String packageName) {
+        HashSet<String> targets = dataChangedTargets(packageName);
+        dataChangedImpl(packageName, targets);
+    }
+
+    private void dataChangedImpl(String packageName, HashSet<String> targets) {
+        // Record that we need a backup pass for the caller.  Since multiple callers
+        // may share a uid, we need to note all candidates within that uid and schedule
+        // a backup pass for each of them.
+        if (targets == null) {
+            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+                    + " uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        synchronized (mQueueLock) {
+            // Note that this client has made data changes that need to be backed up
+            if (targets.contains(packageName)) {
+                // Add the caller to the set of pending backups.  If there is
+                // one already there, then overwrite it, but no harm done.
+                BackupRequest req = new BackupRequest(packageName);
+                if (mPendingBackups.put(packageName, req) == null) {
+                    if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
+
+                    // Journal this request in case of crash.  The put()
+                    // operation returned null when this package was not already
+                    // in the set; we want to avoid touching the disk redundantly.
+                    writeToJournalLocked(packageName);
+                }
+            }
+        }
+
+        // ...and schedule a backup pass if necessary
+        KeyValueBackupJob.schedule(mContext, mConstants);
+    }
+
+    // Note: packageName is currently unused, but may be in the future
+    private HashSet<String> dataChangedTargets(String packageName) {
+        // If the caller does not hold the BACKUP permission, it can only request a
+        // backup of its own data.
+        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
+                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+            synchronized (mBackupParticipants) {
+                return mBackupParticipants.get(Binder.getCallingUid());
+            }
+        }
+
+        // a caller with full permission can ask to back up any participating app
+        if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
+            return Sets.newHashSet(PACKAGE_MANAGER_SENTINEL);
+        } else {
+            synchronized (mBackupParticipants) {
+                return SparseArrayUtils.union(mBackupParticipants);
+            }
+        }
+    }
+
+    private void writeToJournalLocked(String str) {
+        try {
+            if (mJournal == null) mJournal = DataChangedJournal.newJournal(mJournalDir);
+            mJournal.addPackage(str);
+        } catch (IOException e) {
+            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
+            mJournal = null;
+        }
+    }
+
+    // ----- IBackupManager binder interface -----
+
+    /** Sent from an app's backup agent to let the service know that there's new data to backup. */
+    public void dataChanged(final String packageName) {
+        final int callingUserHandle = UserHandle.getCallingUserId();
+        if (callingUserHandle != UserHandle.USER_SYSTEM) {
+            // TODO: http://b/22388012
+            // App is running under a non-owner user profile.  For now, we do not back
+            // up data from secondary user profiles.
+            // TODO: backups for all user profiles although don't add backup for profiles
+            // without adding admin control in DevicePolicyManager.
+            if (MORE_DEBUG) {
+                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
+                        + callingUserHandle);
+            }
+            return;
+        }
+
+        final HashSet<String> targets = dataChangedTargets(packageName);
+        if (targets == null) {
+            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+                    + " uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        mBackupHandler.post(new Runnable() {
+            public void run() {
+                dataChangedImpl(packageName, targets);
+            }
+        });
+    }
+
+    /** Run an initialize operation for the given transport. */
+    public void initializeTransports(String[] transportNames, IBackupObserver observer) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                "initializeTransport");
+        Slog.v(TAG, "initializeTransport(): " + Arrays.asList(transportNames));
+
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            mWakelock.acquire();
+            OnTaskFinishedListener listener = caller -> mWakelock.release();
+            mBackupHandler.post(
+                    new PerformInitializeTask(this, transportNames, observer, listener));
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** Clear the given package's backup data from the current transport. */
+    public void clearBackupData(String transportName, String packageName) {
+        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
+        PackageInfo info;
+        try {
+            info = mPackageManager.getPackageInfo(packageName,
+                    PackageManager.GET_SIGNING_CERTIFICATES);
+        } catch (NameNotFoundException e) {
+            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
+            return;
+        }
+
+        // If the caller does not hold the BACKUP permission, it can only request a
+        // wipe of its own backed-up data.
+        Set<String> apps;
+        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
+                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+            apps = mBackupParticipants.get(Binder.getCallingUid());
+        } else {
+            // a caller with full permission can ask to back up any participating app
+            // !!! TODO: allow data-clear of ANY app?
+            if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
+            apps = mProcessedPackagesJournal.getPackagesCopy();
+        }
+
+        if (apps.contains(packageName)) {
+            // found it; fire off the clear request
+            if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
+            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
+            synchronized (mQueueLock) {
+                TransportClient transportClient =
+                        mTransportManager
+                                .getTransportClient(transportName, "BMS.clearBackupData()");
+                if (transportClient == null) {
+                    // transport is currently unregistered -- make sure to retry
+                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
+                            new ClearRetryParams(transportName, packageName));
+                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
+                    return;
+                }
+                long oldId = Binder.clearCallingIdentity();
+                OnTaskFinishedListener listener =
+                        caller ->
+                                mTransportManager.disposeOfTransportClient(transportClient, caller);
+                mWakelock.acquire();
+                Message msg = mBackupHandler.obtainMessage(
+                        MSG_RUN_CLEAR,
+                        new ClearParams(transportClient, info, listener));
+                mBackupHandler.sendMessage(msg);
+                Binder.restoreCallingIdentity(oldId);
+            }
+        }
+    }
+
+    /**
+     * Run a backup pass immediately for any applications that have declared that they have pending
+     * updates.
+     */
+    public void backupNow() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
+
+        long oldId = Binder.clearCallingIdentity();
+        try {
+            final PowerSaveState result =
+                    mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
+            if (result.batterySaverEnabled) {
+                if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
+                KeyValueBackupJob.schedule(mContext, mConstants);   // try again in several hours
+            } else {
+                if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
+                synchronized (mQueueLock) {
+                    // Fire the intent that kicks off the whole shebang...
+                    try {
+                        mRunBackupIntent.send();
+                    } catch (PendingIntent.CanceledException e) {
+                        // should never happen
+                        Slog.e(TAG, "run-backup intent cancelled!");
+                    }
+
+                    // ...and cancel any pending scheduled job, because we've just superseded it
+                    KeyValueBackupJob.cancel(mContext);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** Returns {@code true} if the system user has gone through SUW. */
+    public boolean deviceIsProvisioned() {
+        final ContentResolver resolver = mContext.getContentResolver();
+        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
+    }
+
+    /**
+     * Used by 'adb backup' to run a backup pass for packages supplied via the command line, writing
+     * the resulting data stream to the supplied {@code fd}. This method is synchronous and does not
+     * return to the caller until the backup has been completed. It requires on-screen confirmation
+     * by the user.
+     */
+    public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
+            boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
+            boolean compress, boolean doKeyValue, String[] pkgList) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");
+
+        final int callingUserHandle = UserHandle.getCallingUserId();
+        // TODO: http://b/22388012
+        if (callingUserHandle != UserHandle.USER_SYSTEM) {
+            throw new IllegalStateException("Backup supported only for the device owner");
+        }
+
+        // Validate
+        if (!doAllApps) {
+            if (!includeShared) {
+                // If we're backing up shared data (sdcard or equivalent), then we can run
+                // without any supplied app names.  Otherwise, we'd be doing no work, so
+                // report the error.
+                if (pkgList == null || pkgList.length == 0) {
+                    throw new IllegalArgumentException(
+                            "Backup requested but neither shared nor any apps named");
+                }
+            }
+        }
+
+        long oldId = Binder.clearCallingIdentity();
+        try {
+            // Doesn't make sense to do a full backup prior to setup
+            if (!deviceIsProvisioned()) {
+                Slog.i(TAG, "Backup not supported before setup");
+                return;
+            }
+
+            if (DEBUG) {
+                Slog.v(TAG, "Requesting backup: apks=" + includeApks + " obb=" + includeObbs
+                        + " shared=" + includeShared + " all=" + doAllApps + " system="
+                        + includeSystem + " includekeyvalue=" + doKeyValue + " pkgs=" + pkgList);
+            }
+            Slog.i(TAG, "Beginning adb backup...");
+
+            AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
+                    includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
+                    pkgList);
+            final int token = generateRandomIntegerToken();
+            synchronized (mAdbBackupRestoreConfirmations) {
+                mAdbBackupRestoreConfirmations.put(token, params);
+            }
+
+            // start up the confirmation UI
+            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
+            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
+                Slog.e(TAG, "Unable to launch backup confirmation UI");
+                mAdbBackupRestoreConfirmations.delete(token);
+                return;
+            }
+
+            // make sure the screen is lit for the user interaction
+            mPowerManager.userActivity(SystemClock.uptimeMillis(),
+                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
+                    0);
+
+            // start the confirmation countdown
+            startConfirmationTimeout(token, params);
+
+            // wait for the backup to be performed
+            if (DEBUG) Slog.d(TAG, "Waiting for backup completion...");
+            waitForCompletion(params);
+        } finally {
+            try {
+                fd.close();
+            } catch (IOException e) {
+                Slog.e(TAG, "IO error closing output for adb backup: " + e.getMessage());
+            }
+            Binder.restoreCallingIdentity(oldId);
+            Slog.d(TAG, "Adb backup processing complete.");
+        }
+    }
+
+    /** Run a full backup pass for the given packages. Used by 'adb shell bmgr'. */
+    public void fullTransportBackup(String[] pkgNames) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                "fullTransportBackup");
+
+        final int callingUserHandle = UserHandle.getCallingUserId();
+        // TODO: http://b/22388012
+        if (callingUserHandle != UserHandle.USER_SYSTEM) {
+            throw new IllegalStateException("Restore supported only for the device owner");
+        }
+
+        String transportName = mTransportManager.getCurrentTransportName();
+        if (!fullBackupAllowable(transportName)) {
+            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
+        } else {
+            if (DEBUG) {
+                Slog.d(TAG, "fullTransportBackup()");
+            }
+
+            final long oldId = Binder.clearCallingIdentity();
+            try {
+                CountDownLatch latch = new CountDownLatch(1);
+                Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport(
+                        this,
+                        /* observer */ null,
+                        pkgNames,
+                        /* updateSchedule */ false,
+                        /* runningJob */ null,
+                        latch,
+                        /* backupObserver */ null,
+                        /* monitor */ null,
+                        /* userInitiated */ false,
+                        "BMS.fullTransportBackup()");
+                // Acquiring wakelock for PerformFullTransportBackupTask before its start.
+                mWakelock.acquire();
+                (new Thread(task, "full-transport-master")).start();
+                do {
+                    try {
+                        latch.await();
+                        break;
+                    } catch (InterruptedException e) {
+                        // Just go back to waiting for the latch to indicate completion
+                    }
+                } while (true);
+
+                // We just ran a backup on these packages, so kick them to the end of the queue
+                final long now = System.currentTimeMillis();
+                for (String pkg : pkgNames) {
+                    enqueueFullBackup(pkg, now);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(oldId);
+            }
+        }
+
+        if (DEBUG) {
+            Slog.d(TAG, "Done with full transport backup.");
+        }
+    }
+
+    /**
+     * Used by 'adb restore' to run a restore pass, blocking until completion. Requires user
+     * confirmation.
+     */
+    public void adbRestore(ParcelFileDescriptor fd) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore");
+
+        final int callingUserHandle = UserHandle.getCallingUserId();
+        // TODO: http://b/22388012
+        if (callingUserHandle != UserHandle.USER_SYSTEM) {
+            throw new IllegalStateException("Restore supported only for the device owner");
+        }
+
+        long oldId = Binder.clearCallingIdentity();
+
+        try {
+            // Check whether the device has been provisioned -- we don't handle
+            // full restores prior to completing the setup process.
+            if (!deviceIsProvisioned()) {
+                Slog.i(TAG, "Full restore not permitted before setup");
+                return;
+            }
+
+            Slog.i(TAG, "Beginning restore...");
+
+            AdbRestoreParams params = new AdbRestoreParams(fd);
+            final int token = generateRandomIntegerToken();
+            synchronized (mAdbBackupRestoreConfirmations) {
+                mAdbBackupRestoreConfirmations.put(token, params);
+            }
+
+            // start up the confirmation UI
+            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
+            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
+                Slog.e(TAG, "Unable to launch restore confirmation");
+                mAdbBackupRestoreConfirmations.delete(token);
+                return;
+            }
+
+            // make sure the screen is lit for the user interaction
+            mPowerManager.userActivity(SystemClock.uptimeMillis(),
+                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
+                    0);
+
+            // start the confirmation countdown
+            startConfirmationTimeout(token, params);
+
+            // wait for the restore to be performed
+            if (DEBUG) Slog.d(TAG, "Waiting for restore completion...");
+            waitForCompletion(params);
+        } finally {
+            try {
+                fd.close();
+            } catch (IOException e) {
+                Slog.w(TAG, "Error trying to close fd after adb restore: " + e);
+            }
+            Binder.restoreCallingIdentity(oldId);
+            Slog.i(TAG, "adb restore processing complete.");
+        }
+    }
+
+    private boolean startConfirmationUi(int token, String action) {
+        try {
+            Intent confIntent = new Intent(action);
+            confIntent.setClassName("com.android.backupconfirm",
+                    "com.android.backupconfirm.BackupRestoreConfirmation");
+            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
+            confIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+            mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
+        } catch (ActivityNotFoundException e) {
+            return false;
+        }
+        return true;
+    }
+
+    private void startConfirmationTimeout(int token, AdbParams params) {
+        if (MORE_DEBUG) {
+            Slog.d(TAG, "Posting conf timeout msg after "
+                    + TIMEOUT_FULL_CONFIRMATION + " millis");
+        }
+        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
+                token, 0, params);
+        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
+    }
+
+    private void waitForCompletion(AdbParams params) {
+        synchronized (params.latch) {
+            while (!params.latch.get()) {
+                try {
+                    params.latch.wait();
+                } catch (InterruptedException e) { /* never interrupted */ }
+            }
+        }
+    }
+
+    /** Called when adb backup/restore has completed. */
+    public void signalAdbBackupRestoreCompletion(AdbParams params) {
+        synchronized (params.latch) {
+            params.latch.set(true);
+            params.latch.notifyAll();
+        }
+    }
+
+    /**
+     * Confirm that the previously-requested full backup/restore operation can proceed. This is used
+     * to require a user-facing disclosure about the operation.
+     */
+    public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
+            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
+        if (DEBUG) {
+            Slog.d(TAG, "acknowledgeAdbBackupOrRestore : token=" + token
+                    + " allow=" + allow);
+        }
+
+        // TODO: possibly require not just this signature-only permission, but even
+        // require that the specific designated confirmation-UI app uid is the caller?
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                "acknowledgeAdbBackupOrRestore");
+
+        long oldId = Binder.clearCallingIdentity();
+        try {
+
+            AdbParams params;
+            synchronized (mAdbBackupRestoreConfirmations) {
+                params = mAdbBackupRestoreConfirmations.get(token);
+                if (params != null) {
+                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
+                    mAdbBackupRestoreConfirmations.delete(token);
+
+                    if (allow) {
+                        final int verb = params instanceof AdbBackupParams
+                                ? MSG_RUN_ADB_BACKUP
+                                : MSG_RUN_ADB_RESTORE;
+
+                        params.observer = observer;
+                        params.curPassword = curPassword;
+
+                        params.encryptPassword = encPpassword;
+
+                        if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
+                        mWakelock.acquire();
+                        Message msg = mBackupHandler.obtainMessage(verb, params);
+                        mBackupHandler.sendMessage(msg);
+                    } else {
+                        Slog.w(TAG, "User rejected full backup/restore operation");
+                        // indicate completion without having actually transferred any data
+                        signalAdbBackupRestoreCompletion(params);
+                    }
+                } else {
+                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** User-configurable enabling/disabling of backups. */
+    public void setBackupEnabled(boolean enable) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "setBackupEnabled");
+
+        Slog.i(TAG, "Backup enabled => " + enable);
+
+        long oldId = Binder.clearCallingIdentity();
+        try {
+            boolean wasEnabled = mEnabled;
+            synchronized (this) {
+                // TODO(b/118520567): Clean up writing backup enabled logic.
+                BackupManagerService.writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
+                mEnabled = enable;
+            }
+
+            synchronized (mQueueLock) {
+                if (enable && !wasEnabled && mProvisioned) {
+                    // if we've just been enabled, start scheduling backup passes
+                    KeyValueBackupJob.schedule(mContext, mConstants);
+                    scheduleNextFullBackupJob(0);
+                } else if (!enable) {
+                    // No longer enabled, so stop running backups
+                    if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
+
+                    KeyValueBackupJob.cancel(mContext);
+
+                    // This also constitutes an opt-out, so we wipe any data for
+                    // this device from the backend.  We start that process with
+                    // an alarm in order to guarantee wakelock states.
+                    if (wasEnabled && mProvisioned) {
+                        // NOTE: we currently flush every registered transport, not just
+                        // the currently-active one.
+                        List<String> transportNames = new ArrayList<>();
+                        List<String> transportDirNames = new ArrayList<>();
+                        mTransportManager.forEachRegisteredTransport(
+                                name -> {
+                                    final String dirName;
+                                    try {
+                                        dirName =
+                                                mTransportManager
+                                                        .getTransportDirName(name);
+                                    } catch (TransportNotRegisteredException e) {
+                                        // Should never happen
+                                        Slog.e(TAG, "Unexpected unregistered transport", e);
+                                        return;
+                                    }
+                                    transportNames.add(name);
+                                    transportDirNames.add(dirName);
+                                });
+
+                        // build the set of transports for which we are posting an init
+                        for (int i = 0; i < transportNames.size(); i++) {
+                            recordInitPending(
+                                    true,
+                                    transportNames.get(i),
+                                    transportDirNames.get(i));
+                        }
+                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
+                                mRunInitIntent);
+                    }
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** Enable/disable automatic restore of app data at install time. */
+    public void setAutoRestore(boolean doAutoRestore) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "setAutoRestore");
+
+        Slog.i(TAG, "Auto restore => " + doAutoRestore);
+
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                Settings.Secure.putInt(mContext.getContentResolver(),
+                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
+                mAutoRestore = doAutoRestore;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** Mark the backup service as having been provisioned. */
+    public void setBackupProvisioned(boolean available) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "setBackupProvisioned");
+        /*
+         * This is now a no-op; provisioning is simply the device's own setup state.
+         */
+    }
+
+    /** Report whether the backup mechanism is currently enabled. */
+    public boolean isBackupEnabled() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "isBackupEnabled");
+        return mEnabled;    // no need to synchronize just to read it
+    }
+
+    /** Report the name of the currently active transport. */
+    public String getCurrentTransport() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "getCurrentTransport");
+        String currentTransport = mTransportManager.getCurrentTransportName();
+        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
+        return currentTransport;
+    }
+
+    /**
+     * Returns the {@link ComponentName} of the host service of the selected transport or {@code
+     * null} if no transport selected or if the transport selected is not registered.
+     */
+    @Nullable
+    public ComponentName getCurrentTransportComponent() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "getCurrentTransportComponent");
+        long oldId = Binder.clearCallingIdentity();
+        try {
+            return mTransportManager.getCurrentTransportComponent();
+        } catch (TransportNotRegisteredException e) {
+            return null;
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** Report all known, available backup transports by name. */
+    public String[] listAllTransports() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "listAllTransports");
+
+        return mTransportManager.getRegisteredTransportNames();
+    }
+
+    /** Report all known, available backup transports by component. */
+    public ComponentName[] listAllTransportComponents() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "listAllTransportComponents");
+        return mTransportManager.getRegisteredTransportComponents();
+    }
+
+    /** Report all system whitelisted transports. */
+    public String[] getTransportWhitelist() {
+        // No permission check, intentionally.
+        Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
+        String[] whitelistedTransports = new String[whitelistedComponents.size()];
+        int i = 0;
+        for (ComponentName component : whitelistedComponents) {
+            whitelistedTransports[i] = component.flattenToShortString();
+            i++;
+        }
+        return whitelistedTransports;
+    }
+
+    /**
+     * Update the attributes of the transport identified by {@code transportComponent}. If the
+     * specified transport has not been bound at least once (for registration), this call will be
+     * ignored. Only the host process of the transport can change its description, otherwise a
+     * {@link SecurityException} will be thrown.
+     *
+     * @param transportComponent The identity of the transport being described.
+     * @param name A {@link String} with the new name for the transport. This is NOT for
+     *     identification. MUST NOT be {@code null}.
+     * @param configurationIntent An {@link Intent} that can be passed to
+     *     {@link Context#startActivity} in order to launch the transport's configuration UI. It may
+     *     be {@code null} if the transport does not offer any user-facing configuration UI.
+     * @param currentDestinationString A {@link String} describing the destination to which the
+     *     transport is currently sending data. MUST NOT be {@code null}.
+     * @param dataManagementIntent An {@link Intent} that can be passed to
+     *     {@link Context#startActivity} in order to launch the transport's data-management UI. It
+     *     may be {@code null} if the transport does not offer any user-facing data
+     *     management UI.
+     * @param dataManagementLabel A {@link String} to be used as the label for the transport's data
+     *     management affordance. This MUST be {@code null} when dataManagementIntent is
+     *     {@code null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
+     * @throws SecurityException If the UID of the calling process differs from the package UID of
+     *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
+     */
+    public void updateTransportAttributes(
+            ComponentName transportComponent,
+            String name,
+            @Nullable Intent configurationIntent,
+            String currentDestinationString,
+            @Nullable Intent dataManagementIntent,
+            @Nullable String dataManagementLabel) {
+        updateTransportAttributes(
+                Binder.getCallingUid(),
+                transportComponent,
+                name,
+                configurationIntent,
+                currentDestinationString,
+                dataManagementIntent,
+                dataManagementLabel);
+    }
+
+    @VisibleForTesting
+    void updateTransportAttributes(
+            int callingUid,
+            ComponentName transportComponent,
+            String name,
+            @Nullable Intent configurationIntent,
+            String currentDestinationString,
+            @Nullable Intent dataManagementIntent,
+            @Nullable String dataManagementLabel) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "updateTransportAttributes");
+
+        Preconditions.checkNotNull(transportComponent, "transportComponent can't be null");
+        Preconditions.checkNotNull(name, "name can't be null");
+        Preconditions.checkNotNull(
+                currentDestinationString, "currentDestinationString can't be null");
+        Preconditions.checkArgument(
+                (dataManagementIntent == null) == (dataManagementLabel == null),
+                "dataManagementLabel should be null iff dataManagementIntent is null");
+
+        try {
+            int transportUid =
+                    mContext.getPackageManager()
+                            .getPackageUid(transportComponent.getPackageName(), 0);
+            if (callingUid != transportUid) {
+                throw new SecurityException("Only the transport can change its description");
+            }
+        } catch (NameNotFoundException e) {
+            throw new SecurityException("Transport package not found", e);
+        }
+
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            mTransportManager.updateTransportAttributes(
+                    transportComponent,
+                    name,
+                    configurationIntent,
+                    currentDestinationString,
+                    dataManagementIntent,
+                    dataManagementLabel);
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /**
+     * Selects transport {@code transportName} and returns previously selected transport.
+     *
+     * @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
+     * ISelectBackupTransportCallback)} instead.
+     */
+    @Deprecated
+    @Nullable
+    public String selectBackupTransport(String transportName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "selectBackupTransport");
+
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            String previousTransportName = mTransportManager.selectTransport(transportName);
+            updateStateForTransport(transportName);
+            Slog.v(TAG, "selectBackupTransport(transport = " + transportName
+                    + "): previous transport = " + previousTransportName);
+            return previousTransportName;
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /**
+     * Selects transport {@code transportComponent} asynchronously and notifies {@code listener}
+     * with the result upon completion.
+     */
+    public void selectBackupTransportAsync(
+            ComponentName transportComponent, ISelectBackupTransportCallback listener) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
+
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            String transportString = transportComponent.flattenToShortString();
+            Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")");
+            mBackupHandler.post(
+                    () -> {
+                        String transportName = null;
+                        int result =
+                                mTransportManager.registerAndSelectTransport(transportComponent);
+                        if (result == BackupManager.SUCCESS) {
+                            try {
+                                transportName =
+                                        mTransportManager.getTransportName(transportComponent);
+                                updateStateForTransport(transportName);
+                            } catch (TransportNotRegisteredException e) {
+                                Slog.e(TAG, "Transport got unregistered");
+                                result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
+                            }
+                        }
+
+                        try {
+                            if (transportName != null) {
+                                listener.onSuccess(transportName);
+                            } else {
+                                listener.onFailure(result);
+                            }
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
+                        }
+                    });
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    private void updateStateForTransport(String newTransportName) {
+        // Publish the name change
+        Settings.Secure.putString(mContext.getContentResolver(),
+                Settings.Secure.BACKUP_TRANSPORT, newTransportName);
+
+        // And update our current-dataset bookkeeping
+        String callerLogString = "BMS.updateStateForTransport()";
+        TransportClient transportClient =
+                mTransportManager.getTransportClient(newTransportName, callerLogString);
+        if (transportClient != null) {
+            try {
+                IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
+                mCurrentToken = transport.getCurrentRestoreSet();
+            } catch (Exception e) {
+                // Oops.  We can't know the current dataset token, so reset and figure it out
+                // when we do the next k/v backup operation on this transport.
+                mCurrentToken = 0;
+                Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
+            }
+            mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+        } else {
+            Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
+            // The named transport isn't registered, so we can't know what its current dataset token
+            // is. Reset as above.
+            mCurrentToken = 0;
+        }
+    }
+
+    /**
+     * Supply the configuration intent for the given transport. If the name is not one of the
+     * available transports, or if the transport does not supply any configuration UI, the method
+     * returns {@code null}.
+     */
+    public Intent getConfigurationIntent(String transportName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "getConfigurationIntent");
+        try {
+            Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
+            }
+            return intent;
+        } catch (TransportNotRegisteredException e) {
+            Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * Supply the current destination string for the given transport. If the name is not one of the
+     * registered transports the method will return null.
+     *
+     * <p>This string is used VERBATIM as the summary text of the relevant Settings item.
+     *
+     * @param transportName The name of the registered transport.
+     * @return The current destination string or null if the transport is not registered.
+     */
+    public String getDestinationString(String transportName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "getDestinationString");
+
+        try {
+            String string = mTransportManager.getTransportCurrentDestinationString(transportName);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "getDestinationString() returning " + string);
+            }
+            return string;
+        } catch (TransportNotRegisteredException e) {
+            Slog.e(TAG, "Unable to get destination string from transport: " + e.getMessage());
+            return null;
+        }
+    }
+
+    /** Supply the manage-data intent for the given transport. */
+    public Intent getDataManagementIntent(String transportName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "getDataManagementIntent");
+
+        try {
+            Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
+            }
+            return intent;
+        } catch (TransportNotRegisteredException e) {
+            Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * Supply the menu label for affordances that fire the manage-data intent for the given
+     * transport.
+     */
+    public String getDataManagementLabel(String transportName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "getDataManagementLabel");
+
+        try {
+            String label = mTransportManager.getTransportDataManagementLabel(transportName);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "getDataManagementLabel() returning " + label);
+            }
+            return label;
+        } catch (TransportNotRegisteredException e) {
+            Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * Callback: a requested backup agent has been instantiated. This should only be called from the
+     * {@link ActivityManager}.
+     */
+    public void agentConnected(String packageName, IBinder agentBinder) {
+        synchronized (mAgentConnectLock) {
+            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
+                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
+                mConnectedAgent = agent;
+                mConnecting = false;
+            } else {
+                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+                        + " claiming agent connected");
+            }
+            mAgentConnectLock.notifyAll();
+        }
+    }
+
+    /**
+     * Callback: a backup agent has failed to come up, or has unexpectedly quit. If the agent failed
+     * to come up in the first place, the agentBinder argument will be {@code null}. This should
+     * only be called from the {@link ActivityManager}.
+     */
+    public void agentDisconnected(String packageName) {
+        // TODO: handle backup being interrupted
+        synchronized (mAgentConnectLock) {
+            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+                mConnectedAgent = null;
+                mConnecting = false;
+            } else {
+                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+                        + " claiming agent disconnected");
+            }
+            mAgentConnectLock.notifyAll();
+        }
+    }
+
+    /**
+     * An application being installed will need a restore pass, then the {@link PackageManager} will
+     * need to be told when the restore is finished.
+     */
+    public void restoreAtInstall(String packageName, int token) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+                    + " attemping install-time restore");
+            return;
+        }
+
+        boolean skip = false;
+
+        long restoreSet = getAvailableRestoreToken(packageName);
+        if (DEBUG) {
+            Slog.v(TAG, "restoreAtInstall pkg=" + packageName
+                    + " token=" + Integer.toHexString(token)
+                    + " restoreSet=" + Long.toHexString(restoreSet));
+        }
+        if (restoreSet == 0) {
+            if (MORE_DEBUG) Slog.i(TAG, "No restore set");
+            skip = true;
+        }
+
+        TransportClient transportClient =
+                mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
+        if (transportClient == null) {
+            if (DEBUG) Slog.w(TAG, "No transport client");
+            skip = true;
+        }
+
+        if (!mAutoRestore) {
+            if (DEBUG) {
+                Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
+            }
+            skip = true;
+        }
+
+        if (!skip) {
+            try {
+                // okay, we're going to attempt a restore of this package from this restore set.
+                // The eventual message back into the Package Manager to run the post-install
+                // steps for 'token' will be issued from the restore handling code.
+
+                mWakelock.acquire();
+
+                OnTaskFinishedListener listener = caller -> {
+                    mTransportManager.disposeOfTransportClient(transportClient, caller);
+                    mWakelock.release();
+                };
+
+                if (MORE_DEBUG) {
+                    Slog.d(TAG, "Restore at install of " + packageName);
+                }
+                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+                msg.obj =
+                        RestoreParams.createForRestoreAtInstall(
+                                transportClient,
+                                /* observer */ null,
+                                /* monitor */ null,
+                                restoreSet,
+                                packageName,
+                                token,
+                                listener);
+                mBackupHandler.sendMessage(msg);
+            } catch (Exception e) {
+                // Calling into the transport broke; back off and proceed with the installation.
+                Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
+                skip = true;
+            }
+        }
+
+        if (skip) {
+            // Auto-restore disabled or no way to attempt a restore
+
+            if (transportClient != null) {
+                mTransportManager.disposeOfTransportClient(
+                        transportClient, "BMS.restoreAtInstall()");
+            }
+
+            // Tell the PackageManager to proceed with the post-install handling for this package.
+            if (DEBUG) Slog.v(TAG, "Finishing install immediately");
+            try {
+                mPackageManagerBinder.finishPackageInstall(token, false);
+            } catch (RemoteException e) { /* can't happen */ }
+        }
+    }
+
+    /** Hand off a restore session. */
+    public IRestoreSession beginRestoreSession(String packageName, String transport) {
+        if (DEBUG) {
+            Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
+                    + " transport=" + transport);
+        }
+
+        boolean needPermission = true;
+        if (transport == null) {
+            transport = mTransportManager.getCurrentTransportName();
+
+            if (packageName != null) {
+                PackageInfo app = null;
+                try {
+                    app = mPackageManager.getPackageInfo(packageName, 0);
+                } catch (NameNotFoundException nnf) {
+                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
+                    throw new IllegalArgumentException("Package " + packageName + " not found");
+                }
+
+                if (app.applicationInfo.uid == Binder.getCallingUid()) {
+                    // So: using the current active transport, and the caller has asked
+                    // that its own package will be restored.  In this narrow use case
+                    // we do not require the caller to hold the permission.
+                    needPermission = false;
+                }
+            }
+        }
+
+        if (needPermission) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                    "beginRestoreSession");
+        } else {
+            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
+        }
+
+        synchronized (this) {
+            if (mActiveRestoreSession != null) {
+                Slog.i(TAG, "Restore session requested but one already active");
+                return null;
+            }
+            if (mBackupRunning) {
+                Slog.i(TAG, "Restore session requested but currently running backups");
+                return null;
+            }
+            mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
+            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
+                    mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
+        }
+        return mActiveRestoreSession;
+    }
+
+    /** Clear the specified restore session. */
+    public void clearRestoreSession(ActiveRestoreSession currentSession) {
+        synchronized (this) {
+            if (currentSession != mActiveRestoreSession) {
+                Slog.e(TAG, "ending non-current restore session");
+            } else {
+                if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
+                mActiveRestoreSession = null;
+                mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
+            }
+        }
+    }
+
+    /**
+     * Note that a currently-active backup agent has notified us that it has completed the given
+     * outstanding asynchronous backup/restore operation.
+     */
+    public void opComplete(int token, long result) {
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
+        }
+        Operation op = null;
+        synchronized (mCurrentOpLock) {
+            op = mCurrentOperations.get(token);
+            if (op != null) {
+                if (op.state == OP_TIMEOUT) {
+                    // The operation already timed out, and this is a late response.  Tidy up
+                    // and ignore it; we've already dealt with the timeout.
+                    op = null;
+                    mCurrentOperations.delete(token);
+                } else if (op.state == OP_ACKNOWLEDGED) {
+                    if (DEBUG) {
+                        Slog.w(TAG, "Received duplicate ack for token="
+                                + Integer.toHexString(token));
+                    }
+                    op = null;
+                    mCurrentOperations.remove(token);
+                } else if (op.state == OP_PENDING) {
+                    // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
+                    // called after we we receive this call.
+                    op.state = OP_ACKNOWLEDGED;
+                }
+            }
+            mCurrentOpLock.notifyAll();
+        }
+
+        // The completion callback, if any, is invoked on the handler
+        if (op != null && op.callback != null) {
+            Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
+            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
+            mBackupHandler.sendMessage(msg);
+        }
+    }
+
+    /** Checks if the package is eligible for backup. */
+    public boolean isAppEligibleForBackup(String packageName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
+
+        long oldToken = Binder.clearCallingIdentity();
+        try {
+            String callerLogString = "BMS.isAppEligibleForBackup";
+            TransportClient transportClient =
+                    mTransportManager.getCurrentTransportClient(callerLogString);
+            boolean eligible =
+                    AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
+                            transportClient, packageName, mPackageManager);
+            if (transportClient != null) {
+                mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+            }
+            return eligible;
+        } finally {
+            Binder.restoreCallingIdentity(oldToken);
+        }
+    }
+
+    /** Returns the inputted packages that are eligible for backup. */
+    public String[] filterAppsEligibleForBackup(String[] packages) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
+
+        long oldToken = Binder.clearCallingIdentity();
+        try {
+            String callerLogString = "BMS.filterAppsEligibleForBackup";
+            TransportClient transportClient =
+                    mTransportManager.getCurrentTransportClient(callerLogString);
+            List<String> eligibleApps = new LinkedList<>();
+            for (String packageName : packages) {
+                if (AppBackupUtils
+                        .appIsRunningAndEligibleForBackupWithTransport(
+                                transportClient, packageName, mPackageManager)) {
+                    eligibleApps.add(packageName);
+                }
+            }
+            if (transportClient != null) {
+                mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+            }
+            return eligibleApps.toArray(new String[eligibleApps.size()]);
+        } finally {
+            Binder.restoreCallingIdentity(oldToken);
+        }
+    }
+
+    /** Prints service state for 'dumpsys backup'. */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+
+        long identityToken = Binder.clearCallingIdentity();
+        try {
+            if (args != null) {
+                for (String arg : args) {
+                    if ("-h".equals(arg)) {
+                        pw.println("'dumpsys backup' optional arguments:");
+                        pw.println("  -h       : this help text");
+                        pw.println("  a[gents] : dump information about defined backup agents");
+                        return;
+                    } else if ("agents".startsWith(arg)) {
+                        dumpAgents(pw);
+                        return;
+                    } else if ("transportclients".equals(arg.toLowerCase())) {
+                        mTransportManager.dumpTransportClients(pw);
+                        return;
+                    } else if ("transportstats".equals(arg.toLowerCase())) {
+                        mTransportManager.dumpTransportStats(pw);
+                        return;
+                    }
+                }
+            }
+            dumpInternal(pw);
+        } finally {
+            Binder.restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private void dumpAgents(PrintWriter pw) {
+        List<PackageInfo> agentPackages = allAgentPackages();
+        pw.println("Defined backup agents:");
+        for (PackageInfo pkg : agentPackages) {
+            pw.print("  ");
+            pw.print(pkg.packageName);
+            pw.println(':');
+            pw.print("      ");
+            pw.println(pkg.applicationInfo.backupAgentName);
+        }
+    }
+
+    private void dumpInternal(PrintWriter pw) {
+        synchronized (mQueueLock) {
+            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
+                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
+            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
+            if (mBackupRunning) pw.println("Backup currently running");
+            pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
+            pw.println("Last backup pass started: " + mLastBackupPass
+                    + " (now = " + System.currentTimeMillis() + ')');
+            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
+
+            pw.println("Transport whitelist:");
+            for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
+                pw.print("    ");
+                pw.println(transport.flattenToShortString());
+            }
+
+            pw.println("Available transports:");
+            final String[] transports = listAllTransports();
+            if (transports != null) {
+                for (String t : transports) {
+                    pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? "  * "
+                            : "    ") + t);
+                    try {
+                        File dir = new File(mBaseStateDir,
+                                mTransportManager.getTransportDirName(t));
+                        pw.println("       destination: "
+                                + mTransportManager.getTransportCurrentDestinationString(t));
+                        pw.println("       intent: "
+                                + mTransportManager.getTransportConfigurationIntent(t));
+                        for (File f : dir.listFiles()) {
+                            pw.println(
+                                    "       " + f.getName() + " - " + f.length() + " state bytes");
+                        }
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Error in transport", e);
+                        pw.println("        Error: " + e);
+                    }
+                }
+            }
+
+            mTransportManager.dumpTransportClients(pw);
+
+            pw.println("Pending init: " + mPendingInits.size());
+            for (String s : mPendingInits) {
+                pw.println("    " + s);
+            }
+
+            pw.print("Ancestral: ");
+            pw.println(Long.toHexString(mAncestralToken));
+            pw.print("Current:   ");
+            pw.println(Long.toHexString(mCurrentToken));
+
+            int numPackages = mBackupParticipants.size();
+            pw.println("Participants:");
+            for (int i = 0; i < numPackages; i++) {
+                int uid = mBackupParticipants.keyAt(i);
+                pw.print("  uid: ");
+                pw.println(uid);
+                HashSet<String> participants = mBackupParticipants.valueAt(i);
+                for (String app : participants) {
+                    pw.println("    " + app);
+                }
+            }
+
+            pw.println("Ancestral packages: "
+                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
+            if (mAncestralPackages != null) {
+                for (String pkg : mAncestralPackages) {
+                    pw.println("    " + pkg);
+                }
+            }
+
+            Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy();
+            pw.println("Ever backed up: " + processedPackages.size());
+            for (String pkg : processedPackages) {
+                pw.println("    " + pkg);
+            }
+
+            pw.println("Pending key/value backup: " + mPendingBackups.size());
+            for (BackupRequest req : mPendingBackups.values()) {
+                pw.println("    " + req);
+            }
+
+            pw.println("Full backup queue:" + mFullBackupQueue.size());
+            for (FullBackupEntry entry : mFullBackupQueue) {
+                pw.print("    ");
+                pw.print(entry.lastBackup);
+                pw.print(" : ");
+                pw.println(entry.packageName);
+            }
+        }
+    }
+
+
+    public IBackupManager getBackupManagerBinder() {
+        return mBackupManagerBinder;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
index 94365d7..bace1aa 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
@@ -1,10 +1,10 @@
 package com.android.server.backup.fullbackup;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
 
 import android.annotation.Nullable;
 import android.app.backup.FullBackup;
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index c9f7218..5e92339 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -16,13 +16,13 @@
 
 package com.android.server.backup.fullbackup;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
@@ -39,8 +39,8 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.remote.RemoteCall;
 import com.android.server.backup.utils.FullBackupUtils;
 
@@ -53,7 +53,7 @@
  * and emitting it to the designated OutputStream.
  */
 public class FullBackupEngine {
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     OutputStream mOutput;
     FullBackupPreflight mPreflightHook;
     BackupRestoreTask mTimeoutMonitor;
@@ -164,24 +164,21 @@
         }
 
         /**
-         * Don't write apks for forward-locked apps or system-bundled apps that are not upgraded.
+         * Don't write apks for system-bundled apps that are not upgraded.
          */
         private boolean shouldWriteApk(
                 ApplicationInfo applicationInfo, boolean includeApks, boolean isSharedStorage) {
-            boolean isForwardLocked =
-                    (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
             boolean isSystemApp = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
             boolean isUpdatedSystemApp =
                     (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
             return includeApks
                     && !isSharedStorage
-                    && !isForwardLocked
                     && (!isSystemApp || isUpdatedSystemApp);
         }
     }
 
     public FullBackupEngine(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             OutputStream output,
             FullBackupPreflight preflightHook,
             PackageInfo pkg,
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index bc7d9fc..e142537 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -17,8 +17,8 @@
 package com.android.server.backup.fullbackup;
 
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
 
 import android.app.backup.IBackupManager;
 import android.content.ComponentName;
@@ -34,7 +34,7 @@
 import com.android.internal.backup.IObbBackupService;
 import com.android.internal.util.Preconditions;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.utils.FullBackupUtils;
 
 import java.io.IOException;
@@ -45,11 +45,11 @@
  */
 public class FullBackupObbConnection implements ServiceConnection {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     volatile IObbBackupService mService;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
-    public FullBackupObbConnection(BackupManagerService backupManagerService) {
+    public FullBackupObbConnection(UserBackupManagerService backupManagerService) {
         this.backupManagerService = backupManagerService;
         mService = null;
         mAgentTimeoutParameters = Preconditions.checkNotNull(
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 44edabc..43a80c4 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -16,13 +16,13 @@
 
 package com.android.server.backup.fullbackup;
 
-import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
 import android.app.backup.IFullBackupRestoreObserver;
 import android.content.pm.ApplicationInfo;
@@ -37,7 +37,7 @@
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.KeyValueAdbBackupEngine;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.utils.AppBackupUtils;
 import com.android.server.backup.utils.PasswordUtils;
 
@@ -66,7 +66,7 @@
  */
 public class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     FullBackupEngine mBackupEngine;
     final AtomicBoolean mLatch;
 
@@ -86,7 +86,7 @@
     String mEncryptPassword;
     private final int mCurrentOpToken;
 
-    public PerformAdbBackupTask(BackupManagerService backupManagerService,
+    public PerformAdbBackupTask(UserBackupManagerService backupManagerService,
             ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
             boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
             String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem,
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 755095e..5b449c5 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -19,9 +19,9 @@
 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;
-import static com.android.server.backup.BackupManagerService.OP_PENDING;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
 
 import android.annotation.Nullable;
 import android.app.IBackupAgent;
@@ -45,10 +45,10 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FullBackupJob;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.internal.Operation;
 import com.android.server.backup.remote.RemoteCall;
@@ -97,7 +97,7 @@
  */
 public class PerformFullTransportBackupTask extends FullBackupTask implements BackupRestoreTask {
     public static PerformFullTransportBackupTask newWithCurrentTransport(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             IFullBackupRestoreObserver observer,
             String[] whichPackages,
             boolean updateSchedule,
@@ -128,7 +128,7 @@
 
     private static final String TAG = "PFTBT";
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     private final Object mCancelLock = new Object();
 
     ArrayList<PackageInfo> mPackages;
@@ -150,7 +150,7 @@
     private final int mCurrentOpToken;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
-    public PerformFullTransportBackupTask(BackupManagerService backupManagerService,
+    public PerformFullTransportBackupTask(UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             IFullBackupRestoreObserver observer,
             String[] whichPackages, boolean updateSchedule,
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index f66d8cc..ba153bf 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -35,10 +35,10 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.fullbackup.PerformAdbBackupTask;
 import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
 import com.android.server.backup.keyvalue.BackupRequest;
@@ -84,10 +84,10 @@
     public static final int MSG_BACKUP_RESTORE_STEP = 20;
     public static final int MSG_OP_COMPLETE = 21;
 
-    private final BackupManagerService backupManagerService;
+    private final UserBackupManagerService backupManagerService;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
-    public BackupHandler(BackupManagerService backupManagerService, Looper looper) {
+    public BackupHandler(UserBackupManagerService backupManagerService, Looper looper) {
         super(looper);
         this.backupManagerService = backupManagerService;
         mAgentTimeoutParameters = Preconditions.checkNotNull(
diff --git a/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java b/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
index b0b8037..396f369 100644
--- a/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
@@ -18,13 +18,13 @@
 
 import android.content.pm.IPackageDataObserver;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 public class ClearDataObserver extends IPackageDataObserver.Stub {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
 
-    public ClearDataObserver(BackupManagerService backupManagerService) {
+    public ClearDataObserver(UserBackupManagerService backupManagerService) {
         this.backupManagerService = backupManagerService;
     }
 
diff --git a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
index d028104..5ffa795 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
@@ -22,20 +22,20 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.transport.TransportClient;
 
 import java.io.File;
 
 public class PerformClearTask implements Runnable {
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final TransportManager mTransportManager;
     private final TransportClient mTransportClient;
     private final PackageInfo mPackage;
     private final OnTaskFinishedListener mListener;
 
-    PerformClearTask(BackupManagerService backupManagerService,
+    PerformClearTask(UserBackupManagerService backupManagerService,
             TransportClient transportClient, PackageInfo packageInfo,
             OnTaskFinishedListener listener) {
         mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
index 1ef740d..6b78fbf 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
@@ -30,8 +30,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.EventLogTags;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.transport.TransportClient;
 
 import java.io.File;
@@ -49,7 +49,7 @@
  * operation was successful then it's {@link BackupTransport#TRANSPORT_OK}.
  */
 public class PerformInitializeTask implements Runnable {
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final TransportManager mTransportManager;
     private final String[] mQueue;
     private final File mBaseStateDir;
@@ -57,7 +57,7 @@
     @Nullable private IBackupObserver mObserver;
 
     public PerformInitializeTask(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             String[] transportNames,
             @Nullable IBackupObserver observer,
             OnTaskFinishedListener listener) {
@@ -72,7 +72,7 @@
 
     @VisibleForTesting
     PerformInitializeTask(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportManager transportManager,
             String[] transportNames,
             @Nullable IBackupObserver observer,
diff --git a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
index 69720d4..7e2ac796 100644
--- a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
@@ -23,15 +23,15 @@
 import android.os.Handler;
 import android.util.Slog;
 
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.UserBackupManagerService;
 
 public class ProvisionedObserver extends ContentObserver {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
 
     public ProvisionedObserver(
-            BackupManagerService backupManagerService, Handler handler) {
+            UserBackupManagerService backupManagerService, Handler handler) {
         super(handler);
         this.backupManagerService = backupManagerService;
     }
diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
index 6f574ca..2a5d913 100644
--- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
@@ -18,8 +18,8 @@
 
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.RUN_BACKUP_ACTION;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.RUN_BACKUP_ACTION;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP;
 
 import android.app.PendingIntent;
@@ -29,13 +29,13 @@
 import android.os.Message;
 import android.util.Slog;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 public class RunBackupReceiver extends BroadcastReceiver {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
 
-    public RunBackupReceiver(BackupManagerService backupManagerService) {
+    public RunBackupReceiver(UserBackupManagerService backupManagerService) {
         this.backupManagerService = backupManagerService;
     }
 
diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
index 548c580..38870cb 100644
--- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -17,8 +17,8 @@
 package com.android.server.backup.internal;
 
 import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.RUN_INITIALIZE_ACTION;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.RUN_INITIALIZE_ACTION;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -27,12 +27,12 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 public class RunInitializeReceiver extends BroadcastReceiver {
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
 
-    public RunInitializeReceiver(BackupManagerService backupManagerService) {
+    public RunInitializeReceiver(UserBackupManagerService backupManagerService) {
         mBackupManagerService = backupManagerService;
     }
 
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
index bb8a1d1..535c7cb 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
@@ -30,6 +30,7 @@
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.remote.RemoteResult;
 import com.android.server.backup.utils.BackupManagerMonitorUtils;
 import com.android.server.backup.utils.BackupObserverUtils;
@@ -54,7 +55,7 @@
 public class KeyValueBackupReporter {
     @VisibleForTesting static final String TAG = "KeyValueBackupTask";
     private static final boolean DEBUG = BackupManagerService.DEBUG;
-    @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG || false;
+    @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG;
 
     static void onNewThread(String threadName) {
         if (DEBUG) {
@@ -62,12 +63,12 @@
         }
     }
 
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final IBackupObserver mObserver;
     @Nullable private IBackupManagerMonitor mMonitor;
 
     KeyValueBackupReporter(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             IBackupObserver observer,
             @Nullable IBackupManagerMonitor monitor) {
         mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index d6f2a87..f39d795 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -22,9 +22,9 @@
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
 
-import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
-import static com.android.server.backup.BackupManagerService.OP_PENDING;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
+import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
+import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
@@ -35,7 +35,6 @@
 import android.app.backup.BackupManager;
 import android.app.backup.BackupTransport;
 import android.app.backup.IBackupCallback;
-import android.app.backup.IBackupManager;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
 import android.content.pm.ApplicationInfo;
@@ -55,11 +54,11 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.KeyValueBackupJob;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.internal.Operation;
@@ -95,12 +94,12 @@
  * <p>A few definitions:
  *
  * <ul>
- *   <li>State directory: {@link BackupManagerService#getBaseStateDir()}/&lt;transport&gt;
+ *   <li>State directory: {@link UserBackupManagerService#getBaseStateDir()}/&lt;transport&gt;
  *   <li>State file: {@link
- *       BackupManagerService#getBaseStateDir()}/&lt;transport&gt;/&lt;package&gt;<br>
+ *       UserBackupManagerService#getBaseStateDir()}/&lt;transport&gt;/&lt;package&gt;<br>
  *       Represents the state of the backup data for a specific package in the current dataset.
- *   <li>Stage directory: {@link BackupManagerService#getDataDir()}
- *   <li>Stage file: {@link BackupManagerService#getDataDir()}/&lt;package&gt;.data<br>
+ *   <li>Stage directory: {@link UserBackupManagerService#getDataDir()}
+ *   <li>Stage file: {@link UserBackupManagerService#getDataDir()}/&lt;package&gt;.data<br>
  *       Contains staged data that the agents wrote via {@link BackupDataOutput}, to be transmitted
  *       to the transport.
  * </ul>
@@ -112,7 +111,7 @@
  * of incremental choice. If non-incremental, PM will only be backed-up if specified in the queue,
  * and if it's the case it will be re-positioned at the head of the queue.
  *
- * <p>Before starting, this task will register itself in {@link BackupManagerService} current
+ * <p>Before starting, this task will register itself in {@link UserBackupManagerService} current
  * operations.
  *
  * <p>In summary, this task will for each package:
@@ -121,7 +120,7 @@
  *   <li>Bind to its {@link IBackupAgent}.
  *   <li>Request transport quota and flags.
  *   <li>Call {@link IBackupAgent#doBackup(ParcelFileDescriptor, ParcelFileDescriptor,
- *       ParcelFileDescriptor, long, int, IBackupManager, int)} via {@link RemoteCall} passing the
+ *       ParcelFileDescriptor, long, IBackupCallback, int)} via {@link RemoteCall} passing the
  *       old state file descriptor (read), the backup data file descriptor (write), the new state
  *       file descriptor (write), the quota and the transport flags. This will call {@link
  *       BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)} with
@@ -131,7 +130,7 @@
  *       <ul>
  *         <li>Agent response.
  *         <li>Agent time-out (specified via {@link
- *             BackupManagerService#getAgentTimeoutParameters()}.
+ *             UserBackupManagerService#getAgentTimeoutParameters()}.
  *         <li>External cancellation or thread interrupt.
  *       </ul>
  *   <li>Unbind the agent.
@@ -149,11 +148,11 @@
  *   <li>Mark data-changed for the remaining packages in the queue (skipped packages).
  *   <li>Delete the {@link DataChangedJournal} provided. Note that this should not be the current
  *       journal.
- *   <li>Set {@link BackupManagerService} current token as {@link
+ *   <li>Set {@link UserBackupManagerService} current token as {@link
  *       IBackupTransport#getCurrentRestoreSet()}, if applicable.
  *   <li>Add the transport to the list of transports pending initialization ({@link
- *       BackupManagerService#getPendingInits()}) and kick-off initialization if the transport ever
- *       returned {@link BackupTransport#TRANSPORT_NOT_INITIALIZED}.
+ *       UserBackupManagerService#getPendingInits()}) and kick-off initialization if the transport
+ *       ever returned {@link BackupTransport#TRANSPORT_NOT_INITIALIZED}.
  *   <li>Unregister the task in current operations.
  *   <li>Release the wakelock.
  *   <li>Kick-off {@link PerformFullTransportBackupTask} if a list of full-backup packages was
@@ -174,7 +173,7 @@
     private static final int THREAD_PRIORITY = Process.THREAD_PRIORITY_BACKGROUND;
     private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
     private static final String BLANK_STATE_FILE_NAME = "blank_state";
-    private static final String PM_PACKAGE = BackupManagerService.PACKAGE_MANAGER_SENTINEL;
+    private static final String PM_PACKAGE = UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
     @VisibleForTesting public static final String STAGING_FILE_SUFFIX = ".data";
     @VisibleForTesting public static final String NEW_STATE_FILE_SUFFIX = ".new";
 
@@ -182,7 +181,7 @@
      * Creates a new {@link KeyValueBackupTask} for key-value backup operation, spins up a new
      * dedicated thread and kicks off the operation in it.
      *
-     * @param backupManagerService The {@link BackupManagerService} system service.
+     * @param backupManagerService The {@link UserBackupManagerService} instance.
      * @param transportClient The {@link TransportClient} that contains the transport used for the
      *     operation.
      * @param transportDirName The value of {@link IBackupTransport#transportDirName()} for the
@@ -201,7 +200,7 @@
      * @return The {@link KeyValueBackupTask} that was started.
      */
     public static KeyValueBackupTask start(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             String transportDirName,
             List<String> queue,
@@ -232,7 +231,7 @@
         return task;
     }
 
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final PackageManager mPackageManager;
     private final TransportManager mTransportManager;
     private final TransportClient mTransportClient;
@@ -289,7 +288,7 @@
 
     @VisibleForTesting
     public KeyValueBackupTask(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             String transportDirName,
             List<String> queue,
diff --git a/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
index 28d85a6..bfc97ae 100644
--- a/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
+++ b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
@@ -20,12 +20,12 @@
 import android.app.backup.IBackupManager;
 import android.os.RemoteException;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 /**
  * An implementation of {@link IBackupCallback} that routes the result to {@link
- * BackupManagerService} via {@link IBackupManager#opComplete(int, long)} passing the token provided
- * in the constructor.
+ * UserBackupManagerService} via {@link IBackupManager#opComplete(int, long)} passing the token
+ * provided in the constructor.
  */
 public class ServiceBackupCallback extends IBackupCallback.Stub {
     private final IBackupManager mBackupManager;
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 140dded..e273b32 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -36,8 +36,8 @@
 import android.os.PowerManager;
 import android.util.Slog;
 
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.params.RestoreGetSetsParams;
 import com.android.server.backup.params.RestoreParams;
@@ -53,14 +53,14 @@
 
     private final TransportManager mTransportManager;
     private final String mTransportName;
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     @Nullable private final String mPackageName;
     public RestoreSet[] mRestoreSets = null;
     boolean mEnded = false;
     boolean mTimedOut = false;
 
     public ActiveRestoreSession(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             @Nullable String packageName,
             String transportName) {
         mBackupManagerService = backupManagerService;
@@ -405,10 +405,10 @@
     // Posted to the handler to tear down a restore session in a cleanly synchronized way
     public class EndRestoreRunnable implements Runnable {
 
-        BackupManagerService mBackupManager;
+        UserBackupManagerService mBackupManager;
         ActiveRestoreSession mSession;
 
-        public EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
+        public EndRestoreRunnable(UserBackupManagerService manager, ActiveRestoreSession session) {
             mBackupManager = manager;
             mSession = session;
         }
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index a8c7ce6..e4890e0 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -23,8 +23,8 @@
 
 import com.android.internal.util.Preconditions;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.UserBackupManagerService;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -35,12 +35,12 @@
 public class AdbRestoreFinishedLatch implements BackupRestoreTask {
 
     private static final String TAG = "AdbRestoreFinishedLatch";
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     final CountDownLatch mLatch;
     private final int mCurrentOpToken;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
-    public AdbRestoreFinishedLatch(BackupManagerService backupManagerService,
+    public AdbRestoreFinishedLatch(UserBackupManagerService backupManagerService,
             int currentOpToken) {
         this.backupManagerService = backupManagerService;
         mLatch = new CountDownLatch(1);
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
index dc7044e..184a6d0 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
@@ -3,7 +3,7 @@
 import android.app.IBackupAgent;
 import android.os.RemoteException;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 /**
  * Runner that can be placed on a separate thread to do in-process invocation of the "restore
@@ -13,10 +13,10 @@
 
     private final IBackupAgent mAgent;
     private final int mToken;
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
 
     AdbRestoreFinishedRunnable(IBackupAgent agent, int token,
-            BackupManagerService backupManagerService) {
+            UserBackupManagerService backupManagerService) {
         mAgent = agent;
         mToken = token;
         mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 1084f52..0d26ea5 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -16,13 +16,13 @@
 
 package com.android.server.backup.restore;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 
 import android.app.ApplicationThreadConstants;
@@ -44,10 +44,10 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FileMetadata;
 import com.android.server.backup.KeyValueAdbRestoreEngine;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.fullbackup.FullBackupObbConnection;
 import com.android.server.backup.utils.BytesReadListener;
 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
@@ -57,7 +57,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -68,7 +67,7 @@
  */
 public class FullRestoreEngine extends RestoreEngine {
 
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     // Task in charge of monitoring timeouts
     private final BackupRestoreTask mMonitorTask;
 
@@ -129,7 +128,7 @@
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     final boolean mIsAdbRestore;
 
-    public FullRestoreEngine(BackupManagerService backupManagerService,
+    public FullRestoreEngine(UserBackupManagerService backupManagerService,
             BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
             IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
             boolean allowObbs, int ephemeralOpToken, boolean isAdbRestore) {
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 32dbad9..c904256 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -16,18 +16,15 @@
 
 package com.android.server.backup.restore;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
 import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
 import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
-
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
+import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
 import android.app.IBackupAgent;
 import android.app.backup.BackupAgent;
@@ -36,15 +33,12 @@
 import android.content.pm.Signature;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
-import com.android.server.LocalServices;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
-import com.android.server.backup.PackageManagerBackupAgent;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.fullbackup.FullBackupObbConnection;
 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
 import com.android.server.backup.utils.PasswordUtils;
@@ -72,7 +66,7 @@
 
 public class PerformAdbRestoreTask implements Runnable {
 
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final ParcelFileDescriptor mInputFile;
     private final String mCurrentPassword;
     private final String mDecryptPassword;
@@ -106,7 +100,7 @@
     // Packages we've already wiped data on when restoring their first file
     private final HashSet<String> mClearedPackages = new HashSet<>();
 
-    public PerformAdbRestoreTask(BackupManagerService backupManagerService,
+    public PerformAdbRestoreTask(UserBackupManagerService backupManagerService,
             ParcelFileDescriptor fd, String curPassword, String decryptPassword,
             IFullBackupRestoreObserver observer, AtomicBoolean latch) {
         this.mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 580f70a..f7efad6 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -17,12 +17,12 @@
 package com.android.server.backup.restore;
 
 import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
+import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
+import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
@@ -40,8 +40,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.os.Bundle;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -62,8 +62,8 @@
 import com.android.server.backup.BackupUtils;
 import com.android.server.backup.PackageManagerBackupAgent;
 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.transport.TransportClient;
 import com.android.server.backup.utils.AppBackupUtils;
@@ -80,7 +80,7 @@
 
 public class PerformUnifiedRestoreTask implements BackupRestoreTask {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     private final TransportManager mTransportManager;
     // Transport client we're working with to do the restore
     private final TransportClient mTransportClient;
@@ -164,7 +164,7 @@
     // This task can assume that the wakelock is properly held for it and doesn't have to worry
     // about releasing it.
     public PerformUnifiedRestoreTask(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             IRestoreObserver observer,
             IBackupManagerMonitor monitor,
diff --git a/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java b/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
index 635b6d6..c4aa2d7 100644
--- a/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
+++ b/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
@@ -21,7 +21,7 @@
 import android.os.RemoteException;
 
 import com.android.server.backup.FileMetadata;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 import java.io.IOException;
 
@@ -35,9 +35,9 @@
     private final FileMetadata mInfo;
     private final ParcelFileDescriptor mSocket;
     private final int mToken;
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
 
-    RestoreFileRunnable(BackupManagerService backupManagerService, IBackupAgent agent,
+    RestoreFileRunnable(UserBackupManagerService backupManagerService, IBackupAgent agent,
             FileMetadata info, ParcelFileDescriptor socket, int token) throws IOException {
         mAgent = agent;
         mInfo = info;
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index c933833..e465c7e 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -17,8 +17,8 @@
 package com.android.server.backup.utils;
 
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
 import android.annotation.Nullable;
 import android.app.backup.BackupTransport;
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 6dd5284..0f4b681 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -34,14 +34,14 @@
 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH;
 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
 import android.app.backup.BackupAgent;
 import android.app.backup.BackupManagerMonitor;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6174300..784d398 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -32,6 +32,10 @@
         "android.hardware.tv.cec-V1.0-java",
     ],
 
+    required: [
+        "gps_debug.conf",
+    ],
+
     static_libs: [
         "time_zone_distro",
         "time_zone_distro_installer",
@@ -69,3 +73,9 @@
     name: "services.core",
     static_libs: ["services.core.priorityboosted"],
 }
+
+
+prebuilt_etc {
+    name: "gps_debug.conf",
+    src: "java/com/android/server/location/gps_debug.conf",
+}
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java
index b37888f..71d261c 100644
--- a/services/core/java/com/android/server/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java
@@ -163,6 +163,20 @@
     }
 
     /**
+     * Gets the user associated with this service.
+     */
+    public final @UserIdInt int getUserId() {
+        return mUserId;
+    }
+
+    /**
+     * Gets the master service.
+     */
+    public final M getMaster() {
+        return mMaster;
+    }
+
+    /**
      * Gets this UID of the remote service this service binds to, or {@code -1} if the service is
      * disabled.
      */
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/AbstractRemoteService.java
index 181d7fd..0d4cf6b 100644
--- a/services/core/java/com/android/server/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/AbstractRemoteService.java
@@ -205,6 +205,9 @@
 
     protected void scheduleUnbind() {
         cancelScheduledUnbind();
+        // TODO(b/111276913): implement "permanent binding"
+        // TODO(b/117779333): make sure it's unbound if the service settings changing (right now
+        // it's not)
         mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this)
                 .setWhat(MSG_UNBIND), getTimeoutIdleBindMillis());
     }
@@ -250,7 +253,7 @@
         }
 
         final boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags,
-                new UserHandle(mUserId));
+                mHandler, new UserHandle(mUserId));
 
         if (!willBind) {
             Slog.w(mTag, "could not bind to " + mIntent + " using flags " + flags);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 74c8023..bb3b9f7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -894,10 +894,18 @@
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(
-                mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
         mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
                 new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
 
+        // Listen to package add and removal events for all users.
+        intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.registerReceiverAsUser(
+                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+
         try {
             mNMS.registerObserver(mTethering);
             mNMS.registerObserver(mDataActivityObserver);
@@ -1659,6 +1667,24 @@
                 loge("Error parsing ip address in validation event");
             }
         }
+
+        @Override
+        public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+                String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
+            NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
+            // Netd event only allow registrants from system. Each NetworkMonitor thread is under
+            // the caller thread of registerNetworkAgent. Thus, it's not allowed to register netd
+            // event callback for certain nai. e.g. cellular. Register here to pass to
+            // NetworkMonitor instead.
+            // TODO: Move the Dns Event to NetworkMonitor. Use Binder.clearCallingIdentity() in
+            // registerNetworkAgent to have NetworkMonitor created with system process as design
+            // expectation. Also, NetdEventListenerService only allow one callback from each
+            // caller type. Need to re-factor NetdEventListenerService to allow multiple
+            // NetworkMonitor registrants.
+            if (nai != null && nai.satisfies(mDefaultRequest)) {
+                nai.networkMonitor.sendMessage(NetworkMonitor.EVENT_DNS_NOTIFICATION, returnCode);
+            }
+        }
     };
 
     @VisibleForTesting
@@ -2073,12 +2099,7 @@
         return new MockableSystemProperties();
     }
 
-    private void updateTcpBufferSizes(NetworkAgentInfo nai) {
-        if (isDefaultNetwork(nai) == false) {
-            return;
-        }
-
-        String tcpBufferSizes = nai.linkProperties.getTcpBufferSizes();
+    private void updateTcpBufferSizes(String tcpBufferSizes) {
         String[] values = null;
         if (tcpBufferSizes != null) {
             values = tcpBufferSizes.split(",");
@@ -4155,6 +4176,7 @@
     }
 
     private void onUserAdded(int userId) {
+        mPermissionMonitor.onUserAdded(userId);
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
@@ -4165,6 +4187,7 @@
     }
 
     private void onUserRemoved(int userId) {
+        mPermissionMonitor.onUserRemoved(userId);
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
@@ -4174,6 +4197,22 @@
         }
     }
 
+    private void onPackageAdded(String packageName, int uid) {
+        if (TextUtils.isEmpty(packageName) || uid < 0) {
+            Slog.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid);
+            return;
+        }
+        mPermissionMonitor.onPackageAdded(packageName, uid);
+    }
+
+    private void onPackageRemoved(String packageName, int uid) {
+        if (TextUtils.isEmpty(packageName) || uid < 0) {
+            Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
+            return;
+        }
+        mPermissionMonitor.onPackageRemoved(uid);
+    }
+
     private void onUserUnlocked(int userId) {
         synchronized (mVpns) {
             // User present may be sent because of an unlock, which might mean an unlocked keystore.
@@ -4185,11 +4224,15 @@
         }
     }
 
-    private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+            final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+            final Uri packageData = intent.getData();
+            final String packageName =
+                    packageData != null ? packageData.getSchemeSpecificPart() : null;
             if (userId == UserHandle.USER_NULL) return;
 
             if (Intent.ACTION_USER_STARTED.equals(action)) {
@@ -4202,6 +4245,10 @@
                 onUserRemoved(userId);
             } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
                 onUserUnlocked(userId);
+            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                onPackageAdded(packageName, uid);
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                onPackageRemoved(packageName, uid);
             }
         }
     };
@@ -4728,8 +4775,8 @@
         updateUids(nai, null, nai.networkCapabilities);
     }
 
-    private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
-        LinkProperties newLp = new LinkProperties(networkAgent.linkProperties);
+    private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
+            LinkProperties oldLp) {
         int netId = networkAgent.network.netId;
 
         // The NetworkAgentInfo does not know whether clatd is running on its network or not. Before
@@ -4744,7 +4791,9 @@
 //        for (LinkProperties lp : newLp.getStackedLinks()) {
 //            updateMtu(lp, null);
 //        }
-        updateTcpBufferSizes(networkAgent);
+        if (isDefaultNetwork(networkAgent)) {
+            updateTcpBufferSizes(newLp.getTcpBufferSizes());
+        }
 
         updateRoutes(newLp, oldLp, netId);
         updateDnses(newLp, oldLp, netId);
@@ -4754,8 +4803,6 @@
         // updateDnses will fetch the private DNS configuration from DnsManager.
         mDnsManager.updatePrivateDnsStatus(netId, newLp);
 
-        // Start or stop clat accordingly to network state.
-        networkAgent.updateClat(mNMS);
         if (isDefaultNetwork(networkAgent)) {
             handleApplyDefaultProxy(newLp.getHttpProxy());
         } else {
@@ -4766,8 +4813,12 @@
             synchronized (networkAgent) {
                 networkAgent.linkProperties = newLp;
             }
+            // Start or stop clat accordingly to network state.
+            networkAgent.updateClat(mNMS);
             notifyIfacesChangedForNetworkStats();
-            notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
+            if (networkAgent.everConnected) {
+                notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
+            }
         }
 
         mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
@@ -5072,13 +5123,7 @@
                     "; created=" + nai.created +
                     "; everConnected=" + nai.everConnected);
         }
-        LinkProperties oldLp = nai.linkProperties;
-        synchronized (nai) {
-            nai.linkProperties = newLp;
-        }
-        if (nai.everConnected) {
-            updateLinkProperties(nai, oldLp);
-        }
+        updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
     }
 
     private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
@@ -5239,7 +5284,7 @@
 
         notifyLockdownVpn(newNetwork);
         handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
-        updateTcpBufferSizes(newNetwork);
+        updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
         mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
         notifyIfacesChangedForNetworkStats();
     }
@@ -5654,7 +5699,8 @@
             }
 
             handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
-            updateLinkProperties(networkAgent, null);
+            updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
+                    null);
 
             networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
             scheduleUnvalidatedPrompt(networkAgent);
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 8c25917..7ee3d3b 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -24,6 +24,7 @@
 import static android.system.OsConstants.EINVAL;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_DGRAM;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.NonNull;
@@ -62,6 +63,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
+import libcore.io.IoUtils;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -73,8 +76,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import libcore.io.IoUtils;
-
 /**
  * A service to manage multiple clients that want to access the IpSec API. The service is
  * responsible for maintaining a list of clients and managing the resources (and related quotas)
@@ -621,7 +622,8 @@
                                 mConfig.getDestinationAddress(),
                                 spi,
                                 mConfig.getMarkValue(),
-                                mConfig.getMarkMask());
+                                mConfig.getMarkMask(),
+                                mConfig.getXfrmInterfaceId());
             } catch (RemoteException | ServiceSpecificException e) {
                 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
             }
@@ -683,7 +685,8 @@
                     mSrvConfig
                             .getNetdInstance()
                             .ipSecDeleteSecurityAssociation(
-                                    uid, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
+                                    uid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
+                                    0 /* mask */, 0 /* if_id */);
                 }
             } catch (ServiceSpecificException | RemoteException e) {
                 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
@@ -795,6 +798,8 @@
         private final int mIkey;
         private final int mOkey;
 
+        private final int mIfId;
+
         TunnelInterfaceRecord(
                 int resourceId,
                 String interfaceName,
@@ -802,7 +807,8 @@
                 String localAddr,
                 String remoteAddr,
                 int ikey,
-                int okey) {
+                int okey,
+                int intfId) {
             super(resourceId);
 
             mInterfaceName = interfaceName;
@@ -811,6 +817,7 @@
             mRemoteAddress = remoteAddr;
             mIkey = ikey;
             mOkey = okey;
+            mIfId = intfId;
         }
 
         /** always guarded by IpSecService#this */
@@ -821,7 +828,7 @@
             //       Delete global policies
             try {
                 final INetd netd = mSrvConfig.getNetdInstance();
-                netd.removeVirtualTunnelInterface(mInterfaceName);
+                netd.ipSecRemoveTunnelInterface(mInterfaceName);
 
                 for (int selAddrFamily : ADDRESS_FAMILIES) {
                     netd.ipSecDeleteSecurityPolicy(
@@ -829,13 +836,15 @@
                             selAddrFamily,
                             IpSecManager.DIRECTION_OUT,
                             mOkey,
-                            0xffffffff);
+                            0xffffffff,
+                            mIfId);
                     netd.ipSecDeleteSecurityPolicy(
                             uid,
                             selAddrFamily,
                             IpSecManager.DIRECTION_IN,
                             mIkey,
-                            0xffffffff);
+                            0xffffffff,
+                            mIfId);
                 }
             } catch (ServiceSpecificException | RemoteException e) {
                 Log.e(
@@ -877,6 +886,10 @@
             return mOkey;
         }
 
+        public int getIfId() {
+            return mIfId;
+        }
+
         @Override
         protected ResourceTracker getResourceTracker() {
             return getUserRecord().mTunnelQuotaTracker;
@@ -1286,7 +1299,7 @@
             //       Add inbound/outbound global policies
             //              (use reqid = 0)
             final INetd netd = mSrvConfig.getNetdInstance();
-            netd.addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey);
+            netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
 
             for (int selAddrFamily : ADDRESS_FAMILIES) {
                 // Always send down correct local/remote addresses for template.
@@ -1298,7 +1311,8 @@
                         remoteAddr,
                         0,
                         okey,
-                        0xffffffff);
+                        0xffffffff,
+                        resourceId);
                 netd.ipSecAddSecurityPolicy(
                         callerUid,
                         selAddrFamily,
@@ -1307,7 +1321,8 @@
                         localAddr,
                         0,
                         ikey,
-                        0xffffffff);
+                        0xffffffff,
+                        resourceId);
             }
 
             userRecord.mTunnelInterfaceRecords.put(
@@ -1320,7 +1335,8 @@
                                     localAddr,
                                     remoteAddr,
                                     ikey,
-                                    okey),
+                                    okey,
+                                    resourceId),
                             binder));
             return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
         } catch (RemoteException e) {
@@ -1523,6 +1539,9 @@
                 throw new IllegalArgumentException(
                         "Invalid IpSecTransform.mode: " + config.getMode());
         }
+
+        config.setMarkValue(0);
+        config.setMarkMask(0);
     }
 
     private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
@@ -1584,7 +1603,8 @@
                         (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
                         encapType,
                         encapLocalPort,
-                        encapRemotePort);
+                        encapRemotePort,
+                        c.getXfrmInterfaceId());
     }
 
     /**
@@ -1740,27 +1760,48 @@
                         : tunnelInterfaceInfo.getIkey();
 
         try {
-            c.setMarkValue(mark);
-            c.setMarkMask(0xffffffff);
+            // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip
+            // SPI matching as part of the template resolution.
+            int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
+            c.setXfrmInterfaceId(tunnelInterfaceInfo.getIfId());
+
+            // TODO: enable this when UPDSA supports updating marks. Adding kernel support upstream
+            //     (and backporting) would allow us to narrow the mark space, and ensure that the SA
+            //     and SPs have matching marks (as VTI are meant to be built).
+            // Currently update does nothing with marks. Leave empty (defaulting to 0) to ensure the
+            //     config matches the actual allocated resources in the kernel.
+            // All SAs will have zero marks (from creation time), and any policy that matches the
+            //     same src/dst could match these SAs. Non-IpSecService governed processes that
+            //     establish floating policies with the same src/dst may result in undefined
+            //     behavior. This is generally limited to vendor code due to the permissions
+            //     (CAP_NET_ADMIN) required.
+            //
+            // c.setMarkValue(mark);
+            // c.setMarkMask(0xffffffff);
 
             if (direction == IpSecManager.DIRECTION_OUT) {
                 // Set output mark via underlying network (output only)
                 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork());
 
-                // If outbound, also add SPI to the policy.
-                for (int selAddrFamily : ADDRESS_FAMILIES) {
-                    mSrvConfig
-                            .getNetdInstance()
-                            .ipSecUpdateSecurityPolicy(
-                                    callingUid,
-                                    selAddrFamily,
-                                    direction,
-                                    tunnelInterfaceInfo.getLocalAddress(),
-                                    tunnelInterfaceInfo.getRemoteAddress(),
-                                    transformInfo.getSpiRecord().getSpi(),
-                                    mark,
-                                    0xffffffff);
-                }
+                // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
+                // but want to guarantee outbound packets are sent over the new SA.
+                spi = transformInfo.getSpiRecord().getSpi();
+            }
+
+            // Always update the policy with the relevant XFRM_IF_ID
+            for (int selAddrFamily : ADDRESS_FAMILIES) {
+                mSrvConfig
+                        .getNetdInstance()
+                        .ipSecUpdateSecurityPolicy(
+                                callingUid,
+                                selAddrFamily,
+                                direction,
+                                transformInfo.getConfig().getSourceAddress(),
+                                transformInfo.getConfig().getDestinationAddress(),
+                                spi, // If outbound, also add SPI to the policy.
+                                mark, // Must always set policy mark; ikey/okey for VTIs
+                                0xffffffff,
+                                c.getXfrmInterfaceId());
             }
 
             // Update SA with tunnel mark (ikey or okey based on direction)
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 046442a..e5275e5 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -2990,7 +2991,7 @@
         ArrayList<Receiver> deadReceivers = null;
         ArrayList<UpdateRecord> deadUpdateRecords = null;
 
-        // Broadcast location or status to all listeners
+        // Broadcast location to all listeners
         for (UpdateRecord r : records) {
             Receiver receiver = r.mReceiver;
             boolean receiverDead = false;
@@ -3049,14 +3050,19 @@
                 }
             }
 
-            long prevStatusUpdateTime = r.mLastStatusBroadcast;
-            if ((newStatusUpdateTime > prevStatusUpdateTime) &&
-                    (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
+            // TODO: location provider status callbacks have been disabled and deprecated, and are
+            // guarded behind this setting now. should be removed completely post-Q
+            if (Settings.Global.getInt(mContext.getContentResolver(),
+                    LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
+                long prevStatusUpdateTime = r.mLastStatusBroadcast;
+                if ((newStatusUpdateTime > prevStatusUpdateTime)
+                        && (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
 
-                r.mLastStatusBroadcast = newStatusUpdateTime;
-                if (!receiver.callStatusChangedLocked(provider, status, extras)) {
-                    receiverDead = true;
-                    Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
+                    r.mLastStatusBroadcast = newStatusUpdateTime;
+                    if (!receiver.callStatusChangedLocked(provider, status, extras)) {
+                        receiverDead = true;
+                        Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
+                    }
                 }
             }
 
@@ -3276,7 +3282,6 @@
             // we don't leave anything dangling.
             clearTestProviderEnabled(provider, opPackageName);
             clearTestProviderLocation(provider, opPackageName);
-            clearTestProviderStatus(provider, opPackageName);
 
             MockProvider mockProvider = mMockProviders.remove(provider);
             if (mockProvider == null) {
@@ -3409,21 +3414,6 @@
     }
 
     @Override
-    public void clearTestProviderStatus(String provider, String opPackageName) {
-        if (!canCallerAccessMockLocation(opPackageName)) {
-            return;
-        }
-
-        synchronized (mLock) {
-            MockProvider mockProvider = mMockProviders.get(provider);
-            if (mockProvider == null) {
-                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
-            }
-            mockProvider.clearStatus();
-        }
-    }
-
-    @Override
     public PendingIntent createManageLocationPermissionIntent(String packageName,
             String permission) {
         Preconditions.checkNotNull(packageName);
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index 2dee3a0..c563ad2 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -141,6 +141,7 @@
         if (mEnabled != enabled) {
             mEnabled = enabled;
             mStats.reset();
+            mStats.setAddDebugEntries(enabled);
             Looper.setObserver(enabled ? mStats : null);
         }
     }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index d1b56e9..e80e9e1 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -105,6 +105,7 @@
         "android.hardware.camera.provider@2.4::ICameraProvider",
         "android.hardware.graphics.allocator@2.0::IAllocator",
         "android.hardware.graphics.composer@2.1::IComposer",
+        "android.hardware.health@2.0::IHealth",
         "android.hardware.media.omx@1.0::IOmx",
         "android.hardware.media.omx@1.0::IOmxStore",
         "android.hardware.sensors@1.0::ISensors",
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 5c77f0a..8571ae6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
+
 import android.content.ContentResolver;
 import android.database.ContentObserver;
 import android.net.Uri;
@@ -26,8 +28,6 @@
 
 import java.io.PrintWriter;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
-
 /**
  * Settings constants that can modify the activity manager's behavior.
  */
@@ -222,6 +222,10 @@
     // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
     volatile boolean mFlagActivityStartsLoggingEnabled;
 
+    // Indicates whether the background activity starts is enabled.
+    // Controlled by Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED
+    volatile boolean mFlagBackgroundActivityStartsEnabled;
+
     private final ActivityManagerService mService;
     private ContentResolver mResolver;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -256,6 +260,10 @@
     private static final Uri ACTIVITY_STARTS_LOGGING_ENABLED_URI = Settings.Global.getUriFor(
                 Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED);
 
+    private static final Uri BACKGROUND_ACTIVITY_STARTS_ENABLED_URI =
+                Settings.Global.getUriFor(
+                        Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED);
+
     public ActivityManagerConstants(ActivityManagerService service, Handler handler) {
         super(handler);
         mService = service;
@@ -266,8 +274,10 @@
         mResolver = resolver;
         mResolver.registerContentObserver(ACTIVITY_MANAGER_CONSTANTS_URI, false, this);
         mResolver.registerContentObserver(ACTIVITY_STARTS_LOGGING_ENABLED_URI, false, this);
+        mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_ENABLED_URI, false, this);
         updateConstants();
         updateActivityStartsLoggingEnabled();
+        updateBackgroundActivityStartsEnabled();
     }
 
     public void setOverrideMaxCachedProcesses(int value) {
@@ -290,6 +300,8 @@
             updateConstants();
         } else if (ACTIVITY_STARTS_LOGGING_ENABLED_URI.equals(uri)) {
             updateActivityStartsLoggingEnabled();
+        } else if (BACKGROUND_ACTIVITY_STARTS_ENABLED_URI.equals(uri)) {
+            updateBackgroundActivityStartsEnabled();
         }
     }
 
@@ -373,6 +385,11 @@
                 Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 0) == 1;
     }
 
+    private void updateBackgroundActivityStartsEnabled() {
+        mFlagBackgroundActivityStartsEnabled = Settings.Global.getInt(mResolver,
+                Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED, 1) == 1;
+    }
+
     private void updateMaxCachedProcesses() {
         CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0
                 ? MAX_CACHED_PROCESSES : mOverrideMaxCachedProcesses;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7e9e83c..b62f648 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -497,6 +497,18 @@
     private static final int MINIMUM_MEMORY_GROWTH_THRESHOLD = 10 * 1000; // 10 MB
 
     /**
+     * The number of binder proxies we need to have before we start warning and
+     * dumping debug info.
+     */
+    private static final int BINDER_PROXY_HIGH_WATERMARK = 6000;
+
+    /**
+     * Low watermark that needs to be met before we consider dumping info again,
+     * after already hitting the high watermark.
+     */
+    private static final int BINDER_PROXY_LOW_WATERMARK = 5500;
+
+    /**
      * State indicating that there is no need for any blocking for network.
      */
     @VisibleForTesting
@@ -4360,7 +4372,6 @@
 
         EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
 
-        app.makeActive(thread, mProcessStats);
         app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
         app.setCurrentSchedulingGroup(app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT);
         app.forcingToImportant = null;
@@ -4567,6 +4578,10 @@
                 profilerInfo.closeFd();
                 profilerInfo = null;
             }
+
+            // Make app active after binding application or client may be running requests (e.g
+            // starting activities) before it is ready.
+            app.makeActive(thread, mProcessStats);
             checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
             mProcessList.updateLruProcessLocked(app, false, null);
             checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
@@ -8477,7 +8492,8 @@
             mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
             mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
 
-            BinderInternal.nSetBinderProxyCountWatermarks(6000,5500);
+            BinderInternal.nSetBinderProxyCountWatermarks(BINDER_PROXY_HIGH_WATERMARK,
+                    BINDER_PROXY_LOW_WATERMARK);
             BinderInternal.nSetBinderProxyCountEnabled(true);
             BinderInternal.setBinderProxyCountCallback(
                     new BinderInternal.BinderProxyLimitListener() {
@@ -9217,11 +9233,6 @@
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
                 }
-                dumpBinderProxies(pw);
-                pw.println();
-                if (dumpAll) {
-                    pw.println("-------------------------------------------------------------------------------");
-                }
                 dumpLmkLocked(pw);
             }
             pw.println();
@@ -9235,6 +9246,19 @@
             }
             dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
         }
+        if (dumpPackage == null) {
+            // Intentionally dropping the lock for this, because dumpBinderProxies() will make many
+            // outgoing binder calls to retrieve interface descriptors; while that is system code,
+            // there is nothing preventing an app from overriding this implementation by talking to
+            // the binder driver directly, and hang up system_server in the process. So, dump
+            // without locks held, and even then only when there is an unreasonably large number of
+            // proxies in the first place.
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpBinderProxies(pw, BINDER_PROXY_HIGH_WATERMARK /* minToDump */);
+        }
     }
 
     /**
@@ -9373,7 +9397,7 @@
                         cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage);
             } else if ("binder-proxies".equals(cmd)) {
                 if (opti >= args.length) {
-                    dumpBinderProxies(pw);
+                    dumpBinderProxies(pw, 0 /* minToDump */);
                 } else {
                     String uid = args[opti];
                     opti++;
@@ -9714,10 +9738,17 @@
         return false;
     }
 
-    void dumpBinderProxies(PrintWriter pw) {
+    void dumpBinderProxies(PrintWriter pw, int minCountToDumpInterfaces) {
         pw.println("ACTIVITY MANAGER BINDER PROXY STATE (dumpsys activity binder-proxies)");
-        dumpBinderProxyInterfaceCounts(pw,
-                "  Top proxy interface names held by SYSTEM");
+        final int proxyCount = BinderProxy.getProxyCount();
+        if (proxyCount >= minCountToDumpInterfaces) {
+            dumpBinderProxyInterfaceCounts(pw,
+                    "Top proxy interface names held by SYSTEM");
+        } else {
+            pw.print("Not dumping proxy interface counts because size ("
+                    + Integer.toString(proxyCount) + ") looks reasonable");
+            pw.println();
+        }
         dumpBinderProxiesCounts(pw,
                 "  Counts of Binder Proxies held by SYSTEM");
     }
@@ -19173,6 +19204,10 @@
             return mConstants.mFlagActivityStartsLoggingEnabled;
         }
 
+        public boolean isBackgroundActivityStartsEnabled() {
+            return mConstants.mFlagBackgroundActivityStartsEnabled;
+        }
+
         public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {
             synchronized(ActivityManagerService.this) {
                 ActivityManagerService.this.reportGlobalUsageEventLocked(keyguardShowing
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 4fe7d03..09df7e20 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -222,11 +222,11 @@
                         receiverExtras.putInt(KEY_RECEIVER_EXTRA_INDEX, i);
                         receiverExtras.putInt(KEY_RECEIVER_EXTRA_COUNT, numActivities);
                         boolean result = requestAutofillData
-                                ? ActivityTaskManager.getService().requestAssistContextExtras(
+                                ? ActivityTaskManager.getService().requestAutofillData(this,
+                                        receiverExtras, topActivity, 0 /* flags */)
+                                : ActivityTaskManager.getService().requestAssistContextExtras(
                                         ASSIST_CONTEXT_FULL, this, receiverExtras, topActivity,
-                                                /* focused= */ i == 0, /* newSessionId= */ i == 0)
-                                : ActivityTaskManager.getService().requestAutofillData(this,
-                                        receiverExtras, topActivity, 0 /* flags */);
+                                        /* focused= */ i == 0, /* newSessionId= */ i == 0);
                         if (result) {
                             mPendingDataCount++;
                         } else if (i == 0) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index a5848ca..4c4a090 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -65,6 +65,7 @@
     // permission in the corresponding .te file your feature belongs to.
     @VisibleForTesting
     static final String[] sGlobalSettings = new String[] {
+            Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
     };
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index b817669..48b4145 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -1,17 +1,6 @@
 {
   "presubmit": [
     {
-      "name": "CtsActivityManagerDeviceTestCases",
-      "options": [
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
-        }
-      ]
-    },
-    {
       "name": "CtsActivityManagerDeviceSdk25TestCases",
       "options": [
         {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 67d27c9..6cde4ad 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -649,6 +649,9 @@
     private String mEnabledSurroundFormats;
     private boolean mSurroundModeChanged;
 
+    @GuardedBy("mSettingsLock")
+    private int mAssistantUid;
+
     // Intent "extra" data keys.
     public static final String CONNECT_INTENT_KEY_PORT_NAME = "portName";
     public static final String CONNECT_INTENT_KEY_STATE = "state";
@@ -1079,6 +1082,10 @@
             AudioSystem.setForceUse(AudioSystem.FOR_DOCK, forDock);
             sendEncodedSurroundMode(mContentResolver, "onAudioServerDied");
             sendEnabledSurroundFormats(mContentResolver, true);
+            updateAssistantUId(true);
+        }
+        synchronized (mAccessibilityServiceUidsLock) {
+            AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
         }
         synchronized (mHdmiClientLock) {
             if (mHdmiManager != null && mHdmiTvClient != null) {
@@ -1404,6 +1411,39 @@
         }
     }
 
+    @GuardedBy("mSettingsLock")
+    private void updateAssistantUId(boolean forceUpdate) {
+        int assistantUid = 0;
+
+        // Consider assistants in the following order of priority:
+        // 1) voice interaction service
+        // 2) assistant
+        String assistantName = Settings.Secure.getStringForUser(
+                        mContentResolver,
+                        Settings.Secure.VOICE_INTERACTION_SERVICE, UserHandle.USER_CURRENT);
+        if (TextUtils.isEmpty(assistantName)) {
+            assistantName = Settings.Secure.getStringForUser(
+                    mContentResolver,
+                    Settings.Secure.ASSISTANT, UserHandle.USER_CURRENT);
+        }
+        if (!TextUtils.isEmpty(assistantName)) {
+            String packageName = ComponentName.unflattenFromString(assistantName).getPackageName();
+            if (!TextUtils.isEmpty(packageName)) {
+                try {
+                    assistantUid = mContext.getPackageManager().getPackageUid(packageName, 0);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.e(TAG,
+                            "updateAssistantUId() could not find UID for package: " + packageName);
+                }
+            }
+        }
+
+        if (assistantUid != mAssistantUid || forceUpdate) {
+            AudioSystem.setAssistantUid(assistantUid);
+            mAssistantUid = assistantUid;
+        }
+    }
+
     private void readPersistedSettings() {
         final ContentResolver cr = mContentResolver;
 
@@ -1447,6 +1487,7 @@
             readDockAudioSettings(cr);
             sendEncodedSurroundMode(cr, "readPersistedSettings");
             sendEnabledSurroundFormats(cr, true);
+            updateAssistantUId(true);
         }
 
         mMuteAffectedStreams = System.getIntForUser(cr,
@@ -5811,6 +5852,9 @@
                     mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
             mContentResolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS), false, this);
+
+            mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.VOICE_INTERACTION_SERVICE), false, this);
         }
 
         @Override
@@ -5832,6 +5876,7 @@
                 updateMasterMono(mContentResolver);
                 updateEncodedSurroundOutput();
                 sendEnabledSurroundFormats(mContentResolver, mSurroundModeChanged);
+                updateAssistantUId(false);
             }
         }
 
@@ -7658,6 +7703,7 @@
                         mAccessibilityServiceUids = uids.toArray();
                     }
                 }
+                AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
             }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index c3e3842..c2f4406 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -72,6 +72,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Protocol;
+import com.android.internal.util.RingBufferIndices;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
@@ -99,7 +100,7 @@
     private static final String TAG = NetworkMonitor.class.getSimpleName();
     private static final boolean DBG  = true;
     private static final boolean VDBG = false;
-
+    private static final boolean VDBG_STALL = Log.isLoggable(TAG, Log.DEBUG);
     // Default configuration values for captive portal detection probes.
     // TODO: append a random length parameter to the default HTTPS url.
     // TODO: randomize browser version ids in the default User-Agent String.
@@ -116,6 +117,15 @@
     private static final int SOCKET_TIMEOUT_MS = 10000;
     private static final int PROBE_TIMEOUT_MS  = 3000;
 
+    // Default configuration values for data stall detection.
+    private static final int DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = 5;
+    private static final int DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS = 60 * 1000;
+    private static final int DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS = 30 * 60 * 1000;
+
+    private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
+    private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES =
+            (1 << DATA_STALL_EVALUATION_TYPE_DNS);
+
     static enum EvaluationResult {
         VALIDATED(true),
         CAPTIVE_PORTAL(false);
@@ -233,6 +243,12 @@
      */
     public static final int CMD_PROBE_COMPLETE = BASE + 16;
 
+    /**
+     * ConnectivityService notifies NetworkMonitor of DNS query responses event.
+     * arg1 = returncode in OnDnsEvent which indicates the response code for the DNS query.
+     */
+    public static final int EVENT_DNS_NOTIFICATION = BASE + 17;
+
     // Start mReevaluateDelayMs at this value and double.
     private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
     private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
@@ -314,6 +330,12 @@
     private int mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
     private int mEvaluateAttempts = 0;
     private volatile int mProbeToken = 0;
+    private final int mConsecutiveDnsTimeoutThreshold;
+    private final int mDataStallMinEvaluateTime;
+    private final int mDataStallValidDnsTimeThreshold;
+    private final int mDataStallEvaluationType;
+    private final DnsStallDetector mDnsStallDetector;
+    private long mLastProbeTime;
 
     public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
             NetworkRequest defaultRequest) {
@@ -359,6 +381,12 @@
         mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
         mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
         mRandom = deps.getRandom();
+        // TODO: Evaluate to move data stall configuration to a specific class.
+        mConsecutiveDnsTimeoutThreshold = getConsecutiveDnsTimeoutThreshold();
+        mDnsStallDetector = new DnsStallDetector(mConsecutiveDnsTimeoutThreshold);
+        mDataStallMinEvaluateTime = getDataStallMinEvaluateTime();
+        mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold();
+        mDataStallEvaluationType = getDataStallEvalutionType();
 
         start();
     }
@@ -507,6 +535,9 @@
                     sendMessage(CMD_EVALUATE_PRIVATE_DNS);
                     break;
                 }
+                case EVENT_DNS_NOTIFICATION:
+                    mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
+                    break;
                 default:
                     break;
             }
@@ -537,6 +568,13 @@
                 case CMD_EVALUATE_PRIVATE_DNS:
                     transitionTo(mEvaluatingPrivateDnsState);
                     break;
+                case EVENT_DNS_NOTIFICATION:
+                    mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
+                    if (isDataStall()) {
+                        validationLog("Suspecting data stall, reevaluate");
+                        transitionTo(mEvaluatingState);
+                    }
+                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -856,6 +894,7 @@
 
                     final CaptivePortalProbeResult probeResult =
                             (CaptivePortalProbeResult) message.obj;
+                    mLastProbeTime = SystemClock.elapsedRealtime();
                     if (probeResult.isSuccessful()) {
                         // Transit EvaluatingPrivateDnsState to get to Validated
                         // state (even if no Private DNS validation required).
@@ -883,6 +922,7 @@
                     // Leave the event to EvaluatingState. Defer this message will result in reset
                     // of mReevaluateDelayMs and mEvaluateAttempts.
                 case CMD_NETWORK_DISCONNECTED:
+                case EVENT_DNS_NOTIFICATION:
                     return NOT_HANDLED;
                 default:
                     // TODO: Some events may able to handle in this state, instead of deferring to
@@ -947,6 +987,29 @@
                 Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
     }
 
+    private int getConsecutiveDnsTimeoutThreshold() {
+        return mDependencies.getSetting(mContext,
+                Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD,
+                DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD);
+    }
+
+    private int getDataStallMinEvaluateTime() {
+        return mDependencies.getSetting(mContext,
+                Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL,
+                DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS);
+    }
+
+    private int getDataStallValidDnsTimeThreshold() {
+        return mDependencies.getSetting(mContext,
+                Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD,
+                DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS);
+    }
+
+    private int getDataStallEvalutionType() {
+        return mDependencies.getSetting(mContext, Settings.Global.DATA_STALL_EVALUATION_TYPE,
+                DEFAULT_DATA_STALL_EVALUATION_TYPES);
+    }
+
     // Static for direct access by ConnectivityService
     public static String getCaptivePortalServerHttpUrl(Context context) {
         return getCaptivePortalServerHttpUrl(Dependencies.DEFAULT, context);
@@ -1462,4 +1525,127 @@
 
         public static final Dependencies DEFAULT = new Dependencies();
     }
+
+    /**
+     * Methods in this class perform no locking because all accesses are performed on the state
+     * machine's thread. Need to consider the thread safety if it ever could be accessed outside the
+     * state machine.
+     */
+    @VisibleForTesting
+    protected class DnsStallDetector {
+        private static final int DEFAULT_DNS_LOG_SIZE = 50;
+        private int mConsecutiveTimeoutCount = 0;
+        private int mSize;
+        final DnsResult[] mDnsEvents;
+        final RingBufferIndices mResultIndices;
+
+        DnsStallDetector(int size) {
+            mSize = Math.max(DEFAULT_DNS_LOG_SIZE, size);
+            mDnsEvents = new DnsResult[mSize];
+            mResultIndices = new RingBufferIndices(mSize);
+        }
+
+        @VisibleForTesting
+        protected void accumulateConsecutiveDnsTimeoutCount(int code) {
+            final DnsResult result = new DnsResult(code);
+            mDnsEvents[mResultIndices.add()] = result;
+            if (result.isTimeout()) {
+                mConsecutiveTimeoutCount++;
+            } else {
+                // Keep the event in mDnsEvents without clearing it so that there are logs to do the
+                // simulation and analysis.
+                mConsecutiveTimeoutCount = 0;
+            }
+        }
+
+        private boolean isDataStallSuspected(int timeoutCountThreshold, int validTime) {
+            if (timeoutCountThreshold <= 0) {
+                Log.wtf(TAG, "Timeout count threshold should be larger than 0.");
+                return false;
+            }
+
+            // Check if the consecutive timeout count reach the threshold or not.
+            if (mConsecutiveTimeoutCount < timeoutCountThreshold) {
+                return false;
+            }
+
+            // Check if the target dns event index is valid or not.
+            final int firstConsecutiveTimeoutIndex =
+                    mResultIndices.indexOf(mResultIndices.size() - timeoutCountThreshold);
+
+            // If the dns timeout events happened long time ago, the events are meaningless for
+            // data stall evaluation. Thus, check if the first consecutive timeout dns event
+            // considered in the evaluation happened in defined threshold time.
+            final long now = SystemClock.elapsedRealtime();
+            final long firstTimeoutTime = now - mDnsEvents[firstConsecutiveTimeoutIndex].mTimeStamp;
+            return (firstTimeoutTime < validTime);
+        }
+
+        int getConsecutiveTimeoutCount() {
+            return mConsecutiveTimeoutCount;
+        }
+    }
+
+    private static class DnsResult {
+        // TODO: Need to move the DNS return code definition to a specific class once unify DNS
+        // response code is done.
+        private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+
+        private final long mTimeStamp;
+        private final int mReturnCode;
+
+        DnsResult(int code) {
+            mTimeStamp = SystemClock.elapsedRealtime();
+            mReturnCode = code;
+        }
+
+        private boolean isTimeout() {
+            return mReturnCode == RETURN_CODE_DNS_TIMEOUT;
+        }
+    }
+
+
+    @VisibleForTesting
+    protected DnsStallDetector getDnsStallDetector() {
+        return mDnsStallDetector;
+    }
+
+    private boolean dataStallEvaluateTypeEnabled(int type) {
+        return (mDataStallEvaluationType & (1 << type)) != 0;
+    }
+
+    @VisibleForTesting
+    protected long getLastProbeTime() {
+        return mLastProbeTime;
+    }
+
+    @VisibleForTesting
+    protected boolean isDataStall() {
+        boolean result = false;
+        // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the
+        // possible traffic cost in metered network.
+        if (mNetworkAgentInfo.networkCapabilities.isMetered()
+                && (SystemClock.elapsedRealtime() - getLastProbeTime()
+                < mDataStallMinEvaluateTime)) {
+            return false;
+        }
+
+        // Check dns signal. Suspect it may be a data stall if both :
+        // 1. The number of consecutive DNS query timeouts > mConsecutiveDnsTimeoutThreshold.
+        // 2. Those consecutive DNS queries happened in the last mValidDataStallDnsTimeThreshold ms.
+        if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_DNS)) {
+            if (mDnsStallDetector.isDataStallSuspected(mConsecutiveDnsTimeoutThreshold,
+                    mDataStallValidDnsTimeThreshold)) {
+                result = true;
+                logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND);
+            }
+        }
+
+        if (VDBG_STALL) {
+            log("isDataStall: result=" + result + ", consecutive dns timeout count="
+                    + mDnsStallDetector.getConsecutiveTimeoutCount());
+        }
+
+        return result;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 94c94a5..420b23e 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -27,10 +27,7 @@
 import static android.os.Process.SYSTEM_UID;
 
 import android.annotation.NonNull;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -42,7 +39,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -64,15 +60,14 @@
 public class PermissionMonitor {
     private static final String TAG = "PermissionMonitor";
     private static final boolean DBG = true;
-    private static final Boolean SYSTEM = Boolean.TRUE;
-    private static final Boolean NETWORK = Boolean.FALSE;
+    protected static final Boolean SYSTEM = Boolean.TRUE;
+    protected static final Boolean NETWORK = Boolean.FALSE;
     private static final int VERSION_Q = Build.VERSION_CODES.Q;
 
     private final Context mContext;
     private final PackageManager mPackageManager;
     private final UserManager mUserManager;
     private final INetworkManagementService mNetd;
-    private final BroadcastReceiver mIntentReceiver;
 
     // Values are User IDs.
     private final Set<Integer> mUsers = new HashSet<>();
@@ -85,26 +80,6 @@
         mPackageManager = context.getPackageManager();
         mUserManager = UserManager.get(context);
         mNetd = netd;
-        mIntentReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
-                Uri appData = intent.getData();
-                String appName = appData != null ? appData.getSchemeSpecificPart() : null;
-
-                if (Intent.ACTION_USER_ADDED.equals(action)) {
-                    onUserAdded(user);
-                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                    onUserRemoved(user);
-                } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-                    onAppAdded(appName, appUid);
-                } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                    onAppRemoved(appUid);
-                }
-            }
-        };
     }
 
     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -112,17 +87,6 @@
     public synchronized void startMonitoring() {
         log("Monitoring");
 
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_ADDED);
-        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
-        mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
-        intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        intentFilter.addDataScheme("package");
-        mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
-
         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
         if (apps == null) {
             loge("No apps");
@@ -260,7 +224,14 @@
         }
     }
 
-    private synchronized void onUserAdded(int user) {
+    /**
+     * Called when a user is added. See {link #ACTION_USER_ADDED}.
+     *
+     * @param user The integer userHandle of the added user. See {@link #EXTRA_USER_HANDLE}.
+     *
+     * @hide
+     */
+    public synchronized void onUserAdded(int user) {
         if (user < 0) {
             loge("Invalid user in onUserAdded: " + user);
             return;
@@ -272,7 +243,14 @@
         update(users, mApps, true);
     }
 
-    private synchronized void onUserRemoved(int user) {
+    /**
+     * Called when an user is removed. See {link #ACTION_USER_REMOVED}.
+     *
+     * @param user The integer userHandle of the removed user. See {@link #EXTRA_USER_HANDLE}.
+     *
+     * @hide
+     */
+    public synchronized void onUserRemoved(int user) {
         if (user < 0) {
             loge("Invalid user in onUserRemoved: " + user);
             return;
@@ -284,8 +262,8 @@
         update(users, mApps, false);
     }
 
-
-    private Boolean highestPermissionForUid(Boolean currentPermission, String name) {
+    @VisibleForTesting
+    protected Boolean highestPermissionForUid(Boolean currentPermission, String name) {
         if (currentPermission == SYSTEM) {
             return currentPermission;
         }
@@ -303,33 +281,39 @@
         return currentPermission;
     }
 
-    private synchronized void onAppAdded(String appName, int appUid) {
-        if (TextUtils.isEmpty(appName) || appUid < 0) {
-            loge("Invalid app in onAppAdded: " + appName + " | " + appUid);
-            return;
-        }
-
+    /**
+     * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}.
+     *
+     * @param packageName The name of the new package.
+     * @param uid The uid of the new package.
+     *
+     * @hide
+     */
+    public synchronized void onPackageAdded(String packageName, int uid) {
         // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
         // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
-        final Boolean permission = highestPermissionForUid(mApps.get(appUid), appName);
-        if (permission != mApps.get(appUid)) {
-            mApps.put(appUid, permission);
+        final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
+        if (permission != mApps.get(uid)) {
+            mApps.put(uid, permission);
 
             Map<Integer, Boolean> apps = new HashMap<>();
-            apps.put(appUid, permission);
+            apps.put(uid, permission);
             update(mUsers, apps, true);
         }
     }
 
-    private synchronized void onAppRemoved(int appUid) {
-        if (appUid < 0) {
-            loge("Invalid app in onAppRemoved: " + appUid);
-            return;
-        }
+    /**
+     * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}.
+     *
+     * @param uid containing the integer uid previously assigned to the package.
+     *
+     * @hide
+     */
+    public synchronized void onPackageRemoved(int uid) {
         Map<Integer, Boolean> apps = new HashMap<>();
 
         Boolean permission = null;
-        String[] packages = mPackageManager.getPackagesForUid(appUid);
+        String[] packages = mPackageManager.getPackagesForUid(uid);
         if (packages != null && packages.length > 0) {
             for (String name : packages) {
                 permission = highestPermissionForUid(permission, name);
@@ -341,16 +325,16 @@
                 }
             }
         }
-        if (permission == mApps.get(appUid)) {
+        if (permission == mApps.get(uid)) {
             // The permissions of this UID have not changed. Nothing to do.
             return;
         } else if (permission != null) {
-            mApps.put(appUid, permission);
-            apps.put(appUid, permission);
+            mApps.put(uid, permission);
+            apps.put(uid, permission);
             update(mUsers, apps, true);
         } else {
-            mApps.remove(appUid);
-            apps.put(appUid, NETWORK);  // doesn't matter which permission we pick here
+            mApps.remove(uid);
+            apps.put(uid, NETWORK);  // doesn't matter which permission we pick here
             update(mUsers, apps, false);
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index a8f7259..3c14393 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -23,24 +23,18 @@
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
 import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
-import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
 import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
 import static android.net.ConnectivityManager.EXTRA_ERRORED_TETHER;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
-import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
-import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
-import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
-import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
-import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
 import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
 import static android.net.ConnectivityManager.TETHERING_INVALID;
 import static android.net.ConnectivityManager.TETHERING_USB;
 import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -50,6 +44,7 @@
 import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
+
 import static com.android.server.ConnectivityService.SHORT_ARG;
 
 import android.app.Notification;
@@ -60,7 +55,6 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProfile.ServiceListener;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -68,7 +62,6 @@
 import android.hardware.usb.UsbManager;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
-import android.net.ip.IpServer;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -76,7 +69,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
-import android.net.RouteInfo;
+import android.net.ip.IpServer;
 import android.net.util.InterfaceSet;
 import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
@@ -89,15 +82,12 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
-import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -113,6 +103,7 @@
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.server.LocalServices;
+import com.android.server.connectivity.tethering.EntitlementManager;
 import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
 import com.android.server.connectivity.tethering.OffloadController;
 import com.android.server.connectivity.tethering.TetheringConfiguration;
@@ -123,8 +114,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -145,18 +134,12 @@
     private final static boolean DBG = false;
     private final static boolean VDBG = false;
 
-    protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
-
     private static final Class[] messageClasses = {
             Tethering.class, TetherMasterSM.class, IpServer.class
     };
     private static final SparseArray<String> sMagicDecoderRing =
             MessageUtils.findMessageNames(messageClasses);
 
-    // {@link ComponentName} of the Service used to run tether provisioning.
-    private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
-            .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
-
     private static class TetherState {
         public final IpServer ipServer;
         public int lastState;
@@ -191,7 +174,6 @@
     private final INetworkStatsService mStatsService;
     private final INetworkPolicyManager mPolicyManager;
     private final Looper mLooper;
-    private final MockableSystemProperties mSystemProperties;
     private final StateMachine mTetherMasterSM;
     private final OffloadController mOffloadController;
     private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@@ -200,6 +182,7 @@
     private final HashSet<IpServer> mForwardedDownstreams;
     private final VersionedBroadcastListener mCarrierConfigChange;
     private final TetheringDependencies mDeps;
+    private final EntitlementManager mEntitlementMgr;
 
     private volatile TetheringConfiguration mConfig;
     private InterfaceSet mCurrentUpstreamIfaceSet;
@@ -220,7 +203,6 @@
         mStatsService = statsService;
         mPolicyManager = policyManager;
         mLooper = looper;
-        mSystemProperties = systemProperties;
         mDeps = deps;
 
         mPublicSync = new Object();
@@ -241,12 +223,13 @@
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
+        mEntitlementMgr = mDeps.getEntitlementManager(mContext, mLog, systemProperties);
         mCarrierConfigChange = new VersionedBroadcastListener(
                 "CarrierConfigChangeListener", mContext, smHandler, filter,
                 (Intent ignored) -> {
                     mLog.log("OBSERVED carrier config change");
                     updateConfiguration();
-                    reevaluateSimCardProvisioning();
+                    mEntitlementMgr.reevaluateSimCardProvisioning();
                 });
 
         mStateReceiver = new StateReceiver();
@@ -289,6 +272,7 @@
     private void updateConfiguration() {
         mConfig = new TetheringConfiguration(mContext, mLog);
         mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
+        mEntitlementMgr.updateConfiguration(mConfig);
     }
 
     private void maybeUpdateConfiguration() {
@@ -354,83 +338,54 @@
     }
 
     public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
-        if (!isTetherProvisioningRequired()) {
+        mEntitlementMgr.startTethering(type);
+        if (!mEntitlementMgr.isTetherProvisioningRequired()) {
             enableTetheringInternal(type, true, receiver);
             return;
         }
 
+        final ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
         if (showProvisioningUi) {
-            runUiTetherProvisioningAndEnable(type, receiver);
+            mEntitlementMgr.runUiTetherProvisioningAndEnable(type, proxyReceiver);
         } else {
-            runSilentTetherProvisioningAndEnable(type, receiver);
+            mEntitlementMgr.runSilentTetherProvisioningAndEnable(type, proxyReceiver);
         }
     }
 
     public void stopTethering(int type) {
         enableTetheringInternal(type, false, null);
-        if (isTetherProvisioningRequired()) {
-            cancelTetherProvisioningRechecks(type);
+        mEntitlementMgr.stopTethering(type);
+        if (mEntitlementMgr.isTetherProvisioningRequired()) {
+            // There are lurking bugs where the notion of "provisioning required" or
+            // "tethering supported" may change without notifying tethering properly, then
+            // tethering can't shutdown correctly.
+            // TODO: cancel re-check all the time
+            if (mDeps.isTetheringSupported()) {
+                mEntitlementMgr.cancelTetherProvisioningRechecks(type);
+            }
         }
     }
 
     /**
-     * Check if the device requires a provisioning check in order to enable tethering.
-     *
-     * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
-     */
-    @VisibleForTesting
-    protected boolean isTetherProvisioningRequired() {
-        final TetheringConfiguration cfg = mConfig;
-        if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
-                || cfg.provisioningApp.length == 0) {
-            return false;
-        }
-        if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
-            return false;
-        }
-        return (cfg.provisioningApp.length == 2);
-    }
-
-    // The logic here is aimed solely at confirming that a CarrierConfig exists
-    // and affirms that entitlement checks are not required.
-    //
-    // TODO: find a better way to express this, or alter the checking process
-    // entirely so that this is more intuitive.
-    private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
-        // Check carrier config for entitlement checks
-        final CarrierConfigManager configManager = (CarrierConfigManager) mContext
-             .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager == null) return false;
-
-        final PersistableBundle carrierConfig = configManager.getConfig();
-        if (carrierConfig == null) return false;
-
-        // A CarrierConfigManager was found and it has a config.
-        final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
-        return !isEntitlementCheckRequired;
-    }
-
-    /**
      * Enables or disables tethering for the given type. This should only be called once
      * provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
      * for the specified interface.
      */
     private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
-        boolean isProvisioningRequired = enable && isTetherProvisioningRequired();
+        boolean isProvisioningRequired = enable && mEntitlementMgr.isTetherProvisioningRequired();
         int result;
         switch (type) {
             case TETHERING_WIFI:
                 result = setWifiTethering(enable);
                 if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
-                    scheduleProvisioningRechecks(type);
+                    mEntitlementMgr.scheduleProvisioningRechecks(type);
                 }
                 sendTetherResult(receiver, result);
                 break;
             case TETHERING_USB:
                 result = setUsbTethering(enable);
                 if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
-                    scheduleProvisioningRechecks(type);
+                    mEntitlementMgr.scheduleProvisioningRechecks(type);
                 }
                 sendTetherResult(receiver, result);
                 break;
@@ -489,32 +444,14 @@
                         ? TETHER_ERROR_NO_ERROR
                         : TETHER_ERROR_MASTER_ERROR;
                 sendTetherResult(receiver, result);
-                if (enable && isTetherProvisioningRequired()) {
-                    scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
+                if (enable && mEntitlementMgr.isTetherProvisioningRequired()) {
+                    mEntitlementMgr.scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
                 }
                 adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
             }
         }, BluetoothProfile.PAN);
     }
 
-    private void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
-        ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
-        sendUiTetherProvisionIntent(type, proxyReceiver);
-    }
-
-    private void sendUiTetherProvisionIntent(int type, ResultReceiver receiver) {
-        Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
-        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
-        intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
     /**
      * Creates a proxy {@link ResultReceiver} which enables tethering if the provisioning result
      * is successful before firing back up to the wrapped receiver.
@@ -546,62 +483,6 @@
         return receiverForSending;
     }
 
-    private void scheduleProvisioningRechecks(int type) {
-        Intent intent = new Intent();
-        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
-        intent.putExtra(EXTRA_SET_ALARM, true);
-        intent.setComponent(TETHER_SERVICE);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    private void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
-        ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
-        sendSilentTetherProvisionIntent(type, proxyReceiver);
-    }
-
-    private void sendSilentTetherProvisionIntent(int type, ResultReceiver receiver) {
-        Intent intent = new Intent();
-        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
-        intent.putExtra(EXTRA_RUN_PROVISION, true);
-        intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
-        intent.setComponent(TETHER_SERVICE);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    private void cancelTetherProvisioningRechecks(int type) {
-        if (mDeps.isTetheringSupported()) {
-            Intent intent = new Intent();
-            intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
-            intent.setComponent(TETHER_SERVICE);
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mContext.startServiceAsUser(intent, UserHandle.CURRENT);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    // Used by the SIM card change observation code.
-    // TODO: De-duplicate with above code, where possible.
-    private void startProvisionIntent(int tetherType) {
-        final Intent startProvIntent = new Intent();
-        startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
-        startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
-        startProvIntent.setComponent(TETHER_SERVICE);
-        mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
-    }
-
     public int tether(String iface) {
         return tether(iface, IpServer.STATE_TETHERED);
     }
@@ -1166,30 +1047,6 @@
         return false;
     }
 
-    private void reevaluateSimCardProvisioning() {
-        if (!mConfig.hasMobileHotspotProvisionApp()) return;
-        if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
-
-        ArrayList<Integer> tethered = new ArrayList<>();
-        synchronized (mPublicSync) {
-            for (int i = 0; i < mTetherStates.size(); i++) {
-                TetherState tetherState = mTetherStates.valueAt(i);
-                if (tetherState.lastState != IpServer.STATE_TETHERED) {
-                    continue;  // Skip interfaces that aren't tethered.
-                }
-                String iface = mTetherStates.keyAt(i);
-                int interfaceType = ifaceNameToType(iface);
-                if (interfaceType != TETHERING_INVALID) {
-                    tethered.add(interfaceType);
-                }
-            }
-        }
-
-        for (int tetherType : tethered) {
-            startProvisionIntent(tetherType);
-        }
-    }
-
     class TetherMasterSM extends StateMachine {
         private static final int BASE_MASTER                    = Protocol.BASE_TETHERING;
         // an interface SM has requested Tethering/Local Hotspot
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
new file mode 100644
index 0000000..a4e3e1d
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -0,0 +1,224 @@
+/*
+ * 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.connectivity.tethering;
+
+import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
+import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
+import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
+
+import static com.android.internal.R.string.config_wifi_tether_enable;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.util.SharedLog;
+import android.os.Binder;
+import android.os.PersistableBundle;
+import android.os.ResultReceiver;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.MockableSystemProperties;
+
+/**
+ * This class encapsulates entitlement/provisioning mechanics
+ * provisioning check only applies to the use of the mobile network as an upstream
+ *
+ * @hide
+ */
+public class EntitlementManager {
+    private static final String TAG = EntitlementManager.class.getSimpleName();
+
+    // {@link ComponentName} of the Service used to run tether provisioning.
+    private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
+            Resources.getSystem().getString(config_wifi_tether_enable));
+    protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
+
+    // The ArraySet contains enabled downstream types, ex:
+    // {@link ConnectivityManager.TETHERING_WIFI}
+    // {@link ConnectivityManager.TETHERING_USB}
+    // {@link ConnectivityManager.TETHERING_BLUETOOTH}
+    @GuardedBy("mCurrentTethers")
+    private final ArraySet<Integer> mCurrentTethers;
+    private final Context mContext;
+    private final MockableSystemProperties mSystemProperties;
+    private final SharedLog mLog;
+    @Nullable
+    private TetheringConfiguration mConfig;
+
+    public EntitlementManager(Context ctx, SharedLog log,
+            MockableSystemProperties systemProperties) {
+        mContext = ctx;
+        mLog = log;
+        mCurrentTethers = new ArraySet<Integer>();
+        mSystemProperties = systemProperties;
+    }
+
+    /**
+     * Pass a new TetheringConfiguration instance each time when
+     * Tethering#updateConfiguration() is called.
+     */
+    public void updateConfiguration(TetheringConfiguration conf) {
+        mConfig = conf;
+    }
+
+    /**
+     * Tell EntitlementManager that a given type of tethering has been enabled
+     *
+     * @param type Tethering type
+     */
+    public void startTethering(int type) {
+        synchronized (mCurrentTethers) {
+            mCurrentTethers.add(type);
+        }
+    }
+
+    /**
+     * Tell EntitlementManager that a given type of tethering has been disabled
+     *
+     * @param type Tethering type
+     */
+    public void stopTethering(int type) {
+        synchronized (mCurrentTethers) {
+            mCurrentTethers.remove(type);
+        }
+    }
+
+    /**
+     * Check if the device requires a provisioning check in order to enable tethering.
+     *
+     * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
+     */
+    @VisibleForTesting
+    public boolean isTetherProvisioningRequired() {
+        if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
+                || mConfig.provisioningApp.length == 0) {
+            return false;
+        }
+        if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
+            return false;
+        }
+        return (mConfig.provisioningApp.length == 2);
+    }
+
+    /**
+     * Re-check tethering provisioning for enabled downstream tether types.
+     * Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
+     */
+    public void reevaluateSimCardProvisioning() {
+        if (!mConfig.hasMobileHotspotProvisionApp()) return;
+        if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
+
+        final ArraySet<Integer> reevaluateType;
+        synchronized (mCurrentTethers) {
+            reevaluateType = new ArraySet<Integer>(mCurrentTethers);
+        }
+        for (Integer type : reevaluateType) {
+            startProvisionIntent(type);
+        }
+    }
+
+    // The logic here is aimed solely at confirming that a CarrierConfig exists
+    // and affirms that entitlement checks are not required.
+    //
+    // TODO: find a better way to express this, or alter the checking process
+    // entirely so that this is more intuitive.
+    private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
+        // Check carrier config for entitlement checks
+        final CarrierConfigManager configManager = (CarrierConfigManager) mContext
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager == null) return false;
+
+        final PersistableBundle carrierConfig = configManager.getConfig();
+        if (carrierConfig == null) return false;
+
+        // A CarrierConfigManager was found and it has a config.
+        final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
+        return !isEntitlementCheckRequired;
+    }
+
+    public void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(EXTRA_RUN_PROVISION, true);
+        intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
+        intent.setComponent(TETHER_SERVICE);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+        Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
+        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    // Used by the SIM card change observation code.
+    // TODO: De-duplicate with above code, where possible.
+    private void startProvisionIntent(int tetherType) {
+        final Intent startProvIntent = new Intent();
+        startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
+        startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
+        startProvIntent.setComponent(TETHER_SERVICE);
+        mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
+    }
+
+    public void scheduleProvisioningRechecks(int type) {
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(EXTRA_SET_ALARM, true);
+        intent.setComponent(TETHER_SERVICE);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    public void cancelTetherProvisioningRechecks(int type) {
+        Intent intent = new Intent();
+        intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
+        intent.setComponent(TETHER_SERVICE);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 8b40069..d56b167 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -17,19 +17,13 @@
 package com.android.server.connectivity.tethering;
 
 import android.content.Context;
-import android.net.INetd;
 import android.net.NetworkRequest;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
 import android.net.ip.IpServer;
-import android.net.ip.RouterAdvertisementDaemon;
-import android.net.util.InterfaceParams;
-import android.net.util.NetdService;
-import android.os.Handler;
 import android.net.util.SharedLog;
-import android.os.Looper;
+import android.os.Handler;
 
 import com.android.internal.util.StateMachine;
+import com.android.server.connectivity.MockableSystemProperties;
 
 import java.util.ArrayList;
 
@@ -65,4 +59,9 @@
     public NetworkRequest getDefaultNetworkRequest() {
         return null;
     }
+
+    public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
+            MockableSystemProperties systemProperties) {
+        return new EntitlementManager(ctx, log, systemProperties);
+    }
 }
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 33525fd..f2c539c 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -16,16 +16,7 @@
 
 package com.android.server.display;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-
 import android.content.Context;
-import android.graphics.PixelFormat;
 import android.graphics.SurfaceTexture;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
@@ -34,20 +25,29 @@
 import android.opengl.EGLContext;
 import android.opengl.EGLDisplay;
 import android.opengl.EGLSurface;
-import android.opengl.GLES20;
 import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
 import android.util.Slog;
 import android.view.DisplayInfo;
-import android.view.Surface.OutOfResourcesException;
 import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
 
-import libcore.io.Streams;
-
 import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
 
+import libcore.io.Streams;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
 /**
  * <p>
  * Animates a screen transition from on to off or off to on by applying
@@ -569,37 +569,31 @@
             mSurfaceSession = new SurfaceSession();
         }
 
-        SurfaceControl.openTransaction();
-        try {
-            if (mSurfaceControl == null) {
-                try {
-                    int flags;
-                    if (mMode == MODE_FADE) {
-                        flags = SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN;
-                    } else {
-                        flags = SurfaceControl.OPAQUE | SurfaceControl.HIDDEN;
-                    }
-                    mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
-                            .setName("ColorFade")
-                            .setSize(mDisplayWidth, mDisplayHeight)
-                            .setFlags(flags)
-                            .build();
-                } catch (OutOfResourcesException ex) {
-                    Slog.e(TAG, "Unable to create surface.", ex);
-                    return false;
+        if (mSurfaceControl == null) {
+            Transaction t = new Transaction();
+            try {
+                final SurfaceControl.Builder builder =
+                        new SurfaceControl.Builder(mSurfaceSession).setName("ColorFade");
+                if (mMode == MODE_FADE) {
+                    builder.setColorLayer(true);
+                } else {
+                    builder.setBufferSize(mDisplayWidth, mDisplayHeight);
                 }
-
-                mSurfaceControl.setLayerStack(mDisplayLayerStack);
-                mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight);
-                mSurface = new Surface();
-                mSurface.copyFrom(mSurfaceControl);
-
-                mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
-                        mDisplayId, mSurfaceControl);
-                mSurfaceLayout.onDisplayTransaction();
+                mSurfaceControl = builder.build();
+            } catch (OutOfResourcesException ex) {
+                Slog.e(TAG, "Unable to create surface.", ex);
+                return false;
             }
-        } finally {
-            SurfaceControl.closeTransaction();
+
+            t.setLayerStack(mSurfaceControl, mDisplayLayerStack);
+            t.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+            mSurface = new Surface();
+            mSurface.copyFrom(mSurfaceControl);
+
+            mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
+                    mDisplayId, mSurfaceControl);
+            mSurfaceLayout.onDisplayTransaction(t);
+            t.apply();
         }
         return true;
     }
@@ -746,7 +740,7 @@
         }
 
         @Override
-        public void onDisplayTransaction() {
+        public void onDisplayTransaction(Transaction t) {
             synchronized (this) {
                 if (mSurfaceControl == null) {
                     return;
@@ -755,21 +749,21 @@
                 DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
                 switch (displayInfo.rotation) {
                     case Surface.ROTATION_0:
-                        mSurfaceControl.setPosition(0, 0);
-                        mSurfaceControl.setMatrix(1, 0, 0, 1);
+                        t.setPosition(mSurfaceControl, 0, 0);
+                        t.setMatrix(mSurfaceControl, 1, 0, 0, 1);
                         break;
                     case Surface.ROTATION_90:
-                        mSurfaceControl.setPosition(0, displayInfo.logicalHeight);
-                        mSurfaceControl.setMatrix(0, -1, 1, 0);
+                        t.setPosition(mSurfaceControl, 0, displayInfo.logicalHeight);
+                        t.setMatrix(mSurfaceControl, 0, -1, 1, 0);
                         break;
                     case Surface.ROTATION_180:
-                        mSurfaceControl.setPosition(displayInfo.logicalWidth,
+                        t.setPosition(mSurfaceControl, displayInfo.logicalWidth,
                                 displayInfo.logicalHeight);
-                        mSurfaceControl.setMatrix(-1, 0, 0, -1);
+                        t.setMatrix(mSurfaceControl, -1, 0, 0, -1);
                         break;
                     case Surface.ROTATION_270:
-                        mSurfaceControl.setPosition(displayInfo.logicalWidth, 0);
-                        mSurfaceControl.setMatrix(0, 1, -1, 0);
+                        t.setPosition(mSurfaceControl, displayInfo.logicalWidth, 0);
+                        t.setMatrix(mSurfaceControl, 0, 1, -1, 0);
                         break;
                 }
             }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e70460a..360a7d1 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -247,9 +247,6 @@
     // device).
     private Point mStableDisplaySize = new Point();
 
-    // Whether the system has finished booting or not.
-    private boolean mSystemReady;
-
     // The top inset of the default display.
     // This gets persisted so that the boot animation knows how to transition from the display's
     // full size to the size configured by the user. Right now we only persist and animate the top
@@ -322,8 +319,6 @@
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
         mCurrentUserId = UserHandle.USER_SYSTEM;
-
-        mSystemReady = false;
     }
 
     public void setupSchedulerPolicies() {
@@ -413,10 +408,6 @@
         synchronized (mSyncRoot) {
             mSafeMode = safeMode;
             mOnlyCore = onlyCore;
-            mSystemReady = true;
-            // Just in case the top inset changed before the system was ready. At this point, any
-            // relevant configuration should be in place.
-            recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY));
         }
 
         mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
@@ -506,7 +497,7 @@
 
         // List is self-synchronized copy-on-write.
         for (DisplayTransactionListener listener : mDisplayTransactionListeners) {
-            listener.onDisplayTransaction();
+            listener.onDisplayTransaction(t);
         }
     }
 
@@ -1065,10 +1056,7 @@
     }
 
     private void recordTopInsetLocked(@Nullable LogicalDisplay d) {
-        // We must only persist the inset after boot has completed, otherwise we will end up
-        // overwriting the persisted value before the masking flag has been loaded from the
-        // resource overlay.
-        if (!mSystemReady || d == null) {
+        if (d == null) {
             return;
         }
         int topInset = d.getInsets().top;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 6f726e6..9566598 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -57,8 +57,6 @@
  * </p>
  */
 final class LogicalDisplay {
-    private static final String PROP_MASKING_INSET_TOP = "persist.sys.displayinset.top";
-
     private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
 
     // The layer stack we use when the display has been blanked to prevent any
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 3a31c9c..3339a49 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1443,25 +1443,10 @@
         }
     }
 
-    public void setInputWindows(InputWindowHandle[] windowHandles, int displayId) {
-        nativeSetInputWindows(mPtr, windowHandles, displayId);
-    }
-
     public void setFocusedApplication(int displayId, InputApplicationHandle application) {
         nativeSetFocusedApplication(mPtr, displayId, application);
     }
 
-    public void setFocusedWindow(InputWindowHandle focusedWindowHandle) {
-        final IWindow newFocusedWindow =
-            focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
-        if (mFocusedWindow != newFocusedWindow) {
-            if (mFocusedWindowHasCapture) {
-                setPointerCapture(false);
-            }
-            mFocusedWindow = newFocusedWindow;
-        }
-    }
-
     public void setFocusedDisplay(int displayId) {
         nativeSetFocusedDisplay(mPtr, displayId);
     }
@@ -1799,11 +1784,22 @@
         mWindowManagerCallbacks.notifyInputChannelBroken(token);
     }
 
+    // Native callback
+    private void notifyFocusChanged(IBinder token) {
+        if (mFocusedWindow != token) {
+            if (mFocusedWindowHasCapture) {
+                setPointerCapture(false);
+            }
+            if (token instanceof IWindow) {
+                mFocusedWindow = (IWindow) token;
+            }
+        }
+    }
+
     // Native callback.
-    private long notifyANR(InputApplicationHandle inputApplicationHandle,
-            IBinder token, String reason) {
+    private long notifyANR(IBinder token, String reason) {
         return mWindowManagerCallbacks.notifyANR(
-                inputApplicationHandle, token, reason);
+                token, reason);
     }
 
     // Native callback.
@@ -1834,14 +1830,12 @@
     }
 
     // Native callback.
-    private long interceptKeyBeforeDispatching(IBinder focus,
-            KeyEvent event, int policyFlags) {
+    private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
         return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
     }
 
     // Native callback.
-    private KeyEvent dispatchUnhandledKey(IBinder focus,
-            KeyEvent event, int policyFlags) {
+    private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) {
         return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
     }
 
@@ -1993,8 +1987,7 @@
 
         public void notifyInputChannelBroken(IBinder token);
 
-        public long notifyANR(InputApplicationHandle inputApplicationHandle,
-                IBinder token, String reason);
+        public long notifyANR(IBinder token, String reason);
 
         public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1dfb86a..a8da968 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2420,8 +2420,9 @@
                         .setContentText(summary)
                         .setContentIntent(mImeSwitchPendingIntent);
                 try {
+                    // TODO(b/120076400): Figure out what is the best behavior
                     if ((mNotificationManager != null)
-                            && !mIWindowManager.hasNavigationBar()) {
+                            && !mIWindowManager.hasNavigationBar(DEFAULT_DISPLAY)) {
                         if (DEBUG) {
                             Slog.d(TAG, "--- show notification: label =  " + summary);
                         }
diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
index aac83b6..6fe6324 100644
--- a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
+++ b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
@@ -19,6 +19,8 @@
 import android.annotation.UserIdInt;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
 
 /**
  * Intelligence Manager local system service interface.
@@ -41,4 +43,37 @@
      */
     public abstract boolean sendActivityAssistData(@UserIdInt int userId,
             @NonNull IBinder activityToken, @NonNull Bundle data);
+
+    /**
+     * Asks the intelligence service to provide Augmented Autofill for a given activity.
+     *
+     * @param userId user handle
+     * @param client binder used to communicate with the activity that originated this request.
+     * @param activityToken activity that originated this request.
+     * @param autofillSessionId autofill session id (must be used on {@code client} calls.
+     * @param focusedId id of the the field that triggered this request.
+     *
+     * @return {@code false} if the service cannot handle this request, {@code true} otherwise.
+     * <b>NOTE: </b> it must return right away; typically it will return {@code false} if the
+     * service is disabled (or the activity blacklisted).
+     */
+    public abstract AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
+            @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
+            int autofillSessionId, @NonNull AutofillId focusedId);
+
+    /**
+     * Callback used by the Autofill Session to communicate with the Augmented Autofill service.
+     */
+    public interface AugmentedAutofillCallback {
+        // TODO(b/111330312): this method is calling when the Autofill session is destroyed, the
+        // main reason being the cases where user tap HOME.
+        // Right now it's completely destroying the UI, but we need to decide whether / how to
+        // properly recover it later (for example, if the user switches back to the activity,
+        // should it be restored? Right not it kind of is, because Autofill's Session trigger a
+        // new FillRequest, which in turn triggers the Augmented Autofill request again)
+        /**
+         * Destroys the Autofill UI.
+         */
+        void destroy();
+    }
 }
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 9e6e381..d5e4681 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -97,8 +97,8 @@
  *
  * {@hide}
  */
-public class GnssLocationProvider implements LocationProviderInterface, InjectNtpTimeCallback,
-        GnssSatelliteBlacklistCallback {
+public class GnssLocationProvider extends LocationProviderInterface
+        implements InjectNtpTimeCallback, GnssSatelliteBlacklistCallback {
 
     private static final String TAG = "GnssLocationProvider";
 
diff --git a/services/core/java/com/android/server/location/LocationProviderInterface.java b/services/core/java/com/android/server/location/LocationProviderInterface.java
index 6f09232..6785964 100644
--- a/services/core/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/core/java/com/android/server/location/LocationProviderInterface.java
@@ -16,33 +16,63 @@
 
 package com.android.server.location;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
+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 android.os.Bundle;
-import android.os.WorkSource;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 /**
  * Location Manager's interface for location providers.
  * @hide
  */
-public interface LocationProviderInterface {
-    public String getName();
+public abstract class LocationProviderInterface {
 
-    public void enable();
-    public void disable();
-    public boolean isEnabled();
-    public void setRequest(ProviderRequest request, WorkSource source);
+    /** Get name. */
+    public abstract String getName();
 
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+    /** Enable. */
+    public abstract void enable();
 
-    // --- deprecated (but still supported) ---
-    public ProviderProperties getProperties();
-    public int getStatus(Bundle extras);
-    public long getStatusUpdateTime();
-    public boolean sendExtraCommand(String command, Bundle extras);
+    /** 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 bb86b48..b408414 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -41,7 +41,7 @@
 /**
  * Proxy for ILocationProvider implementations.
  */
-public class LocationProviderProxy implements LocationProviderInterface {
+public class LocationProviderProxy extends LocationProviderInterface {
     private static final String TAG = "LocationProviderProxy";
     private static final boolean D = LocationManagerService.D;
 
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 8578761..145aee3 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -25,31 +25,31 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
 /**
  * A mock location provider used by LocationManagerService to implement test providers.
  *
  * {@hide}
  */
-public class MockProvider implements LocationProviderInterface {
+public class MockProvider extends LocationProviderInterface {
     private final String mName;
     private final ProviderProperties mProperties;
     private final ILocationManager mLocationManager;
 
     private final Location mLocation;
-    private final Bundle mExtras = new Bundle();
+
+    private boolean mHasLocation;
+    private boolean mEnabled;
+
 
     private int mStatus;
     private long mStatusUpdateTime;
-    private boolean mHasLocation;
-    private boolean mHasStatus;
-    private boolean mEnabled;
+    private Bundle mExtras;
 
     private static final String TAG = "MockProvider";
 
@@ -61,6 +61,10 @@
         mLocationManager = locationManager;
         mProperties = properties;
         mLocation = new Location(name);
+
+        mStatus = LocationProvider.AVAILABLE;
+        mStatusUpdateTime = 0L;
+        mExtras = null;
     }
 
     @Override
@@ -90,13 +94,12 @@
 
     @Override
     public int getStatus(Bundle extras) {
-        if (mHasStatus) {
+        if (mExtras != null) {
             extras.clear();
             extras.putAll(mExtras);
-            return mStatus;
-        } else {
-            return LocationProvider.AVAILABLE;
         }
+
+        return mStatus;
     }
 
     @Override
@@ -120,19 +123,14 @@
         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.clear();
-        if (extras != null) {
-            mExtras.putAll(extras);
-        }
-        mHasStatus = true;
-    }
-
-    public void clearStatus() {
-        mHasStatus = false;
-        mStatusUpdateTime = 0;
+        mExtras = extras;
     }
 
     @Override
@@ -145,9 +143,6 @@
         pw.println(prefix + "mHasLocation=" + mHasLocation);
         pw.println(prefix + "mLocation:");
         mLocation.dump(new PrintWriterPrinter(pw), prefix + "  ");
-        pw.println(prefix + "mHasStatus=" + mHasStatus);
-        pw.println(prefix + "mStatus=" + mStatus);
-        pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime);
         pw.println(prefix + "mExtras=" + mExtras);
     }
 
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 71bae07..99c9214 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -16,22 +16,20 @@
 
 package com.android.server.location;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
 import android.location.Criteria;
 import android.location.ILocationManager;
 import android.location.Location;
 import android.location.LocationManager;
-import android.location.LocationProvider;
 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;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 /**
  * A passive location provider reports locations received from other providers
@@ -40,7 +38,7 @@
  *
  * {@hide}
  */
-public class PassiveProvider implements LocationProviderInterface {
+public class PassiveProvider extends LocationProviderInterface {
     private static final String TAG = "PassiveProvider";
 
     private static final ProviderProperties PROPERTIES = new ProviderProperties(
@@ -78,20 +76,6 @@
     }
 
     @Override
-    public int getStatus(Bundle extras) {
-        if (mReportLocation) {
-            return LocationProvider.AVAILABLE;
-        } else {
-            return LocationProvider.TEMPORARILY_UNAVAILABLE;
-        }
-    }
-
-    @Override
-    public long getStatusUpdateTime() {
-        return -1;
-    }
-
-    @Override
     public void setRequest(ProviderRequest request, WorkSource source) {
         mReportLocation = request.reportLocation;
     }
diff --git a/services/core/java/com/android/server/location/gps_debug.conf b/services/core/java/com/android/server/location/gps_debug.conf
new file mode 100644
index 0000000..34ce96f
--- /dev/null
+++ b/services/core/java/com/android/server/location/gps_debug.conf
@@ -0,0 +1,52 @@
+# Sample file for use for on device debug override only
+# Prefer frameworks/base/core/res/res/values/config.xml and
+# frameworks/base/core/res/res/values-mcc*-mnc*/config.xml
+
+################################
+##### AGPS server settings #####
+################################
+# FOR SUPL SUPPORT, set the following
+# SUPL_HOST=supl.google.com or IP
+# SUPL_PORT=7275
+
+# supl version 2.0
+# SUPL_VER=0x20000
+
+#SUPL_MODE is a bit mask set in config.xml per carrier by default.
+#If it is uncommented here, this value will overwrite the value from
+#config.xml.
+#MSA=0X2
+#MSB=0X1
+#SUPL_MODE=1
+
+# Emergency SUPL, 1=enable, 0=disable
+#SUPL_ES=0
+
+#Choose PDN for Emergency SUPL
+#1 - Use emergency PDN
+#0 - Use regular SUPL PDN for Emergency SUPL
+#USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=0
+
+####################################
+#  LTE Positioning Profile Settings
+####################################
+# 0: Enable RRLP on LTE(Default)
+# 1: Enable LPP_User_Plane on LTE
+# 2: Enable LPP_Control_Plane
+# 3: Enable both LPP_User_Plane and LPP_Control_Plane
+#LPP_PROFILE = 2
+
+##################################################
+# Select Positioning Protocol on A-GLONASS system
+##################################################
+# 0x1: RRC CPlane
+# 0x2: RRLP UPlane
+# 0x4: LLP Uplane
+#A_GLONASS_POS_PROTOCOL_SELECT = 0
+
+# Below bit mask configures how GPS functionalities
+# should be locked when user turns off GPS on Settings
+# Set bit 0x1 if MO GPS functionalities are to be locked
+# Set bit 0x2 if NI GPS functionalities are to be locked
+# default - non is locked for backward compatibility
+#GPS_LOCK = 0
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 4f4b6bf..4bd8f45 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -64,6 +64,7 @@
     private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11;
     private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12;
     private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13;
+    private static final int EVENT_APP_IDLE_WL_CHANGED = 14;
 
     static final int NTWK_BLOCKED_POWER = 0;
     static final int NTWK_ALLOWED_NON_METERED = 1;
@@ -145,6 +146,13 @@
         }
     }
 
+    void appIdleWlChanged(int uid, boolean isWhitelisted) {
+        synchronized (mLock) {
+            if (LOGD) Slog.d(TAG, getAppIdleWlChangedLog(uid, isWhitelisted));
+            mEventsBuffer.appIdleWlChanged(uid, isWhitelisted);
+        }
+    }
+
     void paroleStateChanged(boolean paroleOn) {
         synchronized (mLock) {
             if (LOGD) Slog.d(TAG, getParoleStateChanged(paroleOn));
@@ -259,6 +267,10 @@
         return "App idle state of uid " + uid + ": " + idle;
     }
 
+    private static String getAppIdleWlChangedLog(int uid, boolean isWhitelisted) {
+        return "App idle whitelist state of uid " + uid + ": " + isWhitelisted;
+    }
+
     private static String getParoleStateChanged(boolean paroleOn) {
         return "Parole state: " + paroleOn;
     }
@@ -409,6 +421,17 @@
             data.timeStamp = System.currentTimeMillis();
         }
 
+        public void appIdleWlChanged(int uid, boolean isWhitelisted) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_APP_IDLE_WL_CHANGED;
+            data.ifield1 = uid;
+            data.bfield1 = isWhitelisted;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
         public void paroleStateChanged(boolean paroleOn) {
             final Data data = getNextSlot();
             if (data == null) return;
@@ -487,6 +510,8 @@
                     return getDeviceIdleModeEnabled(data.bfield1);
                 case EVENT_APP_IDLE_STATE_CHANGED:
                     return getAppIdleChangedLog(data.ifield1, data.bfield1);
+                case EVENT_APP_IDLE_WL_CHANGED:
+                    return getAppIdleWlChangedLog(data.ifield1, data.bfield1);
                 case EVENT_PAROLE_STATE_CHANGED:
                     return getParoleStateChanged(data.bfield1);
                 case EVENT_TEMP_POWER_SAVE_WL_CHANGED:
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 099671d..7f650ee 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -105,6 +105,12 @@
     public abstract void onAdminDataAvailable();
 
     /**
+     * Control if a UID should be whitelisted even if it's in app idle mode. Other restrictions may
+     * still be in effect.
+     */
+    public abstract void setAppIdleWhitelist(int uid, boolean shouldWhitelist);
+
+    /**
      * Sets a list of packages which are restricted by admin from accessing metered data.
      *
      * @param packageNames the list of restricted packages.
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index d799642..7750c37 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -464,6 +464,10 @@
     @GuardedBy("mUidRulesFirstLock")
     final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
 
+    // "Power save mode" is the concept used in the DeviceIdleController that includes various
+    // features including Doze and Battery Saver. It include Battery Saver, but "power save mode"
+    // and "battery saver" are not equivalent.
+
     /**
      * UIDs that have been white-listed to always be able to have network access
      * in power save mode, except device idle (doze) still applies.
@@ -484,6 +488,13 @@
     private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray();
 
     /**
+     * UIDs that have been white-listed temporarily to be able to have network access despite being
+     * idle. Other power saving restrictions still apply.
+     */
+    @GuardedBy("mUidRulesFirstLock")
+    private final SparseBooleanArray mAppIdleTempWhitelistAppIds = new SparseBooleanArray();
+
+    /**
      * UIDs that have been initially white-listed by system to avoid restricted background.
      */
     @GuardedBy("mUidRulesFirstLock")
@@ -3372,6 +3383,20 @@
                     fout.decreaseIndent();
                 }
 
+                size = mAppIdleTempWhitelistAppIds.size();
+                if (size > 0) {
+                    fout.println("App idle whitelist app ids:");
+                    fout.increaseIndent();
+                    for (int i = 0; i < size; i++) {
+                        fout.print("UID=");
+                        fout.print(mAppIdleTempWhitelistAppIds.keyAt(i));
+                        fout.print(": ");
+                        fout.print(mAppIdleTempWhitelistAppIds.valueAt(i));
+                        fout.println();
+                    }
+                    fout.decreaseIndent();
+                }
+
                 size = mDefaultRestrictBackgroundWhitelistUids.size();
                 if (size > 0) {
                     fout.println("Default restrict background whitelist uids:");
@@ -3640,12 +3665,15 @@
     }
 
     /**
+     * Returns whether a uid is whitelisted from power saving restrictions (eg: Battery Saver, Doze
+     * mode, and app idle).
+     *
      * @param deviceIdleMode if true then we don't consider
      *        {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is
      *        whitelisted.
      */
     @GuardedBy("mUidRulesFirstLock")
-    private boolean isWhitelistedBatterySaverUL(int uid, boolean deviceIdleMode) {
+    private boolean isWhitelistedFromPowerSaveUL(int uid, boolean deviceIdleMode) {
         final int appId = UserHandle.getAppId(uid);
         boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
                 || mPowerSaveWhitelistAppIds.get(appId);
@@ -3660,7 +3688,7 @@
     @GuardedBy("mUidRulesFirstLock")
     private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) {
         if (enabled) {
-            final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid,
+            final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid,
                     chain == FIREWALL_CHAIN_DOZABLE);
             if (isWhitelisted || isUidForegroundOnRestrictPowerUL(uid)) {
                 setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW);
@@ -3712,8 +3740,10 @@
             if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
                     && !isUidForegroundOnRestrictPowerUL(uid)) {
                 setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
+                if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid);
             } else {
                 setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
+                if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL " + uid + " to DEFAULT");
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -3896,7 +3926,59 @@
         return UserHandle.isApp(uid) && hasInternetPermissions(uid);
     }
 
-    private boolean isUidIdle(int uid) {
+    /**
+     * Set whether or not an app should be whitelisted for network access while in app idle. Other
+     * power saving restrictions may still apply.
+     */
+    @VisibleForTesting
+    public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+        synchronized (mUidRulesFirstLock) {
+            if (mAppIdleTempWhitelistAppIds.get(uid) == shouldWhitelist) {
+                // No change.
+                return;
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mLogger.appIdleWlChanged(uid, shouldWhitelist);
+                if (shouldWhitelist) {
+                    mAppIdleTempWhitelistAppIds.put(uid, true);
+                } else {
+                    mAppIdleTempWhitelistAppIds.delete(uid);
+                }
+                updateRuleForAppIdleUL(uid);
+                updateRulesForPowerRestrictionsUL(uid);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
+    /** Return the list of UIDs currently in the app idle whitelist. */
+    @VisibleForTesting
+    public int[] getAppIdleWhitelist() {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        synchronized (mUidRulesFirstLock) {
+            final int len = mAppIdleTempWhitelistAppIds.size();
+            int[] uids = new int[len];
+            for (int i = 0; i < len; ++i) {
+                uids[i] = mAppIdleTempWhitelistAppIds.keyAt(i);
+            }
+            return uids;
+        }
+    }
+
+    /** Returns if the UID is currently considered idle. */
+    @VisibleForTesting
+    public boolean isUidIdle(int uid) {
+        synchronized (mUidRulesFirstLock) {
+            if (mAppIdleTempWhitelistAppIds.get(uid)) {
+                // UID is temporarily whitelisted.
+                return false;
+            }
+        }
+
         final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
         final int userId = UserHandle.getUserId(uid);
 
@@ -3940,6 +4022,7 @@
         mPowerSaveWhitelistExceptIdleAppIds.delete(uid);
         mPowerSaveWhitelistAppIds.delete(uid);
         mPowerSaveTempWhitelistAppIds.delete(uid);
+        mAppIdleTempWhitelistAppIds.delete(uid);
 
         // ...then update iptables asynchronously.
         mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget();
@@ -3984,7 +4067,7 @@
      * <li>@{code bw_happy_box}: UIDs added to this chain have access (whitelist), unless they're
      *     also blacklisted.
      * <li>@{code bw_data_saver}: when enabled (through {@link #setRestrictBackground(boolean)}),
-     *     no UIDs other those whitelisted will have access.
+     *     no UIDs other than those whitelisted will have access.
      * <ul>
      *
      * <p>The @{code bw_penalty_box} and @{code bw_happy_box} are primarily managed through the
@@ -4194,7 +4277,7 @@
         final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;
         final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
 
-        final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, mDeviceIdleMode);
+        final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
         final int oldRule = oldUidRules & MASK_ALL_NETWORKS;
         int newRule = RULE_NONE;
 
@@ -5023,6 +5106,11 @@
         }
 
         @Override
+        public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+            NetworkPolicyManagerService.this.setAppIdleWhitelist(uid, shouldWhitelist);
+        }
+
+        @Override
         public void setMeteredRestrictedPackages(Set<String> packageNames, int userId) {
             setMeteredRestrictedPackagesInternal(packageNames, userId);
         }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 56d41c5..156c01d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -78,6 +78,8 @@
         pw.println("    Adds a UID to the whitelist for restrict background usage.");
         pw.println("  add restrict-background-blacklist UID");
         pw.println("    Adds a UID to the blacklist for restrict background usage.");
+        pw.println("  add app-idle-whitelist UID");
+        pw.println("    Adds a UID to the temporary app idle whitelist.");
         pw.println("  get restrict-background");
         pw.println("    Gets the global restrict background usage status.");
         pw.println("  list wifi-networks [true|false]");
@@ -92,6 +94,8 @@
         pw.println("    Removes a UID from the whitelist for restrict background usage.");
         pw.println("  remove restrict-background-blacklist UID");
         pw.println("    Removes a UID from the blacklist for restrict background usage.");
+        pw.println("  remove app-idle-whitelist UID");
+        pw.println("    Removes a UID from the temporary app idle whitelist.");
         pw.println("  set metered-network ID [undefined|true|false]");
         pw.println("    Toggles whether the given wi-fi network is metered.");
         pw.println("  set restrict-background BOOLEAN");
@@ -142,6 +146,8 @@
             return -1;
         }
         switch(type) {
+            case "app-idle-whitelist":
+                return listAppIdleWhitelist();
             case "wifi-networks":
                 return listWifiNetworks();
             case "restrict-background-whitelist":
@@ -165,6 +171,8 @@
                 return addRestrictBackgroundWhitelist();
             case "restrict-background-blacklist":
                 return addRestrictBackgroundBlacklist();
+            case "app-idle-whitelist":
+                return addAppIdleWhitelist();
         }
         pw.println("Error: unknown add type '" + type + "'");
         return -1;
@@ -182,14 +190,20 @@
                 return removeRestrictBackgroundWhitelist();
             case "restrict-background-blacklist":
                 return removeRestrictBackgroundBlacklist();
+            case "app-idle-whitelist":
+                return removeAppIdleWhitelist();
         }
         pw.println("Error: unknown remove type '" + type + "'");
         return -1;
     }
 
     private int listUidPolicies(String msg, int policy) throws RemoteException {
-        final PrintWriter pw = getOutPrintWriter();
         final int[] uids = mInterface.getUidsWithPolicy(policy);
+        return listUidList(msg, uids);
+    }
+
+    private int listUidList(String msg, int[] uids) {
+        final PrintWriter pw = getOutPrintWriter();
         pw.print(msg); pw.print(": ");
         if (uids.length == 0) {
             pw.println("none");
@@ -214,6 +228,12 @@
                 POLICY_REJECT_METERED_BACKGROUND);
     }
 
+    private int listAppIdleWhitelist() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final int[] uids = mInterface.getAppIdleWhitelist();
+        return listUidList("App Idle whitelisted UIDs", uids);
+    }
+
     private int getRestrictBackground() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         pw.print("Restrict background status: ");
@@ -277,6 +297,23 @@
         return resetUidPolicy("not blacklisted", POLICY_REJECT_METERED_BACKGROUND);
     }
 
+    private int setAppIdleWhitelist(boolean isWhitelisted) {
+        final int uid = getUidFromNextArg();
+        if (uid < 0) {
+            return uid;
+        }
+        mInterface.setAppIdleWhitelist(uid, isWhitelisted);
+        return 0;
+    }
+
+    private int addAppIdleWhitelist() throws RemoteException {
+        return setAppIdleWhitelist(true);
+    }
+
+    private int removeAppIdleWhitelist() throws RemoteException {
+        return setAppIdleWhitelist(false);
+    }
+
     private int listWifiNetworks() {
         final PrintWriter pw = getOutPrintWriter();
         final String arg = getNextArg();
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index decdac6..84bb13e 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import android.app.Notification;
 import android.service.notification.NotificationStats;
 
 import com.android.internal.statusbar.NotificationVisibility;
@@ -26,7 +27,7 @@
     void onNotificationClick(int callingUid, int callingPid, String key,
             NotificationVisibility nv);
     void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex,
-            NotificationVisibility nv);
+            Notification.Action action, NotificationVisibility nv, boolean generatedByAssistant);
     void onNotificationClear(int callingUid, int callingPid,
             String pkg, String tag, int id, int userId, String key,
             @NotificationStats.DismissalSurface int dismissalSurface,
@@ -45,5 +46,15 @@
     void onNotificationDirectReplied(String key);
     void onNotificationSettingsViewed(String key);
     void onNotificationSmartRepliesAdded(String key, int replyCount);
-    void onNotificationSmartReplySent(String key, int replyIndex);
+
+    /**
+     * Notifies a smart reply is sent.
+     *
+     * @param key the notification key
+     * @param clickedIndex the index of clicked reply
+     * @param reply the reply that is sent
+     * @param generatedByAssistant specifies is the reply generated by NAS
+     */
+    void onNotificationSmartReplySent(String key, int clickedIndex, CharSequence reply,
+            boolean generatedByAssistant);
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4da29e4..ae27d0c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -246,6 +246,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
 import java.util.function.Predicate;
 
 /** {@hide} */
@@ -751,7 +752,8 @@
 
         @Override
         public void onNotificationActionClick(int callingUid, int callingPid, String key,
-                int actionIndex, NotificationVisibility nv) {
+                int actionIndex, Notification.Action action, NotificationVisibility nv,
+                boolean generatedByAssistant) {
             exitIdle();
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
@@ -771,6 +773,8 @@
                         nv.rank, nv.count);
                 nv.recycle();
                 reportUserInteraction(r);
+                mAssistants.notifyAssistantActionClicked(
+                        r.sbn, actionIndex, action, generatedByAssistant);
             }
         }
 
@@ -887,6 +891,7 @@
                     EventLogTags.writeNotificationExpansion(key,
                             userAction ? 1 : 0, expanded ? 1 : 0,
                             r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
+                    mAssistants.notifyAssistantExpansionChangedLocked(r.sbn, userAction, expanded);
                 }
             }
         }
@@ -902,6 +907,7 @@
                             .setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
                             .setType(MetricsEvent.TYPE_ACTION));
                     reportUserInteraction(r);
+                    mAssistants.notifyAssistantNotificationDirectReplyLocked(r.sbn);
                 }
             }
         }
@@ -917,7 +923,8 @@
         }
 
         @Override
-        public void onNotificationSmartReplySent(String key, int replyIndex) {
+        public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply,
+                boolean generatedByAssistant) {
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
@@ -927,6 +934,8 @@
                     mMetricsLogger.write(logMaker);
                     // Treat clicking on a smart reply as a user interaction.
                     reportUserInteraction(r);
+                    mAssistants.notifyAssistantSuggestedReplySent(
+                            r.sbn, reply, generatedByAssistant);
                 }
             }
         }
@@ -4398,19 +4407,20 @@
      *
      * Has side effects.
      */
-    private boolean checkDisqualifyingFeatures(int userId, int callingUid, int id, String tag,
+    private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
             NotificationRecord r, boolean isAutogroup) {
         final String pkg = r.sbn.getPackageName();
         final boolean isSystemNotification =
-                isUidSystemOrPhone(callingUid) || ("android".equals(pkg));
+                isUidSystemOrPhone(uid) || ("android".equals(pkg));
         final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
 
         // Limit the number of notifications that any given package except the android
         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
         if (!isSystemNotification && !isNotificationFromListener) {
             synchronized (mNotificationLock) {
+                final int callingUid = Binder.getCallingUid();
                 if (mNotificationsByKey.get(r.sbn.getKey()) == null
-                        && isCallerInstantApp(pkg, Binder.getCallingUid(), userId)) {
+                        && isCallerInstantApp(callingUid, userId)) {
                     // Ephemeral apps have some special constraints for notifications.
                     // They are not allowed to create new notifications however they are allowed to
                     // update notifications created by the system (e.g. a foreground service
@@ -4729,7 +4739,7 @@
                 mRankingHelper.extractSignals(r);
                 // tell the assistant service about the notification
                 if (mAssistants.isEnabled()) {
-                    mAssistants.onNotificationEnqueued(r);
+                    mAssistants.onNotificationEnqueuedLocked(r);
                     mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
                             DELAY_FOR_ASSISTANT_TIME);
                 } else {
@@ -6521,24 +6531,28 @@
     }
 
     @VisibleForTesting
-    boolean isCallerInstantApp(String pkg, int callingUid, int userId) {
+    boolean isCallerInstantApp(int callingUid, int userId) {
         // System is always allowed to act for ephemeral apps.
         if (isUidSystemOrPhone(callingUid)) {
             return false;
         }
 
-        mAppOps.checkPackage(callingUid, pkg);
-
         try {
+            final String[] pkgs = mPackageManager.getPackagesForUid(callingUid);
+            if (pkgs == null) {
+                throw new SecurityException("Unknown uid " + callingUid);
+            }
+            final String pkg = pkgs[0];
+            mAppOps.checkPackage(callingUid, pkg);
+
             ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0, userId);
             if (ai == null) {
                 throw new SecurityException("Unknown package " + pkg);
             }
             return ai.isInstantApp();
         } catch (RemoteException re) {
-            throw new SecurityException("Unknown package " + pkg, re);
+            throw new SecurityException("Unknown uid " + callingUid, re);
         }
-
     }
 
     private void checkCallerIsSameApp(String pkg) {
@@ -6842,38 +6856,96 @@
             }
         }
 
-        public void onNotificationEnqueued(final NotificationRecord r) {
+        @GuardedBy("mNotificationLock")
+        private void onNotificationEnqueuedLocked(final NotificationRecord r) {
             final StatusBarNotification sbn = r.sbn;
-            TrimCache trimCache = new TrimCache(sbn);
-
-            // There should be only one, but it's a list, so while we enforce
-            // singularity elsewhere, we keep it general here, to avoid surprises.
-            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
-                boolean sbnVisible = isVisibleToListener(sbn, info)
-                        && info.isSameUser(r.getUserId());
-                if (!sbnVisible) {
-                    continue;
-                }
-
-                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        notifyEnqueued(info, sbnToPost, r.getChannel());
-                    }
-                });
-            }
+            notifyAssistantLocked(
+                    sbn,
+                    true /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
+                        try {
+                            assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel());
+                        } catch (RemoteException ex) {
+                            Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+                        }
+                    });
         }
 
-        private void notifyEnqueued(final ManagedServiceInfo info,
-                final StatusBarNotification sbn, final NotificationChannel channel) {
-            final INotificationListener assistant = (INotificationListener) info.service;
-            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
-            try {
-                assistant.onNotificationEnqueuedWithChannel(sbnHolder, channel);
-            } catch (RemoteException ex) {
-                Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
-            }
+        @GuardedBy("mNotificationLock")
+        void notifyAssistantExpansionChangedLocked(
+                final StatusBarNotification sbn,
+                final boolean isUserAction,
+                final boolean isExpanded) {
+            final String key = sbn.getKey();
+            notifyAssistantLocked(
+                    sbn,
+                    false /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
+                        try {
+                            assistant.onNotificationExpansionChanged(key, isUserAction, isExpanded);
+                        } catch (RemoteException ex) {
+                            Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+                        }
+                    });
+        }
+
+        @GuardedBy("mNotificationLock")
+        void notifyAssistantNotificationDirectReplyLocked(
+                final StatusBarNotification sbn) {
+            final String key = sbn.getKey();
+            notifyAssistantLocked(
+                    sbn,
+                    false /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
+                        try {
+                            assistant.onNotificationDirectReply(key);
+                        } catch (RemoteException ex) {
+                            Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+                        }
+                    });
+        }
+
+        @GuardedBy("mNotificationLock")
+        void notifyAssistantSuggestedReplySent(
+                final StatusBarNotification sbn, CharSequence reply, boolean generatedByAssistant) {
+            final String key = sbn.getKey();
+            notifyAssistantLocked(
+                    sbn,
+                    false /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
+                        try {
+                            assistant.onSuggestedReplySent(
+                                    key,
+                                    reply,
+                                    generatedByAssistant
+                                            ? NotificationAssistantService.SOURCE_FROM_ASSISTANT
+                                            : NotificationAssistantService.SOURCE_FROM_APP);
+                        } catch (RemoteException ex) {
+                            Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
+                        }
+                    });
+        }
+
+        @GuardedBy("mNotificationLock")
+        void notifyAssistantActionClicked(
+                final StatusBarNotification sbn, int actionIndex, Notification.Action action,
+                boolean generatedByAssistant) {
+            final String key = sbn.getKey();
+            notifyAssistantLocked(
+                    sbn,
+                    false /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
+                        try {
+                            assistant.onActionClicked(
+                                    key,
+                                    action,
+                                    generatedByAssistant
+                                            ? NotificationAssistantService.SOURCE_FROM_ASSISTANT
+                                            : NotificationAssistantService.SOURCE_FROM_APP);
+                        } catch (RemoteException ex) {
+                            Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
+                        }
+                    });
         }
 
         /**
@@ -6881,30 +6953,49 @@
          * context
          */
         @GuardedBy("mNotificationLock")
-        public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
-                final String snoozeCriterionId) {
-            TrimCache trimCache = new TrimCache(sbn);
-            for (final ManagedServiceInfo info : getServices()) {
-                boolean sbnVisible = isVisibleToListener(sbn, info);
-                if (!sbnVisible) {
-                    continue;
-                }
-                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        final INotificationListener assistant =
-                                (INotificationListener) info.service;
-                        StatusBarNotificationHolder sbnHolder
-                                = new StatusBarNotificationHolder(sbnToPost);
+        private void notifyAssistantSnoozedLocked(
+                final StatusBarNotification sbn, final String snoozeCriterionId) {
+            notifyAssistantLocked(
+                    sbn,
+                    false /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
                         try {
                             assistant.onNotificationSnoozedUntilContext(
                                     sbnHolder, snoozeCriterionId);
                         } catch (RemoteException ex) {
                             Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
                         }
-                    }
-                });
+                    });
+        }
+
+        /**
+         * Notifies the assistant something about the specified notification, only assistant
+         * that is visible to the notification will be notified.
+         *
+         * @param sbn          the notification object that the update is about.
+         * @param sameUserOnly should the update  be sent to the assistant in the same user only.
+         * @param callback     the callback that provides the assistant to be notified, executed
+         *                     in WorkerHandler.
+         */
+        @GuardedBy("mNotificationLock")
+        private void notifyAssistantLocked(
+                final StatusBarNotification sbn,
+                boolean sameUserOnly,
+                BiConsumer<INotificationListener, StatusBarNotificationHolder> callback) {
+            TrimCache trimCache = new TrimCache(sbn);
+            // There should be only one, but it's a list, so while we enforce
+            // singularity elsewhere, we keep it general here, to avoid surprises.
+            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+                boolean sbnVisible = isVisibleToListener(sbn, info)
+                        && (!sameUserOnly || info.isSameUser(sbn.getUserId()));
+                if (!sbnVisible) {
+                    continue;
+                }
+                final INotificationListener assistant = (INotificationListener) info.service;
+                final StatusBarNotification sbnToPost = trimCache.ForListener(info);
+                final StatusBarNotificationHolder sbnHolder =
+                        new StatusBarNotificationHolder(sbnToPost);
+                mHandler.post(() -> callback.accept(assistant, sbnHolder));
             }
         }
 
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 731e6bc..6d59827 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -46,7 +46,7 @@
  * Note: this class is subclassed in the OMS unit tests, and hence not marked as final.
  */
 class IdmapManager {
-    private static final boolean FEATURE_FLAG_IDMAP2 = false;
+    private static final boolean FEATURE_FLAG_IDMAP2 = true;
 
     private final Installer mInstaller;
     private IIdmap2 mIdmap2Service;
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f1b03d1..d471904 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -22,14 +22,11 @@
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 import static android.content.Intent.ACTION_USER_ADDED;
 import static android.content.Intent.ACTION_USER_REMOVED;
-import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.SIGNATURE_MATCH;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ActivityThread;
 import android.app.IActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -37,7 +34,6 @@
 import android.content.IntentFilter;
 import android.content.om.IOverlayManager;
 import android.content.om.OverlayInfo;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManagerInternal;
@@ -59,11 +55,9 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
-import com.android.internal.util.ConcurrentUtils;
 import com.android.server.FgThread;
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
-import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
 import com.android.server.pm.Installer;
 import com.android.server.pm.UserManagerService;
@@ -84,8 +78,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -228,8 +220,6 @@
 
     private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
 
-    private Future<?> mInitCompleteSignal;
-
     public OverlayManagerService(@NonNull final Context context,
             @NonNull final Installer installer) {
         super(context);
@@ -241,29 +231,28 @@
         mSettings = new OverlayManagerSettings();
         mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
                 getDefaultOverlayPackages(), new OverlayChangeListener());
-        mInitCompleteSignal = SystemServerInitThreadPool.get().submit(() -> {
-            final IntentFilter packageFilter = new IntentFilter();
-            packageFilter.addAction(ACTION_PACKAGE_ADDED);
-            packageFilter.addAction(ACTION_PACKAGE_CHANGED);
-            packageFilter.addAction(ACTION_PACKAGE_REMOVED);
-            packageFilter.addDataScheme("package");
-            getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
-                    packageFilter, null, null);
 
-            final IntentFilter userFilter = new IntentFilter();
-            userFilter.addAction(ACTION_USER_ADDED);
-            userFilter.addAction(ACTION_USER_REMOVED);
-            getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
-                    userFilter, null, null);
+        final IntentFilter packageFilter = new IntentFilter();
+        packageFilter.addAction(ACTION_PACKAGE_ADDED);
+        packageFilter.addAction(ACTION_PACKAGE_CHANGED);
+        packageFilter.addAction(ACTION_PACKAGE_REMOVED);
+        packageFilter.addDataScheme("package");
+        getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
+                packageFilter, null, null);
 
-            restoreSettings();
+        final IntentFilter userFilter = new IntentFilter();
+        userFilter.addAction(ACTION_USER_ADDED);
+        userFilter.addAction(ACTION_USER_REMOVED);
+        getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
+                userFilter, null, null);
 
-            initIfNeeded();
-            onSwitchUser(UserHandle.USER_SYSTEM);
+        restoreSettings();
 
-            publishBinderService(Context.OVERLAY_SERVICE, mService);
-            publishLocalService(OverlayManagerService.class, this);
-        }, "Init OverlayManagerService");
+        initIfNeeded();
+        onSwitchUser(UserHandle.USER_SYSTEM);
+
+        publishBinderService(Context.OVERLAY_SERVICE, mService);
+        publishLocalService(OverlayManagerService.class, this);
     }
 
     @Override
@@ -271,32 +260,6 @@
         // Intentionally left empty.
     }
 
-    @Override
-    public void onBootPhase(int phase) {
-        if (phase == PHASE_SYSTEM_SERVICES_READY && mInitCompleteSignal != null) {
-            ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
-                    "Wait for OverlayManagerService init");
-            mInitCompleteSignal = null;
-        }
-    }
-
-    public void updateSystemUiContext() {
-        if (mInitCompleteSignal != null) {
-            ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
-                    "Wait for OverlayManagerService init");
-            mInitCompleteSignal = null;
-        }
-
-        final ApplicationInfo ai;
-        try {
-            ai = mPackageManager.mPackageManager.getApplicationInfo("android",
-                    GET_SHARED_LIBRARY_FILES, UserHandle.USER_SYSTEM);
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
-        }
-        ActivityThread.currentActivityThread().handleSystemApplicationInfoChanged(ai);
-    }
-
     private void initIfNeeded() {
         final UserManager um = getContext().getSystemService(UserManager.class);
         final List<UserInfo> users = um.getUsers(true /*excludeDying*/);
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index b490381..7f1fb6c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -310,7 +310,7 @@
                             .setPackage(packageName),
                     user);
             if (Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 0) == 0) {
+                    Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
                 return launcherActivities;
             }
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index cc640f0..093b85e 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -16,6 +16,28 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
+
+import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
+import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
+import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
+import static com.android.server.pm.Installer.DEXOPT_FORCE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
+import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
+import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
+import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
+
+import static dalvik.system.DexFile.getSafeModeCompilerFilter;
+import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -41,6 +63,8 @@
 import com.android.server.pm.dex.DexoptUtils;
 import com.android.server.pm.dex.PackageDexUsage;
 
+import dalvik.system.DexFile;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -48,32 +72,6 @@
 import java.util.List;
 import java.util.Map;
 
-import dalvik.system.DexFile;
-
-import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
-
-import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
-import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
-import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
-import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
-import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
-import static com.android.server.pm.Installer.DEXOPT_FORCE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
-import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
-import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
-
-import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
-
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
-
-import static dalvik.system.DexFile.getSafeModeCompilerFilter;
-import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
-
 /**
  * Helper class for running dexopt command on packages.
  */
@@ -544,8 +542,7 @@
         // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
         // the user does not have an existing profile.
         boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
-        boolean isPublic = !info.isForwardLocked() &&
-                (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
+        boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata();
         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
         // Some apps are executed with restrictions on hidden API usage. If this app is one
         // of them, pass a flag to dexopt to enable the same restrictions during compilation.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1a5b86c..2e9a71a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -455,12 +455,6 @@
                         + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
             }
 
-            if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
-                    || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-                throw new IllegalArgumentException(
-                        "New installs into ASEC containers no longer supported");
-            }
-
             // Defensively resize giant app icons
             if (params.appIcon != null) {
                 final ActivityManager am = (ActivityManager) mContext.getSystemService(
@@ -487,21 +481,10 @@
                 if (!PackageHelper.fitsOnInternal(mContext, params)) {
                     throw new IOException("No suitable internal storage available");
                 }
-
-            } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-                if (!PackageHelper.fitsOnExternal(mContext, params)) {
-                    throw new IOException("No suitable external storage available");
-                }
-
-            } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
-                // For now, installs to adopted media are treated as internal from
-                // an install flag point-of-view.
-                params.setInstallFlagsInternal();
-
             } else {
                 // For now, installs to adopted media are treated as internal from
                 // an install flag point-of-view.
-                params.setInstallFlagsInternal();
+                params.installFlags |= PackageManager.INSTALL_INTERNAL;
 
                 // Resolve best location for install, based on combination of
                 // requested install flags, delta size, and manifest settings.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index acbd81d..1045289 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -43,7 +43,6 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
-import static android.content.pm.PackageManager.INSTALL_EXTERNAL;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
@@ -54,7 +53,6 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
@@ -646,9 +644,6 @@
     /** Directory where installed application's 32-bit native libraries are copied. */
     private static final File sAppLib32InstallDir =
             new File(Environment.getDataDirectory(), "app-lib");
-    /** Directory where code and non-resource assets of forward-locked applications are stored */
-    private static final File sDrmAppPrivateInstallDir =
-            new File(Environment.getDataDirectory(), "app-private");
 
     // ----------------------------------------------------------------
 
@@ -1811,12 +1806,10 @@
                             firstUserIds, firstInstantUserIds);
                 }
 
-                // Send broadcast package appeared if forward locked/external for all users
-                // treat asec-hosted packages like removable media on upgrade
-                if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
+                // Send broadcast package appeared if external for all users
+                if (isExternal(res.pkg)) {
                     if (DEBUG_INSTALL) {
-                        Slog.i(TAG, "upgrading pkg " + res.pkg
-                                + " is ASEC-hosted -> AVAILABLE");
+                        Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
                     }
                     final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};
                     ArrayList<String> pkgList = new ArrayList<>(1);
@@ -2629,10 +2622,6 @@
                         SystemClock.uptimeMillis());
                 scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
 
-                scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
-                        | PackageParser.PARSE_FORWARD_LOCK,
-                        scanFlags | SCAN_REQUIRE_KNOWN, 0);
-
                 // Remove disable package settings for updated system apps that were
                 // removed via an OTA. If the update is no longer present, remove the
                 // app completely. Otherwise, revoke their system privileges.
@@ -5407,7 +5396,7 @@
         synchronized (mPackages) {
             Signature[] s1;
             Signature[] s2;
-            Object obj = mSettings.getUserIdLPr(uid1);
+            Object obj = mSettings.getSettingLPr(uid1);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     if (isCallerInstantApp) {
@@ -5426,7 +5415,7 @@
             } else {
                 return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
             }
-            obj = mSettings.getUserIdLPr(uid2);
+            obj = mSettings.getSettingLPr(uid2);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     if (isCallerInstantApp) {
@@ -5485,7 +5474,7 @@
         // reader
         synchronized (mPackages) {
             final PackageParser.SigningDetails signingDetails;
-            final Object obj = mSettings.getUserIdLPr(uid);
+            final Object obj = mSettings.getSettingLPr(uid);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
@@ -5598,7 +5587,7 @@
         uid = UserHandle.getAppId(uid);
         // reader
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(uid);
+            Object obj = mSettings.getSettingLPr(uid);
             if (obj instanceof SharedUserSetting) {
                 if (isCallerInstantApp) {
                     return null;
@@ -5634,7 +5623,7 @@
             return null;
         }
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+            Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.name + ":" + sus.userId;
@@ -5662,7 +5651,7 @@
         synchronized (mPackages) {
             for (int i = uids.length - 1; i >= 0; i--) {
                 final int uid = uids[i];
-                Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+                Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
                 if (obj instanceof SharedUserSetting) {
                     final SharedUserSetting sus = (SharedUserSetting) obj;
                     names[i] = "shared:" + sus.name;
@@ -5711,7 +5700,7 @@
             return 0;
         }
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+            Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.pkgFlags;
@@ -5733,7 +5722,7 @@
             return 0;
         }
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+            Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.pkgPrivateFlags;
@@ -5756,7 +5745,7 @@
         uid = UserHandle.getAppId(uid);
         // reader
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(uid);
+            Object obj = mSettings.getSettingLPr(uid);
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 final Iterator<PackageSetting> it = sus.packages.iterator();
@@ -6396,7 +6385,7 @@
                 callingUid = mIsolatedOwners.get(callingUid);
             }
             final int appId = UserHandle.getAppId(callingUid);
-            final Object obj = mSettings.getUserIdLPr(appId);
+            final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof PackageSetting) {
                 final PackageSetting ps = (PackageSetting) obj;
                 final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid));
@@ -8631,7 +8620,7 @@
                     + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
 
             final InstallArgs args = createInstallArgsForExisting(
-                    packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+                    pkgSetting.codePathString,
                     pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
             args.cleanUpResourcesLI();
             synchronized (mPackages) {
@@ -8704,7 +8693,7 @@
                         + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
                         + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
                 InstallArgs args = createInstallArgsForExisting(
-                        packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+                        pkgSetting.codePathString,
                         pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
                 synchronized (mInstallLock) {
                     args.cleanUpResourcesLI();
@@ -8726,7 +8715,13 @@
                 | SCAN_UPDATE_SIGNATURE, currentTime, user);
         if (scanResult.success) {
             synchronized (mPackages) {
-                commitScanResultLocked(scanResult);
+                try {
+                    prepareScanResultLocked(scanResult);
+                    commitScanResultLocked(scanResult);
+                } catch (PackageManagerException e) {
+                    unprepareScanResultLocked(scanResult);
+                    throw e;
+                }
             }
         }
 
@@ -10125,7 +10120,37 @@
                 }
             }
             for (ScanResult result : results) {
-                commitScanResultLocked(result);
+                try {
+                    prepareScanResultLocked(result);
+                    commitScanResultLocked(result);
+                } catch (PackageManagerException e) {
+                    unprepareScanResultLocked(result);
+                    throw e;
+                }
+            }
+        }
+    }
+
+    /** Prepares the system to commit a {@link ScanResult} in a way that will not fail. */
+    private void prepareScanResultLocked(@NonNull ScanResult result)
+            throws PackageManagerException {
+        if (!result.existingSettingCopied) {
+            // THROWS: when we can't allocate a user id. add call to check if there's
+            // enough space to ensure we won't throw; otherwise, don't modify state
+            mSettings.registerAppIdLPw(result.pkgSetting);
+        }
+    }
+
+    /**
+     * Reverts any changes to the system that were made by
+     * {@link #prepareScanResultLocked(ScanResult)}
+     */
+    private void unprepareScanResultLocked(@NonNull ScanResult result) {
+        if (!result.existingSettingCopied) {
+            // iff we've acquired an app ID for a new package setting, remove it so that it can be
+            // acquired by another request.
+            if (result.pkgSetting.appId > 0) {
+                mSettings.removeAppIdLPw(result.pkgSetting.appId);
             }
         }
     }
@@ -10164,10 +10189,6 @@
             if (originalPkgSetting != null) {
                 mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
             }
-            // THROWS: when we can't allocate a user id. add call to check if there's
-            // enough space to ensure we won't throw; otherwise, don't modify state
-            mSettings.addUserToSettingLPw(pkgSetting);
-
             if (originalPkgSetting != null && (scanFlags & SCAN_CHECK_ONLY) == 0) {
                 mTransferedPackages.add(originalPkgSetting.name);
             }
@@ -11536,11 +11557,8 @@
         // pass once we've determined ABI below.
         setNativeLibraryPaths(pkg, sAppLib32InstallDir);
 
-        // We would never need to extract libs for forward-locked and external packages,
-        // since the container service will do it for us. We shouldn't attempt to
-        // extract libs from system app when it was not updated.
-        if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() ||
-                (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) {
+        // We shouldn't attempt to extract libs from system app when it was not updated.
+        if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
             extractLibs = false;
         }
 
@@ -11881,7 +11899,6 @@
         final String codePath = pkg.codePath;
         final File codeFile = new File(codePath);
         final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
-        final boolean asecApp = info.isForwardLocked() || info.isExternalAsec();
 
         info.nativeLibraryRootDir = null;
         info.nativeLibraryRootRequiresIsa = false;
@@ -11910,9 +11927,6 @@
                     info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
                             secondaryLibDir, apkName).getAbsolutePath();
                 }
-            } else if (asecApp) {
-                info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
-                        .getAbsolutePath();
             } else {
                 final String apkName = deriveCodePathName(codePath);
                 info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
@@ -13496,7 +13510,7 @@
             }
 
             Signature[] callerSignature;
-            Object obj = mSettings.getUserIdLPr(callingUid);
+            Object obj = mSettings.getSettingLPr(callingUid);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     callerSignature =
@@ -14021,7 +14035,6 @@
         private int installLocationPolicy(PackageInfoLite pkgLite) {
             String packageName = pkgLite.packageName;
             int installLocation = pkgLite.installLocation;
-            boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
             // reader
             synchronized (mPackages) {
                 // Currently installed package which the new package is attempting to replace or
@@ -14074,16 +14087,8 @@
                     if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                         // Check for updated system application.
                         if ((installedPkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                            if (onSd) {
-                                Slog.w(TAG, "Cannot install update to system app on sdcard");
-                                return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION;
-                            }
                             return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                         } else {
-                            if (onSd) {
-                                // Install flag overrides everything.
-                                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
-                            }
                             // If current upgrade specifies particular preference
                             if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
                                 // Application explicitly specified internal.
@@ -14104,11 +14109,6 @@
                     }
                 }
             }
-            // All the special cases have been taken care of.
-            // Return result based on recommended install location.
-            if (onSd) {
-                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
-            }
             return pkgLite.recommendedInstallLocation;
         }
 
@@ -14125,70 +14125,59 @@
             if (origin.staged) {
                 if (origin.file != null) {
                     installFlags |= PackageManager.INSTALL_INTERNAL;
-                    installFlags &= ~PackageManager.INSTALL_EXTERNAL;
                 } else {
                     throw new IllegalStateException("Invalid stage location");
                 }
             }
 
-            final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
             final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
             final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
             PackageInfoLite pkgLite = null;
 
-            if (onInt && onSd) {
-                // Check if both bits are set.
-                Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
-                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-            } else if (onSd && ephemeral) {
-                Slog.w(TAG,  "Conflicting flags specified for installing ephemeral on external");
-                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-            } else {
-                pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
-                        origin.resolvedPath, installFlags, packageAbiOverride);
 
-                if (DEBUG_INSTANT && ephemeral) {
-                    Slog.v(TAG, "pkgLite for install: " + pkgLite);
+            pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+                    origin.resolvedPath, installFlags, packageAbiOverride);
+
+            if (DEBUG_INSTANT && ephemeral) {
+                Slog.v(TAG, "pkgLite for install: " + pkgLite);
+            }
+
+            /*
+             * If we have too little free space, try to free cache
+             * before giving up.
+             */
+            if (!origin.staged && pkgLite.recommendedInstallLocation
+                    == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+                // TODO: focus freeing disk space on the target device
+                final StorageManager storage = StorageManager.from(mContext);
+                final long lowThreshold = storage.getStorageLowBytes(
+                        Environment.getDataDirectory());
+
+                final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
+                        origin.resolvedPath, packageAbiOverride);
+                if (sizeBytes >= 0) {
+                    try {
+                        mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
+                        pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+                                origin.resolvedPath, installFlags, packageAbiOverride);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Failed to free cache", e);
+                    }
                 }
 
                 /*
-                 * If we have too little free space, try to free cache
-                 * before giving up.
+                 * The cache free must have deleted the file we downloaded to install.
+                 *
+                 * TODO: fix the "freeCache" call to not delete the file we care about.
                  */
-                if (!origin.staged && pkgLite.recommendedInstallLocation
-                        == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
-                    // TODO: focus freeing disk space on the target device
-                    final StorageManager storage = StorageManager.from(mContext);
-                    final long lowThreshold = storage.getStorageLowBytes(
-                            Environment.getDataDirectory());
-
-                    final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
-                            origin.resolvedPath, packageAbiOverride);
-                    if (sizeBytes >= 0) {
-                        try {
-                            mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
-                            pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
-                                    origin.resolvedPath, installFlags, packageAbiOverride);
-                        } catch (InstallerException e) {
-                            Slog.w(TAG, "Failed to free cache", e);
-                        }
-                    }
-
-                    /*
-                     * The cache free must have deleted the file we
-                     * downloaded to install.
-                     *
-                     * TODO: fix the "freeCache" call to not delete
-                     *       the file we care about.
-                     */
-                    if (pkgLite.recommendedInstallLocation
-                            == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
-                        pkgLite.recommendedInstallLocation
+                if (pkgLite.recommendedInstallLocation
+                        == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
+                    pkgLite.recommendedInstallLocation
                             = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
-                    }
                 }
             }
 
+
             if (ret == PackageManager.INSTALL_SUCCEEDED) {
                 int loc = pkgLite.recommendedInstallLocation;
                 if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
@@ -14208,24 +14197,21 @@
                     loc = installLocationPolicy(pkgLite);
                     if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
                         ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-                    } else if (!onSd && !onInt) {
+                    } else if (!onInt) {
                         // Override install location with flags
                         if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
                             // Set the flag to install on external media.
-                            installFlags |= PackageManager.INSTALL_EXTERNAL;
                             installFlags &= ~PackageManager.INSTALL_INTERNAL;
                         } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
                             if (DEBUG_INSTANT) {
                                 Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
                             }
                             installFlags |= PackageManager.INSTALL_INSTANT_APP;
-                            installFlags &= ~(PackageManager.INSTALL_EXTERNAL
-                                    |PackageManager.INSTALL_INTERNAL);
+                            installFlags &= ~PackageManager.INSTALL_INTERNAL;
                         } else {
                             // Make sure the flag for installing on external
                             // media is unset
                             installFlags |= PackageManager.INSTALL_INTERNAL;
-                            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
                         }
                     }
                 }
@@ -14409,7 +14395,7 @@
      * Create args that describe an existing installed package. Typically used
      * when cleaning up old installs, or used as a move source.
      */
-    private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,
+    private InstallArgs createInstallArgsForExisting(String codePath,
             String resourcePath, String[] instructionSets) {
         return new FileInstallArgs(codePath, resourcePath, instructionSets);
     }
@@ -14501,14 +14487,6 @@
             return PackageManager.INSTALL_SUCCEEDED;
         }
 
-        protected boolean isFwdLocked() {
-            return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
-        }
-
-        protected boolean isExternalAsec() {
-            return (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
-        }
-
         protected boolean isEphemeral() {
             return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
         }
@@ -14536,7 +14514,7 @@
     }
 
     /**
-     * Logic to handle installation of non-ASEC applications, including copying
+     * Logic to handle installation of new applications, including copying
      * and renaming logic.
      */
     class FileInstallArgs extends InstallArgs {
@@ -14558,9 +14536,6 @@
                     params.grantedRuntimePermissions,
                     params.traceMethod, params.traceCookie, params.signingDetails,
                     params.installReason, params.mParentInstallParams);
-            if (isFwdLocked()) {
-                throw new IllegalArgumentException("Forward locking only supported in ASEC");
-            }
         }
 
         /** Existing install */
@@ -15215,8 +15190,11 @@
         }
     }
     private static class ReconcileFailure extends PackageManagerException {
-        public ReconcileFailure(String message) {
-            super("Invalid reconcile request: " + message);
+        ReconcileFailure(String message) {
+            super("Reconcile failed: " + message);
+        }
+        ReconcileFailure(int reason, String message) {
+            super(reason, "Reconcile failed: " + message);
         }
     }
 
@@ -15235,10 +15213,12 @@
         @PackageManager.InstallFlags
         public final int installFlags;
         public final InstallArgs installArgs;
+        public final DeletePackageAction deletePackageAction;
 
         private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting,
                 UserHandle installForUser, PackageInstalledInfo installResult, int installFlags,
-                String volumeUuid, PrepareResult prepareResult, ScanResult scanResult) {
+                String volumeUuid, PrepareResult prepareResult, ScanResult scanResult,
+                DeletePackageAction deletePackageAction) {
             this.installArgs = installArgs;
             this.pkgSetting = pkgSetting;
             this.installForUser = installForUser;
@@ -15247,6 +15227,7 @@
             this.volumeUuid = volumeUuid;
             this.prepareResult = prepareResult;
             this.scanResult = scanResult;
+            this.deletePackageAction = deletePackageAction;
         }
     }
 
@@ -15259,14 +15240,29 @@
             final ScanResult scanResult = request.scannedPackages.get(installPackageName);
             final InstallArgs installArgs = request.installArgs.get(installPackageName);
             final PackageInstalledInfo res = request.installResults.get(installPackageName);
+            final PrepareResult prepareResult = request.preparedPackages.get(installPackageName);
             if (scanResult == null || installArgs == null || res == null) {
                 throw new ReconcileFailure(
                         "inputs not balanced; missing argument for " + installPackageName);
             }
+            final DeletePackageAction deletePackageAction;
+            if (prepareResult.replace) {
+                deletePackageAction = mayDeletePackageLocked(res.removedInfo,
+                        prepareResult.originalPs, prepareResult.disabledPs,
+                        prepareResult.childPackageSettings);
+                if (deletePackageAction == null) {
+                    throw new ReconcileFailure(
+                            PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
+                            "May not delete " + installPackageName + " to replace");
+                }
+            } else {
+                deletePackageAction = null;
+            }
             result.put(installPackageName,
                     new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(),
                             res, installArgs.installFlags, installArgs.volumeUuid,
-                            request.preparedPackages.get(installPackageName), scanResult));
+                            request.preparedPackages.get(installPackageName), scanResult,
+                            deletePackageAction));
         }
         return result;
     }
@@ -15291,7 +15287,7 @@
                         // We didn't need to disable the .apk as a current system package,
                         // which means we are replacing another update that is already
                         // installed.  We need to make sure to delete the older one's .apk.
-                        res.removedInfo.args = createInstallArgsForExisting(0,
+                        res.removedInfo.args = createInstallArgsForExisting(
                                 oldPackage.applicationInfo.getCodePath(),
                                 oldPackage.applicationInfo.getResourcePath(),
                                 getAppDexInstructionSets(oldPackage.applicationInfo));
@@ -15338,29 +15334,29 @@
                     final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0;
                     final int deleteFlags = PackageManager.DELETE_KEEP_DATA
                             | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
-                    // First delete the existing package while retaining the data directory
-                    if (!deletePackageLIF(packageName, null, true, request.mAllUsers, deleteFlags,
-                            res.removedInfo, true, pkg)) {
-                        // If the existing package wasn't successfully deleted
-                        res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
-                                "replaceNonSystemPackageLI");
-                        return false;
-                    } else {
-                        // Successfully deleted the old package; proceed with replace.
-
-                        // If deleted package lived in a container, give users a chance to
-                        // relinquish resources before killing.
-                        if (oldPackage.isForwardLocked() || isExternal(oldPackage)) {
-                            if (DEBUG_INSTALL) {
-                                Slog.i(TAG, "upgrading pkg " + oldPackage
-                                        + " is ASEC-hosted -> UNAVAILABLE");
-                            }
-                            final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
-                            final ArrayList<String> pkgList = new ArrayList<>(1);
-                            pkgList.add(oldPackage.applicationInfo.packageName);
-                            sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+                    try {
+                        executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
+                                null, true, request.mAllUsers, deleteFlags, true, pkg);
+                    } catch (SystemDeleteException e) {
+                        if (Build.IS_ENG) {
+                            throw new RuntimeException("Unexpected failure", e);
+                            // ignore; not possible for non-system app
                         }
                     }
+                    // Successfully deleted the old package; proceed with replace.
+
+                    // If deleted package lived in a container, give users a chance to
+                    // relinquish resources before killing.
+                    if (oldPackage.isForwardLocked() || isExternal(oldPackage)) {
+                        if (DEBUG_INSTALL) {
+                            Slog.i(TAG, "upgrading pkg " + oldPackage
+                                    + " is ASEC-hosted -> UNAVAILABLE");
+                        }
+                        final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
+                        final ArrayList<String> pkgList = new ArrayList<>(1);
+                        pkgList.add(oldPackage.applicationInfo.packageName);
+                        sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+                    }
 
                     // Update the in-memory copy of the previous code paths.
                     PackageSetting ps1 = mSettings.mPackages.get(
@@ -15414,8 +15410,10 @@
 
 
             try {
+                prepareScanResultLocked(scanResult);
                 commitScanResultLocked(scanResult);
             } catch (PackageManagerException e) {
+                unprepareScanResultLocked(scanResult);
                 res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
                 res.setError("Package couldn't be installed in " + pkg.codePath, e);
                 return false;
@@ -15570,8 +15568,6 @@
      */
     private void executePostCommitSteps(CommitRequest commitRequest) {
         for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
-            final boolean forwardLocked =
-                    ((reconciledPkg.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
             final boolean instantApp =
                     ((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
             final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
@@ -15604,10 +15600,8 @@
             //     This update happens in place!
             //
             // We only need to dexopt if the package meets ALL of the following conditions:
-            //   1) it is not forward locked.
-            //   2) it is not on on an external ASEC container.
-            //   3) it is not an instant app or if it is then dexopt is enabled via gservices.
-            //   4) it is not debuggable.
+            //   1) it is not an instant app or if it is then dexopt is enabled via gservices.
+            //   2) it is not debuggable.
             //
             // Note that we do not dexopt instant apps by default. dexopt can take some time to
             // complete, so we skip this step during installation. Instead, we'll take extra time
@@ -15615,9 +15609,8 @@
             // continuous progress to the useur instead of mysteriously blocking somewhere in the
             // middle of running an instant app. The default behaviour can be overridden
             // via gservices.
-            final boolean performDexopt = !forwardLocked
-                    && !pkg.applicationInfo.isExternalAsec()
-                    && (!instantApp || Global.getInt(mContext.getContentResolver(),
+            final boolean performDexopt =
+                    (!instantApp || Global.getInt(mContext.getContentResolver(),
                     Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
                     && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
 
@@ -15712,7 +15705,6 @@
                     // Parse old package
                     boolean oldExternal = isExternal(oldPackage);
                     int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
-                            | (oldPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0)
                             | (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
                     int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
                     try {
@@ -15772,13 +15764,16 @@
         @Nullable
         public final String renamedPackage;
         public final PackageFreezer freezer;
+        public final PackageSetting originalPs;
+        public final PackageSetting disabledPs;
+        public final PackageSetting[] childPackageSettings;
 
         private PrepareResult(int installReason, String volumeUuid,
                 String installerPackageName, UserHandle user, boolean replace, int scanFlags,
                 int parseFlags, PackageParser.Package existingPackage,
                 PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
-                String renamedPackage,
-                PackageFreezer freezer) {
+                String renamedPackage, PackageFreezer freezer, PackageSetting originalPs,
+                PackageSetting disabledPs, PackageSetting[] childPackageSettings) {
             this.installReason = installReason;
             this.volumeUuid = volumeUuid;
             this.installerPackageName = installerPackageName;
@@ -15792,6 +15787,9 @@
             this.system = system;
             this.renamedPackage = renamedPackage;
             this.freezer = freezer;
+            this.originalPs = originalPs;
+            this.disabledPs = disabledPs;
+            this.childPackageSettings = childPackageSettings;
         }
     }
 
@@ -15830,9 +15828,7 @@
         final String installerPackageName = args.installerPackageName;
         final String volumeUuid = args.volumeUuid;
         final File tmpPackageFile = new File(args.getCodePath());
-        final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
-        final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
-                || (args.volumeUuid != null));
+        final boolean onExternal = args.volumeUuid != null;
         final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
         final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
         final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
@@ -15859,16 +15855,14 @@
         if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
 
         // Sanity check
-        if (instantApp && (forwardLocked || onExternal)) {
-            Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
-                    + " external=" + onExternal);
+        if (instantApp && onExternal) {
+            Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
             throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
         }
 
         // Retrieve PackageSettings and parse package
         @ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
                 | PackageParser.PARSE_ENFORCE_CODE
-                | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
                 | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
                 | (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
 
@@ -16213,7 +16207,7 @@
                 pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
             }
 
-        } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
+        } else {
             // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
             scanFlags |= SCAN_NO_DEX;
 
@@ -16299,7 +16293,9 @@
             String targetVolumeUuid = volumeUuid;
             int targetScanFlags = scanFlags;
             int targetParseFlags = parseFlags;
-
+            final PackageSetting ps;
+            final PackageSetting disabledPs;
+            final PackageSetting[] childPackages;
             if (replace) {
                 targetVolumeUuid = null;
                 if (pkg.applicationInfo.isStaticSharedLibrary()) {
@@ -16319,7 +16315,6 @@
                 final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
 
                 final PackageParser.Package oldPackage;
-                final PackageSetting ps;
                 final String pkgName11 = pkg.packageName;
                 final int[] allUsers;
                 final int[] installedUsers;
@@ -16346,6 +16341,7 @@
                     }
 
                     ps = mSettings.mPackages.get(pkgName11);
+                    disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
 
                     // verify signatures are valid
                     final KeySetManagerService ksms = mSettings.mKeySetManagerService;
@@ -16442,49 +16438,52 @@
                     res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
                 }
 
-                final int childCount = (oldPackage.childPackages != null)
-                        ? oldPackage.childPackages.size() : 0;
-                for (int i = 0; i < childCount; i++) {
-                    boolean childPackageUpdated = false;
-                    PackageParser.Package childPkg = oldPackage.childPackages.get(i);
-                    final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
-                    if (res.addedChildPackages != null) {
-                        PackageInstalledInfo childRes = res.addedChildPackages.get(
-                                childPkg.packageName);
-                        if (childRes != null) {
-                            childRes.removedInfo.uid = childPkg.applicationInfo.uid;
-                            childRes.removedInfo.removedPackage = childPkg.packageName;
-                            if (childPs != null) {
-                                childRes.removedInfo.installerPackageName =
-                                        childPs.installerPackageName;
-                            }
-                            childRes.removedInfo.isUpdate = true;
-                            childRes.removedInfo.installReasons = res.removedInfo.installReasons;
-                            childPackageUpdated = true;
-                        }
-                    }
-                    if (!childPackageUpdated) {
-                        PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
-                        childRemovedRes.removedPackage = childPkg.packageName;
-                        if (childPs != null) {
-                            childRemovedRes.installerPackageName = childPs.installerPackageName;
-                        }
-                        childRemovedRes.isUpdate = false;
-                        childRemovedRes.dataRemoved = true;
-                        synchronized (mPackages) {
-                            if (childPs != null) {
-                                childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers,
-                                        true);
+                childPackages = mSettings.getChildSettingsLPr(ps);
+                if (childPackages != null) {
+                    for (PackageSetting childPs : childPackages) {
+                        boolean childPackageUpdated = false;
+                        PackageParser.Package childPkg = (childPs == null) ? null : childPs.pkg;
+                        if (res.addedChildPackages != null) {
+                            PackageInstalledInfo childRes = res.addedChildPackages.get(
+                                    childPkg.packageName);
+                            if (childRes != null) {
+                                childRes.removedInfo.uid = childPkg.applicationInfo.uid;
+                                childRes.removedInfo.removedPackage = childPkg.packageName;
+                                if (childPs != null) {
+                                    childRes.removedInfo.installerPackageName =
+                                            childPs.installerPackageName;
+                                }
+                                childRes.removedInfo.isUpdate = true;
+                                childRes.removedInfo.installReasons =
+                                        res.removedInfo.installReasons;
+                                childPackageUpdated = true;
                             }
                         }
-                        if (res.removedInfo.removedChildPackages == null) {
-                            res.removedInfo.removedChildPackages = new ArrayMap<>();
+                        if (!childPackageUpdated) {
+                            PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
+                            childRemovedRes.removedPackage = childPkg.packageName;
+                            if (childPs != null) {
+                                childRemovedRes.installerPackageName = childPs.installerPackageName;
+                            }
+                            childRemovedRes.isUpdate = false;
+                            childRemovedRes.dataRemoved = true;
+                            synchronized (mPackages) {
+                                if (childPs != null) {
+                                    childRemovedRes.origUsers = childPs.queryInstalledUsers(
+                                            allUsers,
+                                            true);
+                                }
+                            }
+                            if (res.removedInfo.removedChildPackages == null) {
+                                res.removedInfo.removedChildPackages = new ArrayMap<>();
+                            }
+                            res.removedInfo.removedChildPackages.put(childPkg.packageName,
+                                    childRemovedRes);
                         }
-                        res.removedInfo.removedChildPackages.put(childPkg.packageName,
-                                childRemovedRes);
                     }
                 }
 
+
                 sysPkg = (isSystemApp(oldPackage));
                 if (sysPkg) {
                     // Set the system/privileged/oem/vendor/product flags as needed
@@ -16527,6 +16526,9 @@
 
                 }
             } else { // new package install
+                ps = null;
+                childPackages = null;
+                disabledPs = null;
                 replace = false;
                 existingPackage = null;
                 // Remember this for later, in case we need to rollback this install
@@ -16557,9 +16559,11 @@
             }
             // we're passing the freezer back to be closed in a later phase of install
             shouldCloseFreezerBeforeReturn = false;
+
             return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
                     args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
-                    replace /* clearCodeCache */, sysPkg, renamedPackage, freezer);
+                    replace /* clearCodeCache */, sysPkg, renamedPackage, freezer,
+                    ps, disabledPs, childPackages);
         } finally {
             if (shouldCloseFreezerBeforeReturn) {
                 freezer.close();
@@ -16745,19 +16749,6 @@
         return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
     }
 
-    private int packageFlagsToInstallFlags(PackageSetting ps) {
-        int installFlags = 0;
-        if (isExternal(ps) && TextUtils.isEmpty(ps.volumeUuid)) {
-            // This existing package was an external ASEC install when we have
-            // the external flag without a UUID
-            installFlags |= PackageManager.INSTALL_EXTERNAL;
-        }
-        if (ps.isForwardLocked()) {
-            installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
-        }
-        return installFlags;
-    }
-
     private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
         if (isExternal(pkg)) {
             if (TextUtils.isEmpty(pkg.volumeUuid)) {
@@ -16773,9 +16764,6 @@
     private void deleteTempPackageFiles() {
         final FilenameFilter filter =
                 (dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
-        for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
-            file.delete();
-        }
     }
 
     @Override
@@ -17525,34 +17513,20 @@
     /*
      * Tries to delete system package.
      */
-    private boolean deleteSystemPackageLIF(PackageParser.Package deletedPkg,
-            PackageSetting deletedPs, int[] allUserHandles, int flags, PackageRemovedInfo outInfo,
-            boolean writeSettings) {
-        if (deletedPs.parentPackageName != null) {
-            Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName);
-            return false;
-        }
-
+    private void deleteSystemPackageLIF(DeletePackageAction action,
+            PackageParser.Package deletedPkg, PackageSetting deletedPs, int[] allUserHandles,
+            int flags, PackageRemovedInfo outInfo, boolean writeSettings)
+            throws SystemDeleteException {
         final boolean applyUserRestrictions
                 = (allUserHandles != null) && (outInfo.origUsers != null);
-        final PackageSetting disabledPs;
         // Confirm if the system package has been updated
         // An updated system app can be deleted. This will also have to restore
         // the system pkg from system partition
         // reader
-        synchronized (mPackages) {
-            disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPs.name);
-        }
-
+        final PackageSetting disabledPs = action.disabledPs;
         if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName
                 + " disabledPs=" + disabledPs);
-
-        if (disabledPs == null) {
-            Slog.w(TAG, "Attempt to delete unknown system package "+ deletedPkg.packageName);
-            return false;
-        } else if (DEBUG_REMOVE) {
-            Slog.d(TAG, "Deleting system pkg from data partition");
-        }
+        Slog.d(TAG, "Deleting system pkg from data partition");
 
         if (DEBUG_REMOVE) {
             if (applyUserRestrictions) {
@@ -17590,11 +17564,8 @@
             flags |= PackageManager.DELETE_KEEP_DATA;
         }
 
-        boolean ret = deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
+        deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
                 outInfo, writeSettings, disabledPs.pkg);
-        if (!ret) {
-            return false;
-        }
 
         // writer
         synchronized (mPackages) {
@@ -17611,25 +17582,25 @@
         // Install the system package
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
         try {
-            installPackageFromSystemLIF(disabledPs.codePathString, false, allUserHandles,
+            installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles,
                     outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
                     + e.getMessage());
-            return false;
+            // TODO(patb): can we avoid this; throw would come from scan...
+            throw new SystemDeleteException(e);
         } finally {
             if (disabledPs.pkg.isStub) {
                 mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/);
             }
         }
-        return true;
     }
 
     /**
      * Installs a package that's already on the system partition.
      */
     private PackageParser.Package installPackageFromSystemLIF(@NonNull String codePathString,
-            boolean isPrivileged, @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
+            @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
             @Nullable PermissionsState origPermissionState, boolean writeSettings)
                     throws PackageManagerException {
         @ParseFlags int parseFlags =
@@ -17637,7 +17608,7 @@
                 | PackageParser.PARSE_MUST_BE_APK
                 | PackageParser.PARSE_IS_SYSTEM_DIR;
         @ScanFlags int scanFlags = SCAN_AS_SYSTEM;
-        if (isPrivileged || locationIsPrivileged(codePathString)) {
+        if (locationIsPrivileged(codePathString)) {
             scanFlags |= SCAN_AS_PRIVILEGED;
         }
         if (locationIsOem(codePathString)) {
@@ -17713,7 +17684,7 @@
         return pkg;
     }
 
-    private boolean deleteInstalledPackageLIF(PackageSetting ps,
+    private void deleteInstalledPackageLIF(PackageSetting ps,
             boolean deleteCodeAndResources, int flags, int[] allUserHandles,
             PackageRemovedInfo outInfo, boolean writeSettings,
             PackageParser.Package replacingPackage) {
@@ -17728,9 +17699,6 @@
                 for (int i = 0; i < childCount; i++) {
                     String childPackageName = ps.childPackageNames.get(i);
                     PackageSetting childPs = mSettings.mPackages.get(childPackageName);
-                    if (childPs == null) {
-                        return false;
-                    }
                     PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
                             childPackageName);
                     if (childInfo != null) {
@@ -17766,13 +17734,11 @@
         // Delete application code and resources only for parent packages
         if (ps.parentPackageName == null) {
             if (deleteCodeAndResources && (outInfo != null)) {
-                outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
+                outInfo.args = createInstallArgsForExisting(
                         ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
                 if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
             }
         }
-
-        return true;
     }
 
     @Override
@@ -17828,26 +17794,59 @@
 
     private static class DeletePackageAction {
         public final PackageSetting deletingPs;
+        public final PackageSetting disabledPs;
+        public final PackageRemovedInfo outInfo;
 
-        private DeletePackageAction(PackageSetting deletingPs) {
+        private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
+                PackageRemovedInfo outInfo) {
             this.deletingPs = deletingPs;
+            this.disabledPs = disabledPs;
+            this.outInfo = outInfo;
         }
     }
 
     /**
-     * @return a {@link DeletePackageAction} if the provided package may be deleted, {@code null}
-     * otherwise.
+     * @return a {@link DeletePackageAction} if the provided package and related state may be
+     * deleted, {@code null} otherwise.
      */
     @Nullable
-    private DeletePackageAction mayDeletePackageLIF(@NonNull String packageName) {
-        synchronized (mPackages) {
-            final PackageSetting ps;
-            ps = mSettings.mPackages.get(packageName);
-            if (ps == null) {
+    @GuardedBy("mPackages")
+    private static DeletePackageAction mayDeletePackageLocked(
+            PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs,
+            @Nullable PackageSetting[] children) {
+        if (ps == null) {
+            return null;
+        }
+        if (isSystemApp(ps)) {
+            if (ps.parentPackageName == null) {
+                Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName);
                 return null;
             }
-            return new DeletePackageAction(ps);
+
+            // Confirm if the system package has been updated
+            // An updated system app can be deleted. This will also have to restore
+            // the system pkg from system partition
+            // reader
+            if (disabledPs == null) {
+                Slog.w(TAG,
+                        "Attempt to delete unknown system package " + ps.pkg.packageName);
+                return null;
+            }
         }
+        final int parentReferenceCount =
+                (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
+        final int childCount = children != null ? children.length : 0;
+        if (childCount != parentReferenceCount) {
+            return null;
+        }
+        if (childCount != 0 && outInfo != null && outInfo.removedChildPackages != null) {
+            for (PackageSetting child : children) {
+                if (child == null || !ps.childPackageNames.contains(child.name)) {
+                    return null;
+                }
+            }
+        }
+        return new DeletePackageAction(ps, disabledPs, outInfo);
     }
 
     /*
@@ -17857,22 +17856,43 @@
             boolean deleteCodeAndResources, int[] allUserHandles, int flags,
             PackageRemovedInfo outInfo, boolean writeSettings,
             PackageParser.Package replacingPackage) {
-        final DeletePackageAction action = mayDeletePackageLIF(packageName);
+        final DeletePackageAction action;
+        synchronized (mPackages) {
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
+            final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
+            PackageSetting[] children = mSettings.getChildSettingsLPr(ps);
+            action = mayDeletePackageLocked(outInfo, ps, disabledPs, children);
+        }
         if (null == action) {
             return false;
         }
 
         if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
 
-        return executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources,
-                allUserHandles, flags, outInfo, writeSettings, replacingPackage);
+        try {
+            executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources,
+                    allUserHandles, flags, writeSettings, replacingPackage);
+        } catch (SystemDeleteException e) {
+            return false;
+        }
+        return true;
     }
 
-    private boolean executeDeletePackageLIF(DeletePackageAction action,
+    private static class SystemDeleteException extends Exception {
+        public final PackageManagerException reason;
+
+        private SystemDeleteException(PackageManagerException reason) {
+            this.reason = reason;
+        }
+    }
+
+    /** Deletes a package. Only throws when install of a disabled package fails. */
+    private void executeDeletePackageLIF(DeletePackageAction action,
             String packageName, UserHandle user, boolean deleteCodeAndResources,
-            int[] allUserHandles, int flags, PackageRemovedInfo outInfo,
-            boolean writeSettings, PackageParser.Package replacingPackage) {
+            int[] allUserHandles, int flags, boolean writeSettings,
+            PackageParser.Package replacingPackage) throws SystemDeleteException {
         final PackageSetting ps = action.deletingPs;
+        final PackageRemovedInfo outInfo = action.outInfo;
         final boolean systemApp = isSystemApp(ps);
         synchronized (mPackages) {
 
@@ -17888,7 +17908,7 @@
                 clearPackageStateForUserLIF(ps, removedUserId, outInfo);
                 markPackageUninstalledForUserLPw(ps, user);
                 scheduleWritePackageRestrictionsLocked(user);
-                return true;
+                return;
             }
         }
 
@@ -17917,7 +17937,7 @@
                     if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
                     clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
                     scheduleWritePackageRestrictionsLocked(user);
-                    return true;
+                    return;
                 } else {
                     // We need to set it back to 'installed' so the uninstall
                     // broadcasts will be sent correctly.
@@ -17933,7 +17953,7 @@
                 if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
                 clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
                 scheduleWritePackageRestrictionsLocked(user);
-                return true;
+                return;
             }
         }
 
@@ -17958,15 +17978,15 @@
         }
 
         // TODO(b/109941548): break reasons for ret = false out into mayDelete method
-        final boolean ret;
         if (systemApp) {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
             // When an updated system application is deleted we delete the existing resources
             // as well and fall back to existing code in system partition
-            ret = deleteSystemPackageLIF(ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);
+            deleteSystemPackageLIF(
+                    action, ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);
         } else {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
-            ret = deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
+            deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
                     outInfo, writeSettings, replacingPackage);
         }
 
@@ -18015,8 +18035,6 @@
                 }
             }
         }
-
-        return ret;
     }
 
     @GuardedBy("mPackages")
@@ -18477,7 +18495,7 @@
 
     @GuardedBy("mPackages")
     private int getUidTargetSdkVersionLockedLPr(int uid) {
-        Object obj = mSettings.getUserIdLPr(uid);
+        Object obj = mSettings.getSettingLPr(uid);
         if (obj instanceof SharedUserSetting) {
             final SharedUserSetting sus = (SharedUserSetting) obj;
             int vers = Build.VERSION_CODES.CUR_DEVELOPMENT;
@@ -19742,7 +19760,7 @@
                                 enableSystemPackageLPw(deletedPkg);
                             }
                             installPackageFromSystemLIF(deletedPkg.codePath,
-                                    false /*isPrivileged*/, null /*allUserHandles*/,
+                                    /*isPrivileged*/ null /*allUserHandles*/,
                                     null /*origUserHandles*/, null /*origPermissionsState*/,
                                     true /*writeSettings*/);
                         } catch (PackageManagerException pme) {
@@ -21698,7 +21716,6 @@
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
         final PackageManager pm = mContext.getPackageManager();
 
-        final boolean currentAsec;
         final String currentVolumeUuid;
         final File codeFile;
         final String installerPackageName;
@@ -21732,22 +21749,13 @@
                         "3rd party apps are not allowed on internal storage");
             }
 
-            if (pkg.applicationInfo.isExternalAsec()) {
-                currentAsec = true;
-                currentVolumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
-            } else if (pkg.applicationInfo.isForwardLocked()) {
-                currentAsec = true;
-                currentVolumeUuid = "forward_locked";
-            } else {
-                currentAsec = false;
-                currentVolumeUuid = ps.volumeUuid;
+            currentVolumeUuid = ps.volumeUuid;
 
-                final File probe = new File(pkg.codePath);
-                final File probeOat = new File(probe, "oat");
-                if (!probe.isDirectory() || !probeOat.isDirectory()) {
-                    throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
-                            "Move only supported for modern cluster style installs");
-                }
+            final File probe = new File(pkg.codePath);
+            final File probeOat = new File(probe, "oat");
+            if (!probe.isDirectory() || !probeOat.isDirectory()) {
+                throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+                        "Move only supported for modern cluster style installs");
             }
 
             if (Objects.equals(currentVolumeUuid, volumeUuid)) {
@@ -21784,12 +21792,11 @@
         final boolean moveCompleteApp;
         final File measurePath;
 
+        installFlags = INSTALL_INTERNAL;
         if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
-            installFlags = INSTALL_INTERNAL;
-            moveCompleteApp = !currentAsec;
+            moveCompleteApp = true;
             measurePath = Environment.getDataAppDirectory(volumeUuid);
         } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
-            installFlags = INSTALL_EXTERNAL;
             moveCompleteApp = false;
             measurePath = storage.getPrimaryPhysicalVolume().getPath();
         } else {
@@ -21801,9 +21808,6 @@
                         "Move location not mounted private volume");
             }
 
-            Preconditions.checkState(!currentAsec);
-
-            installFlags = INSTALL_INTERNAL;
             moveCompleteApp = true;
             measurePath = Environment.getDataAppDirectory(volumeUuid);
         }
@@ -22588,7 +22592,7 @@
         private SigningDetails getSigningDetails(int uid) {
             synchronized (mPackages) {
                 final int appId = UserHandle.getAppId(uid);
-                final Object obj = mSettings.getUserIdLPr(appId);
+                final Object obj = mSettings.getSettingLPr(appId);
                 if (obj != null) {
                     if (obj instanceof SharedUserSetting) {
                         return ((SharedUserSetting) obj).signatures.mSigningDetails;
@@ -23590,6 +23594,30 @@
         return mProtectedPackages.isPackageStateProtected(userId, packageName);
     }
 
+    @Override
+    public void sendDeviceCustomizationReadyBroadcast() {
+        mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY,
+                "sendDeviceCustomizationReadyBroadcast");
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
+            intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+            final IActivityManager am = ActivityManager.getService();
+            final String[] requiredPermissions = {
+                Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
+            };
+            try {
+                am.broadcastIntent(null, intent, null, null, 0, null, null, requiredPermissions,
+                        android.app.AppOpsManager.OP_NONE, null, false, false, UserHandle.USER_ALL);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     static class ActiveInstallSession {
         private final String mPackageName;
         private final File mStagedDir;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 6f275ec..77f8c3a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2193,9 +2193,6 @@
         boolean replaceExisting = true;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
-                case "-l":
-                    sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
-                    break;
                 case "-r": // ignore
                     break;
                 case "-R":
@@ -2210,9 +2207,6 @@
                 case "-t":
                     sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
                     break;
-                case "-s":
-                    sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
-                    break;
                 case "-f":
                     sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
                     break;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index b850613..2c2cc7e 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -152,10 +152,6 @@
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0;
     }
 
-    public boolean isForwardLocked() {
-        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
-    }
-
     public boolean isSystem() {
         return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 239dc99..fbf5439 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -64,7 +64,6 @@
                 | ApplicationInfo.PRIVATE_FLAG_VENDOR
                 | ApplicationInfo.PRIVATE_FLAG_PRODUCT
                 | ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES
-                | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK
                 | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
     }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6009bd3..c524dba 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -381,11 +381,9 @@
     final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers =
             new SparseArray<CrossProfileIntentResolver>();
 
-    final ArrayMap<String, SharedUserSetting> mSharedUsers =
-            new ArrayMap<String, SharedUserSetting>();
-    private final ArrayList<Object> mUserIds = new ArrayList<Object>();
-    private final SparseArray<Object> mOtherUserIds =
-            new SparseArray<Object>();
+    final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<>();
+    private final ArrayList<SettingBase> mAppIds = new ArrayList<>();
+    private final SparseArray<SettingBase> mOtherAppIds = new SparseArray<>();
 
     // For reading/writing settings file.
     private final ArrayList<Signature> mPastSignatures =
@@ -519,7 +517,7 @@
         SharedUserSetting s = mSharedUsers.get(name);
         if (s == null && create) {
             s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
-            s.userId = newUserIdLPw(s);
+            s.userId = acquireAndRegisterNewAppIdLPw(s);
             if (s.userId < 0) {
                 // < 0 means we couldn't assign a userid; throw exception
                 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
@@ -612,7 +610,7 @@
                 cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, parentPackageName,
                 childPackageNames, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames);
         p.appId = uid;
-        if (addUserIdLPw(uid, p, name)) {
+        if (registerExistingAppIdLPw(uid, p, name)) {
             mPackages.put(name, p);
             return p;
         }
@@ -635,7 +633,7 @@
         }
         s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
         s.userId = uid;
-        if (addUserIdLPw(uid, s, name)) {
+        if (registerExistingAppIdLPw(uid, s, name)) {
             mSharedUsers.put(name, s);
             return s;
         }
@@ -885,13 +883,13 @@
      * Registers a user ID with the system. Potentially allocates a new user ID.
      * @throws PackageManagerException If a user ID could not be allocated.
      */
-    void addUserToSettingLPw(PackageSetting p) throws PackageManagerException {
+    void registerAppIdLPw(PackageSetting p) throws PackageManagerException {
         if (p.appId == 0) {
             // Assign new user ID
-            p.appId = newUserIdLPw(p);
+            p.appId = acquireAndRegisterNewAppIdLPw(p);
         } else {
             // Add new setting to list of user IDs
-            addUserIdLPw(p.appId, p, p.name);
+            registerExistingAppIdLPw(p.appId, p, p.name);
         }
         if (p.appId < 0) {
             PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -972,14 +970,14 @@
 
         // If the we know about this user id, we have to update it as it
         // has to point to the same PackageSetting instance as the package.
-        Object userIdPs = getUserIdLPr(p.appId);
+        Object userIdPs = getSettingLPr(p.appId);
         if (sharedUser == null) {
             if (userIdPs != null && userIdPs != p) {
-                replaceUserIdLPw(p.appId, p);
+                replaceAppIdLPw(p.appId, p);
             }
         } else {
             if (userIdPs != null && userIdPs != sharedUser) {
-                replaceUserIdLPw(p.appId, sharedUser);
+                replaceAppIdLPw(p.appId, sharedUser);
             }
         }
 
@@ -1083,11 +1081,11 @@
                 p.sharedUser.removePackage(p);
                 if (p.sharedUser.packages.size() == 0) {
                     mSharedUsers.remove(p.sharedUser.name);
-                    removeUserIdLPw(p.sharedUser.userId);
+                    removeAppIdLPw(p.sharedUser.userId);
                     return p.sharedUser.userId;
                 }
             } else {
-                removeUserIdLPw(p.appId);
+                removeAppIdLPw(p.appId);
                 return p.appId;
             }
         }
@@ -1115,65 +1113,69 @@
         mInstallerPackages.remove(packageName);
     }
 
-    private boolean addUserIdLPw(int uid, Object obj, Object name) {
-        if (uid > Process.LAST_APPLICATION_UID) {
+    /** Returns true if the requested AppID was valid and not already registered. */
+    private boolean registerExistingAppIdLPw(int appId, SettingBase obj, Object name) {
+        if (appId > Process.LAST_APPLICATION_UID) {
             return false;
         }
 
-        if (uid >= Process.FIRST_APPLICATION_UID) {
-            int N = mUserIds.size();
-            final int index = uid - Process.FIRST_APPLICATION_UID;
-            while (index >= N) {
-                mUserIds.add(null);
-                N++;
+        if (appId >= Process.FIRST_APPLICATION_UID) {
+            int size = mAppIds.size();
+            final int index = appId - Process.FIRST_APPLICATION_UID;
+            // fill the array until our index becomes valid
+            while (index >= size) {
+                mAppIds.add(null);
+                size++;
             }
-            if (mUserIds.get(index) != null) {
+            if (mAppIds.get(index) != null) {
                 PackageManagerService.reportSettingsProblem(Log.ERROR,
-                        "Adding duplicate user id: " + uid
+                        "Adding duplicate app id: " + appId
                         + " name=" + name);
                 return false;
             }
-            mUserIds.set(index, obj);
+            mAppIds.set(index, obj);
         } else {
-            if (mOtherUserIds.get(uid) != null) {
+            if (mOtherAppIds.get(appId) != null) {
                 PackageManagerService.reportSettingsProblem(Log.ERROR,
-                        "Adding duplicate shared id: " + uid
+                        "Adding duplicate shared id: " + appId
                                 + " name=" + name);
                 return false;
             }
-            mOtherUserIds.put(uid, obj);
+            mOtherAppIds.put(appId, obj);
         }
         return true;
     }
 
-    public Object getUserIdLPr(int uid) {
-        if (uid >= Process.FIRST_APPLICATION_UID) {
-            final int N = mUserIds.size();
-            final int index = uid - Process.FIRST_APPLICATION_UID;
-            return index < N ? mUserIds.get(index) : null;
+    /** Gets the setting associated with the provided App ID */
+    public SettingBase getSettingLPr(int appId) {
+        if (appId >= Process.FIRST_APPLICATION_UID) {
+            final int size = mAppIds.size();
+            final int index = appId - Process.FIRST_APPLICATION_UID;
+            return index < size ? mAppIds.get(index) : null;
         } else {
-            return mOtherUserIds.get(uid);
+            return mOtherAppIds.get(appId);
         }
     }
 
-    private void removeUserIdLPw(int uid) {
-        if (uid >= Process.FIRST_APPLICATION_UID) {
-            final int N = mUserIds.size();
-            final int index = uid - Process.FIRST_APPLICATION_UID;
-            if (index < N) mUserIds.set(index, null);
+    /** Unregisters the provided app ID. */
+    void removeAppIdLPw(int appId) {
+        if (appId >= Process.FIRST_APPLICATION_UID) {
+            final int size = mAppIds.size();
+            final int index = appId - Process.FIRST_APPLICATION_UID;
+            if (index < size) mAppIds.set(index, null);
         } else {
-            mOtherUserIds.remove(uid);
+            mOtherAppIds.remove(appId);
         }
-        setFirstAvailableUid(uid+1);
+        setFirstAvailableUid(appId + 1);
     }
 
-    private void replaceUserIdLPw(int uid, Object obj) {
-        if (uid >= Process.FIRST_APPLICATION_UID) {
-            final int N = mUserIds.size();
-            final int index = uid - Process.FIRST_APPLICATION_UID;
-            if (index < N) mUserIds.set(index, obj);
+    private void replaceAppIdLPw(int appId, SettingBase obj) {
+        if (appId >= Process.FIRST_APPLICATION_UID) {
+            final int size = mAppIds.size();
+            final int index = appId - Process.FIRST_APPLICATION_UID;
+            if (index < size) mAppIds.set(index, obj);
         } else {
-            mOtherUserIds.put(uid, obj);
+            mOtherAppIds.put(appId, obj);
         }
     }
 
@@ -3157,7 +3159,7 @@
         for (int i = 0; i < N; i++) {
             final PackageSetting p = mPendingPackages.get(i);
             final int sharedUserId = p.getSharedUserId();
-            final Object idObj = getUserIdLPr(sharedUserId);
+            final Object idObj = getSettingLPr(sharedUserId);
             if (idObj instanceof SharedUserSetting) {
                 final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
                 p.sharedUser = sharedUser;
@@ -3202,7 +3204,7 @@
         final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
         while (disabledIt.hasNext()) {
             final PackageSetting disabledPs = disabledIt.next();
-            final Object id = getUserIdLPr(disabledPs.appId);
+            final Object id = getSettingLPr(disabledPs.appId);
             if (id != null && id instanceof SharedUserSetting) {
                 disabledPs.sharedUser = (SharedUserSetting) id;
             }
@@ -3656,7 +3658,6 @@
 
     private static int PRE_M_APP_INFO_FLAG_HIDDEN = 1<<27;
     private static int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1<<28;
-    private static int PRE_M_APP_INFO_FLAG_FORWARD_LOCK = 1<<29;
     private static int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1<<30;
 
     private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
@@ -3756,15 +3757,11 @@
                     if ((pkgFlags & PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE) != 0) {
                         pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
                     }
-                    if ((pkgFlags & PRE_M_APP_INFO_FLAG_FORWARD_LOCK) != 0) {
-                        pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
-                    }
                     if ((pkgFlags & PRE_M_APP_INFO_FLAG_PRIVILEGED) != 0) {
                         pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
                     }
                     pkgFlags &= ~(PRE_M_APP_INFO_FLAG_HIDDEN
                             | PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE
-                            | PRE_M_APP_INFO_FLAG_FORWARD_LOCK
                             | PRE_M_APP_INFO_FLAG_PRIVILEGED);
                 } else {
                     // For backward compatibility
@@ -4206,24 +4203,24 @@
         }
     }
 
-    // Returns -1 if we could not find an available UserId to assign
-    private int newUserIdLPw(Object obj) {
+    /** Returns a new AppID or -1 if we could not find an available AppID to assign */
+    private int acquireAndRegisterNewAppIdLPw(SettingBase obj) {
         // Let's be stupidly inefficient for now...
-        final int N = mUserIds.size();
-        for (int i = mFirstAvailableUid; i < N; i++) {
-            if (mUserIds.get(i) == null) {
-                mUserIds.set(i, obj);
+        final int size = mAppIds.size();
+        for (int i = mFirstAvailableUid; i < size; i++) {
+            if (mAppIds.get(i) == null) {
+                mAppIds.set(i, obj);
                 return Process.FIRST_APPLICATION_UID + i;
             }
         }
 
         // None left?
-        if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {
+        if (size > (Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID)) {
             return -1;
         }
 
-        mUserIds.add(obj);
-        return Process.FIRST_APPLICATION_UID + N;
+        mAppIds.add(obj);
+        return Process.FIRST_APPLICATION_UID + size;
     }
 
     public VerifierDeviceIdentity getVerifierDeviceIdentityLPw() {
@@ -4258,11 +4255,48 @@
         return false;
     }
 
+    /**
+     * Returns the disabled {@link PackageSetting} for the provided package name if one exists,
+     * {@code null} otherwise.
+     */
+    @Nullable
     public PackageSetting getDisabledSystemPkgLPr(String name) {
         PackageSetting ps = mDisabledSysPackages.get(name);
         return ps;
     }
 
+    /**
+     * Returns the disabled {@link PackageSetting} for the provided enabled {@link PackageSetting}
+     * if one exists, {@code null} otherwise.
+     */
+    @Nullable
+    public PackageSetting getDisabledSystemPkgLPr(PackageSetting enabledPackageSetting) {
+        if (enabledPackageSetting == null) {
+            return null;
+        }
+        return getDisabledSystemPkgLPr(enabledPackageSetting.name);
+    }
+
+    /**
+     * Fetches an array of the child {@link PackageSetting}s for all child package names referenced
+     * by the provided parent {@link PackageSetting} or {@code null} if no children are referenced.
+     *
+     * Note: Any child packages not found will be null in the returned array.
+     */
+    @Nullable
+    public PackageSetting[] getChildSettingsLPr(PackageSetting parentPackageSetting) {
+        if (parentPackageSetting == null || !parentPackageSetting.hasChildPackages()) {
+            return null;
+        }
+        final int childCount = parentPackageSetting.childPackageNames.size();
+        PackageSetting[] children =
+                new PackageSetting[childCount];
+        for (int i = 0; i < childCount; i++) {
+            children[i] = mPackages.get(parentPackageSetting.childPackageNames.get(i));
+        }
+        return children;
+    }
+
     boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
         final PackageSetting ps = mPackages.get(componentInfo.packageName);
         if (ps == null) return false;
@@ -4428,7 +4462,6 @@
             ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
             ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE",
             ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE, "DIRECT_BOOT_AWARE",
-            ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
             ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS, "HAS_DOMAIN_URLS",
             ApplicationInfo.PRIVATE_FLAG_HIDDEN, "HIDDEN",
             ApplicationInfo.PRIVATE_FLAG_INSTANT, "EPHEMERAL",
diff --git a/services/core/java/com/android/server/pm/ShareTargetInfo.java b/services/core/java/com/android/server/pm/ShareTargetInfo.java
new file mode 100644
index 0000000..9e8b73e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShareTargetInfo.java
@@ -0,0 +1,101 @@
+/*
+ * 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;
+
+import android.text.TextUtils;
+
+/**
+ * Represents a Share Target definition, read from the application's manifest (shortcuts.xml)
+ */
+class ShareTargetInfo {
+    static class TargetData {
+        final String mScheme;
+        final String mHost;
+        final String mPort;
+        final String mPath;
+        final String mPathPattern;
+        final String mPathPrefix;
+        final String mMimeType;
+
+        TargetData(String scheme, String host, String port, String path, String pathPattern,
+                String pathPrefix, String mimeType) {
+            mScheme = scheme;
+            mHost = host;
+            mPort = port;
+            mPath = path;
+            mPathPattern = pathPattern;
+            mPathPrefix = pathPrefix;
+            mMimeType = mimeType;
+        }
+
+        public void toStringInner(StringBuilder strBuilder) {
+            if (!TextUtils.isEmpty(mScheme)) {
+                strBuilder.append(" scheme=").append(mScheme);
+            }
+            if (!TextUtils.isEmpty(mHost)) {
+                strBuilder.append(" host=").append(mHost);
+            }
+            if (!TextUtils.isEmpty(mPort)) {
+                strBuilder.append(" port=").append(mPort);
+            }
+            if (!TextUtils.isEmpty(mPath)) {
+                strBuilder.append(" path=").append(mPath);
+            }
+            if (!TextUtils.isEmpty(mPathPattern)) {
+                strBuilder.append(" pathPattern=").append(mPathPattern);
+            }
+            if (!TextUtils.isEmpty(mPathPrefix)) {
+                strBuilder.append(" pathPrefix=").append(mPathPrefix);
+            }
+            if (!TextUtils.isEmpty(mMimeType)) {
+                strBuilder.append(" mimeType=").append(mMimeType);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder strBuilder = new StringBuilder();
+            toStringInner(strBuilder);
+            return strBuilder.toString();
+        }
+    }
+
+    final TargetData[] mTargetData;
+    final String mTargetClass;
+    final String[] mCategories;
+
+    ShareTargetInfo(TargetData[] data, String targetClass, String[] categories) {
+        mTargetData = data;
+        mTargetClass = targetClass;
+        mCategories = categories;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder strBuilder = new StringBuilder();
+        strBuilder.append("targetClass=").append(mTargetClass);
+        for (int i = 0; i < mTargetData.length; i++) {
+            strBuilder.append(" data={");
+            mTargetData[i].toStringInner(strBuilder);
+            strBuilder.append("}");
+        }
+        for (int i = 0; i < mCategories.length; i++) {
+            strBuilder.append(" category=").append(mCategories[i]);
+        }
+
+        return strBuilder.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 92e261a..83f0fde 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -111,6 +111,11 @@
     final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
 
     /**
+     * All the share targets from the package
+     */
+    private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0);
+
+    /**
      * # of times the package has called rate-limited APIs.
      */
     private int mApiCallCount;
@@ -739,15 +744,16 @@
         List<ShortcutInfo> newManifestShortcutList = null;
         try {
             newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
-                    getPackageName(), getPackageUserId());
+                    getPackageName(), getPackageUserId(), mShareTargets);
         } catch (IOException|XmlPullParserException e) {
             Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
         }
         final int manifestShortcutSize = newManifestShortcutList == null ? 0
                 : newManifestShortcutList.size();
         if (ShortcutService.DEBUG) {
-            Slog.d(TAG, String.format("Package %s has %d manifest shortcut(s)",
-                    getPackageName(), manifestShortcutSize));
+            Slog.d(TAG,
+                    String.format("Package %s has %d manifest shortcut(s), and %d share target(s)",
+                            getPackageName(), manifestShortcutSize, mShareTargets.size()));
         }
         if (isNewApp && (manifestShortcutSize == 0)) {
             // If it's a new app, and it doesn't have manifest shortcuts, then nothing to do.
@@ -1657,6 +1663,11 @@
         return new ArrayList<>(mShortcuts.values());
     }
 
+    @VisibleForTesting
+    List<ShareTargetInfo> getAllShareTargetsForTest() {
+        return new ArrayList<>(mShareTargets);
+    }
+
     @Override
     public void verifyStates() {
         super.verifyStates();
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index 866c46c..90f08c3 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.pm;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
@@ -55,10 +56,14 @@
     private static final String TAG_SHORTCUT = "shortcut";
     private static final String TAG_INTENT = "intent";
     private static final String TAG_CATEGORIES = "categories";
+    private static final String TAG_SHARE_TARGET = "share-target";
+    private static final String TAG_DATA = "data";
+    private static final String TAG_CATEGORY = "category";
 
     @Nullable
-    public static List<ShortcutInfo> parseShortcuts(ShortcutService service,
-            String packageName, @UserIdInt int userId) throws IOException, XmlPullParserException {
+    public static List<ShortcutInfo> parseShortcuts(ShortcutService service, String packageName,
+            @UserIdInt int userId, @NonNull List<ShareTargetInfo> outShareTargets)
+            throws IOException, XmlPullParserException {
         if (ShortcutService.DEBUG) {
             Slog.d(TAG, String.format("Scanning package %s for manifest shortcuts on user %d",
                     packageName, userId));
@@ -69,6 +74,7 @@
         }
 
         List<ShortcutInfo> result = null;
+        outShareTargets.clear();
 
         try {
             final int size = activities.size();
@@ -82,8 +88,8 @@
                         service.getActivityInfoWithMetadata(
                         activityInfoNoMetadata.getComponentName(), userId);
                 if (activityInfoWithMetadata != null) {
-                    result = parseShortcutsOneFile(
-                            service, activityInfoWithMetadata, packageName, userId, result);
+                    result = parseShortcutsOneFile(service, activityInfoWithMetadata, packageName,
+                            userId, result, outShareTargets);
                 }
             }
         } catch (RuntimeException e) {
@@ -99,7 +105,8 @@
     private static List<ShortcutInfo> parseShortcutsOneFile(
             ShortcutService service,
             ActivityInfo activityInfo, String packageName, @UserIdInt int userId,
-            List<ShortcutInfo> result) throws IOException, XmlPullParserException {
+            List<ShortcutInfo> result, @NonNull List<ShareTargetInfo> outShareTargets)
+            throws IOException, XmlPullParserException {
         if (ShortcutService.DEBUG) {
             Slog.d(TAG, String.format(
                     "Checking main activity %s", activityInfo.getComponentName()));
@@ -126,9 +133,19 @@
             // after parsing <intent>.  We keep the current one in here.
             ShortcutInfo currentShortcut = null;
 
+            // We instantiate ShareTargetInfo at <share-target>, but add it to outShareTargets at
+            // </share-target>, after parsing <data> and <category>. We keep the current one here.
+            ShareTargetInfo currentShareTarget = null;
+
+            // Keeps parsed categories for both ShortcutInfo and ShareTargetInfo
             Set<String> categories = null;
+
+            // Keeps parsed intents for ShortcutInfo
             final ArrayList<Intent> intents = new ArrayList<>();
 
+            // Keeps parsed data fields for ShareTargetInfo
+            final ArrayList<ShareTargetInfo.TargetData> dataList = new ArrayList<>();
+
             outer:
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                     && (type != XmlPullParser.END_TAG || parser.getDepth() > 0)) {
@@ -194,6 +211,32 @@
                     continue;
                 }
 
+                // When a share-target tag is closing, publish.
+                if ((type == XmlPullParser.END_TAG) && (depth == 2)
+                        && (TAG_SHARE_TARGET.equals(tag))) {
+                    if (currentShareTarget == null) {
+                        // ShareTarget was invalid.
+                        continue;
+                    }
+                    final ShareTargetInfo sti = currentShareTarget;
+                    currentShareTarget = null; // Make sure to null out for the next iteration.
+
+                    if (categories == null || categories.isEmpty() || dataList.isEmpty()) {
+                        // Incomplete ShareTargetInfo.
+                        continue;
+                    }
+
+                    final ShareTargetInfo newShareTarget = new ShareTargetInfo(
+                            dataList.toArray(new ShareTargetInfo.TargetData[dataList.size()]),
+                            sti.mTargetClass, categories.toArray(new String[categories.size()]));
+                    outShareTargets.add(newShareTarget);
+                    if (ShortcutService.DEBUG) {
+                        Slog.d(TAG, "ShareTarget added: " + newShareTarget.toString());
+                    }
+                    categories = null;
+                    dataList.clear();
+                }
+
                 // Otherwise, just look at start tags.
                 if (type != XmlPullParser.START_TAG) {
                     continue;
@@ -224,6 +267,17 @@
                     categories = null;
                     continue;
                 }
+                if (depth == 2 && TAG_SHARE_TARGET.equals(tag)) {
+                    final ShareTargetInfo sti = parseShareTargetAttributes(service, attrs);
+                    if (sti == null) {
+                        // ShareTarget was invalid.
+                        continue;
+                    }
+                    currentShareTarget = sti;
+                    categories = null;
+                    dataList.clear();
+                    continue;
+                }
                 if (depth == 3 && TAG_INTENT.equals(tag)) {
                     if ((currentShortcut == null)
                             || !currentShortcut.isEnabled()) {
@@ -258,6 +312,34 @@
                     categories.add(name);
                     continue;
                 }
+                if (depth == 3 && TAG_CATEGORY.equals(tag)) {
+                    if ((currentShareTarget == null)) {
+                        continue;
+                    }
+                    final String name = parseCategory(service, attrs);
+                    if (TextUtils.isEmpty(name)) {
+                        Log.e(TAG, "Empty category found. activity=" + activity);
+                        continue;
+                    }
+
+                    if (categories == null) {
+                        categories = new ArraySet<>();
+                    }
+                    categories.add(name);
+                    continue;
+                }
+                if (depth == 3 && TAG_DATA.equals(tag)) {
+                    if ((currentShareTarget == null)) {
+                        continue;
+                    }
+                    final ShareTargetInfo.TargetData data = parseShareTargetData(service, attrs);
+                    if (data == null) {
+                        Log.e(TAG, "Invalid data tag found. activity=" + activity);
+                        continue;
+                    }
+                    dataList.add(data);
+                    continue;
+                }
 
                 Log.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
             }
@@ -369,4 +451,57 @@
                 null, // bitmap path
                 disabledReason);
     }
+
+    private static String parseCategory(ShortcutService service, AttributeSet attrs) {
+        final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+                R.styleable.IntentCategory);
+        try {
+            if (sa.getType(R.styleable.IntentCategory_name) != TypedValue.TYPE_STRING) {
+                Log.w(TAG, "android:name must be string literal.");
+                return null;
+            }
+            return sa.getString(R.styleable.IntentCategory_name);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ShareTargetInfo parseShareTargetAttributes(ShortcutService service,
+            AttributeSet attrs) {
+        final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+                R.styleable.Intent);
+        try {
+            String targetClass = sa.getString(R.styleable.Intent_targetClass);
+            if (TextUtils.isEmpty(targetClass)) {
+                Log.w(TAG, "android:targetClass must be provided.");
+                return null;
+            }
+            return new ShareTargetInfo(null, targetClass, null);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ShareTargetInfo.TargetData parseShareTargetData(ShortcutService service,
+            AttributeSet attrs) {
+        final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+                R.styleable.AndroidManifestData);
+        try {
+            if (sa.getType(R.styleable.AndroidManifestData_mimeType) != TypedValue.TYPE_STRING) {
+                Log.w(TAG, "android:mimeType must be string literal.");
+                return null;
+            }
+            String scheme = sa.getString(R.styleable.AndroidManifestData_scheme);
+            String host = sa.getString(R.styleable.AndroidManifestData_host);
+            String port = sa.getString(R.styleable.AndroidManifestData_port);
+            String path = sa.getString(R.styleable.AndroidManifestData_path);
+            String pathPattern = sa.getString(R.styleable.AndroidManifestData_pathPattern);
+            String pathPrefix = sa.getString(R.styleable.AndroidManifestData_pathPrefix);
+            String mimeType = sa.getString(R.styleable.AndroidManifestData_mimeType);
+            return new ShareTargetInfo.TargetData(scheme, host, port, path, pathPattern, pathPrefix,
+                    mimeType);
+        } finally {
+            sa.recycle();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index b9c3048..2b773f4 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -41,8 +41,8 @@
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
@@ -98,8 +98,8 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
-import com.android.server.LocalServices;
 import com.android.internal.util.StatLogger;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.ShortcutUser.PackageWithUser;
 
@@ -1172,7 +1172,7 @@
                 return true;
             }
         }
-        
+
         // If the local copy says the user is locked, check with AM for the actual state, since
         // the user might just have been unlocked.
         // Note we just don't use isUserUnlockingOrUnlocked() here, because it'll return false
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 505e4ee..1fd9b69 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -294,13 +294,14 @@
      */
     public void detectLocaleChange() {
         final String currentLocales = mService.injectGetLocaleTagsForUser(mUserId);
-        if (getKnownLocales().equals(currentLocales)) {
+        if (!TextUtils.isEmpty(mKnownLocales) && mKnownLocales.equals(currentLocales)) {
             return;
         }
         if (ShortcutService.DEBUG) {
-            Slog.d(TAG, "Locale changed from " + currentLocales + " to " + mKnownLocales
+            Slog.d(TAG, "Locale changed from " + mKnownLocales + " to " + currentLocales
                     + " for user " + mUserId);
         }
+
         mKnownLocales = currentLocales;
 
         forAllPackages(pkg -> {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 774134c..21cc14e 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -195,7 +195,6 @@
         // STOPSHIP(b/112545973): remove once feature enabled by default
         if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
             MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
-            MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_AUDIO);
         }
     }
 
@@ -203,10 +202,8 @@
     static {
         // STOPSHIP(b/112545973): remove once feature enabled by default
         if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
-            MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
-            MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_IMAGES);
             MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
-            MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_MEDIA_VIDEO);
+            MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
         }
     }
 
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 4124210..b390eeb 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -30,6 +30,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.text.TextUtils;
@@ -42,6 +44,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
+import java.io.FileDescriptor;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -334,5 +337,13 @@
             return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
                     false, true, name, null);
         }
+
+        @Override
+        public void onShellCommand(FileDescriptor in, FileDescriptor out,
+                FileDescriptor err, String[] args, ShellCallback callback,
+                ResultReceiver resultReceiver) {
+            (new RoleManagerShellCommand(this)).exec(
+                    this, in, out, err, args, callback, resultReceiver);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/role/RoleManagerShellCommand.java b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
new file mode 100644
index 0000000..e1977ef
--- /dev/null
+++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
@@ -0,0 +1,134 @@
+/*
+ * 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.role;
+
+import android.app.role.IRoleManager;
+import android.app.role.IRoleManagerCallback;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+class RoleManagerShellCommand extends ShellCommand {
+    private final IRoleManager mRoleManager;
+
+    RoleManagerShellCommand(IRoleManager roleManager) {
+        mRoleManager = roleManager;
+    }
+
+    private class Callback extends IRoleManagerCallback.Stub {
+        private final CompletableFuture<Void> mResult = new CompletableFuture<>();
+
+        public int waitForResult() {
+            try {
+                mResult.get(5, TimeUnit.SECONDS);
+                return 0;
+            } catch (Exception e) {
+                getErrPrintWriter().println("Error: " + e.toString());
+                return -1;
+            }
+        }
+
+        @Override
+        public void onSuccess() {
+            mResult.complete(null);
+        }
+
+        @Override
+        public void onFailure() {
+            mResult.completeExceptionally(new RuntimeException("Failed"));
+        }
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+
+        PrintWriter pw = getOutPrintWriter();
+        try {
+            switch (cmd) {
+                case "add-role-holder":
+                    return runAddRoleHolder();
+                case "remove-role-holder":
+                    return runRemoveRoleHolder();
+                case "clear-role-holders":
+                    return runClearRoleHolders();
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        } catch (RemoteException e) {
+            pw.println("Remote exception: " + e);
+        }
+        return -1;
+    }
+
+    private int getUserIdMaybe() {
+        int userId = UserHandle.USER_SYSTEM;
+        String option = getNextOption();
+        if (option != null && option.equals("--user")) {
+            userId = UserHandle.parseUserArg(getNextArgRequired());
+        }
+        return userId;
+    }
+
+    private int runAddRoleHolder() throws RemoteException {
+        int userId = getUserIdMaybe();
+        String roleName = getNextArgRequired();
+        String packageName = getNextArgRequired();
+
+        Callback callback = new Callback();
+        mRoleManager.addRoleHolderAsUser(roleName, packageName, userId, callback);
+        return callback.waitForResult();
+    }
+
+    private int runRemoveRoleHolder() throws RemoteException {
+        int userId = getUserIdMaybe();
+        String roleName = getNextArgRequired();
+        String packageName = getNextArgRequired();
+
+        Callback callback = new Callback();
+        mRoleManager.removeRoleHolderAsUser(roleName, packageName, userId, callback);
+        return callback.waitForResult();
+    }
+
+    private int runClearRoleHolders() throws RemoteException {
+        int userId = getUserIdMaybe();
+        String roleName = getNextArgRequired();
+
+        Callback callback = new Callback();
+        mRoleManager.clearRoleHoldersAsUser(roleName, userId, callback);
+        return callback.waitForResult();
+    }
+
+    @Override
+    public void onHelp() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Role manager (role) commands:");
+        pw.println("  help");
+        pw.println("    Print this help text.");
+        pw.println();
+        pw.println("  add-role-holder [--user USER_ID] ROLE PACKAGE");
+        pw.println("  remove-role-holder [--user USER_ID] ROLE PACKAGE");
+        pw.println("  clear-role-holders [--user USER_ID] ROLE");
+        pw.println();
+    }
+}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 6f6846d..aa11e1e 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -51,6 +51,7 @@
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -194,8 +195,10 @@
             "/system/bin/traced",  // Perfetto.
             "/system/bin/traced_probes",  // Perfetto.
             "webview_zygote",
-            "zygote",
-            "zygote64",
+            // Temporarily excluded zygote to investigate its forking consequences in
+            // NativeProcessMemoryState.
+            // "zygote",
+            // "zygote64",
     };
 
 
@@ -1515,6 +1518,21 @@
         pulledData.add(e);
     }
 
+    private void pullBuildInformation(int tagId,
+            long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+        e.writeString(Build.FINGERPRINT);
+        e.writeString(Build.BRAND);
+        e.writeString(Build.PRODUCT);
+        e.writeString(Build.DEVICE);
+        e.writeString(Build.VERSION.RELEASE);
+        e.writeString(Build.ID);
+        e.writeString(Build.VERSION.INCREMENTAL);
+        e.writeString(Build.TYPE);
+        e.writeString(Build.TAGS);
+        pulledData.add(e);
+    }
+
     private BatteryStatsHelper getBatteryStatsHelper() {
         if (mBatteryStatsHelper == null) {
             final long callingToken = Binder.clearCallingIdentity();
@@ -1808,6 +1826,10 @@
                 pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.BUILD_INFORMATION: {
+                pullBuildInformation(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             case StatsLog.PROCESS_CPU_TIME: {
                 pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 1eb44a0..0d66a2c8 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -19,6 +19,7 @@
 import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
 
 import android.app.ActivityThread;
+import android.app.Notification;
 import android.app.StatusBarManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -1080,14 +1081,16 @@
     }
 
     @Override
-    public void onNotificationActionClick(String key, int actionIndex, NotificationVisibility nv) {
+    public void onNotificationActionClick(
+            String key, int actionIndex, Notification.Action action, NotificationVisibility nv,
+            boolean generatedByAssistant) {
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
         long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key,
-                    actionIndex, nv);
+                    actionIndex, action, nv, generatedByAssistant);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -1177,12 +1180,14 @@
     }
 
     @Override
-    public void onNotificationSmartReplySent(String key, int replyIndex)
+    public void onNotificationSmartReplySent(
+            String key, int replyIndex, CharSequence reply, boolean generatedByAssistant)
             throws RemoteException {
         enforceStatusBarService();
         long identity = Binder.clearCallingIdentity();
         try {
-            mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex);
+            mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex, reply,
+                    generatedByAssistant);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index da0a794..4b413e5 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -49,7 +49,7 @@
 import libcore.icu.ICU;
 import libcore.timezone.TzDataSetVersion;
 import libcore.timezone.TimeZoneFinder;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
 
 import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
 import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainerListener.java b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
similarity index 60%
rename from services/core/java/com/android/server/wm/RootWindowContainerListener.java
rename to services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
index f413e3f7..7310af3 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainerListener.java
+++ b/services/core/java/com/android/server/updates/ConversationActionsInstallReceiver.java
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.server.wm;
+package com.android.server.updates;
 
-/**
- * Interface used by the creator of {@link RootWindowContainerController} to notify the changes to
- * the display container in activity manager.
- */
-public interface RootWindowContainerListener extends WindowContainerListener {
-    /** Called when the z-order of display is changed. */
-    void onChildPositionChanged(DisplayWindowController childController, int position);
+public class ConversationActionsInstallReceiver extends ConfigUpdateInstallReceiver {
+
+    public ConversationActionsInstallReceiver() {
+        super(
+                "/data/misc/textclassifier/",
+                "actions_suggestions.model",
+                "metadata/actions_suggestions",
+                "version");
+    }
 }
diff --git a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
index dfe02ec..05dad21 100644
--- a/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/LangIdInstallReceiver.java
@@ -21,8 +21,8 @@
     public LangIdInstallReceiver() {
         super(
             "/data/misc/textclassifier/",
-            "textclassifier.langid.model",
-            "metadata/langid",
+            "lang_id.model",
+            "metadata/lang_id",
             "version");
     }
 }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 409d2b4..6ede423 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -88,7 +88,6 @@
 import android.util.Xml;
 import android.view.Display;
 import android.view.IWindowManager;
-import android.view.WindowManager;
 
 import com.android.internal.R;
 import com.android.internal.content.PackageMonitor;
@@ -487,6 +486,8 @@
     private void generateCrop(WallpaperData wallpaper) {
         boolean success = false;
 
+        // Only generate crop for default display.
+        final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
         Rect cropHint = new Rect(wallpaper.cropHint);
 
         if (DEBUG) {
@@ -494,7 +495,7 @@
                     + Integer.toHexString(wallpaper.whichPending)
                     + " to " + wallpaper.cropFile.getName()
                     + " crop=(" + cropHint.width() + 'x' + cropHint.height()
-                    + ") dim=(" + wallpaper.width + 'x' + wallpaper.height + ')');
+                    + ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
         }
 
         // Analyse the source; needed in multiple cases
@@ -533,11 +534,11 @@
             }
 
             // scale if the crop height winds up not matching the recommended metrics
-            needScale = (wallpaper.height != cropHint.height());
+            needScale = (wpData.mHeight != cropHint.height());
 
             if (DEBUG) {
                 Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
-                Slog.v(TAG, "dims: w=" + wallpaper.width + " h=" + wallpaper.height);
+                Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
                 Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
                 Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
             }
@@ -567,7 +568,7 @@
                     // just let the decode take care of it because we also want to remap where the
                     // cropHint rectangle lies in the decoded [super]rect.
                     final BitmapFactory.Options scaler;
-                    final int actualScale = cropHint.height() / wallpaper.height;
+                    final int actualScale = cropHint.height() / wpData.mHeight;
                     int scale = 1;
                     while (2*scale < actualScale) {
                         scale *= 2;
@@ -593,17 +594,18 @@
                         cropHint.offsetTo(0, 0);
                         cropHint.right /= scale;    // adjust by downsampling factor
                         cropHint.bottom /= scale;
-                        final float heightR = ((float)wallpaper.height) / ((float)cropHint.height());
+                        final float heightR =
+                                ((float) wpData.mHeight) / ((float) cropHint.height());
                         if (DEBUG) {
                             Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
                         }
                         final int destWidth = (int)(cropHint.width() * heightR);
                         final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
-                                destWidth, wallpaper.height, true);
+                                destWidth, wpData.mHeight, true);
                         if (DEBUG) {
                             Slog.v(TAG, "Final extract:");
-                            Slog.v(TAG, "  dims: w=" + wallpaper.width
-                                    + " h=" + wallpaper.height);
+                            Slog.v(TAG, "  dims: w=" + wpData.mWidth
+                                    + " h=" + wpData.mHeight);
                             Slog.v(TAG, "   out: w=" + finalCrop.getWidth()
                                     + " h=" + finalCrop.getHeight());
                         }
@@ -670,13 +672,13 @@
                     if (connector == null) return;
                     connector.disconnectLocked();
                     mLastWallpaper.connection.removeDisplayConnector(displayId);
+                    mLastWallpaper.removeDisplayData(displayId);
                 }
             }
         }
 
         @Override
         public void onDisplayChanged(int displayId) {
-            // TODO(b/115486823) Review that do we need to handle display changes.
         }
     };
 
@@ -778,16 +780,23 @@
         private RemoteCallbackList<IWallpaperManagerCallback> callbacks
                 = new RemoteCallbackList<IWallpaperManagerCallback>();
 
-        int width = -1;
-        int height = -1;
+        private static final class DisplayData {
+            int mWidth = -1;
+            int mHeight = -1;
+            final Rect mPadding = new Rect(0, 0, 0, 0);
+            final int mDisplayId;
+
+            DisplayData(int displayId) {
+                mDisplayId = displayId;
+            }
+        }
+        private SparseArray<DisplayData> mDisplayDatas = new SparseArray<>();
 
         /**
          * The crop hint supplied for displaying a subset of the source image
          */
         final Rect cropHint = new Rect(0, 0, 0, 0);
 
-        final Rect padding = new Rect(0, 0, 0, 0);
-
         WallpaperData(int userId, String inputFileName, String cropFileName) {
             this.userId = userId;
             final File wallpaperDir = getWallpaperDir(userId);
@@ -803,6 +812,44 @@
         boolean sourceExists() {
             return wallpaperFile.exists();
         }
+
+        void removeDisplayData(int displayId) {
+            mDisplayDatas.remove(displayId);
+        }
+    }
+
+    private WallpaperData.DisplayData getDisplayDataOrCreate(WallpaperData data, int displayId) {
+        WallpaperData.DisplayData wpdData = data.mDisplayDatas.get(displayId);
+        if (wpdData == null) {
+            wpdData = new WallpaperData.DisplayData(displayId);
+            ensureSaneWallpaperDisplaySize(wpdData, displayId);
+            data.mDisplayDatas.append(displayId, wpdData);
+        }
+        return wpdData;
+    }
+
+    private void ensureSaneWallpaperDisplaySize(WallpaperData.DisplayData wpdData,
+            int displayId) {
+        // We always want to have some reasonable width hint.
+        final int baseSize = getMaximumSizeDimension(displayId);
+        if (wpdData.mWidth < baseSize) {
+            wpdData.mWidth = baseSize;
+        }
+        if (wpdData.mHeight < baseSize) {
+            wpdData.mHeight = baseSize;
+        }
+    }
+
+    private int getMaximumSizeDimension(int displayId) {
+        Display display = mDisplayManager.getDisplay(displayId);
+        return display.getMaximumSizeDimension();
+    }
+
+    void forEachDisplayData(WallpaperData data, Consumer<WallpaperData.DisplayData> action) {
+        for (int i = data.mDisplayDatas.size() - 1; i >= 0; i--) {
+            final WallpaperData.DisplayData wpdData = data.mDisplayDatas.valueAt(i);
+            action.accept(wpdData);
+        }
     }
 
     int makeWallpaperIdLocked() {
@@ -830,9 +877,11 @@
             }
 
             void ensureStatusHandled() {
+                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(mWallpaper,
+                        mDisplayId);
                 if (mDimensionsChanged) {
                     try {
-                        mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
+                        mEngine.setDesiredSize(wpdData.mWidth, wpdData.mHeight);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failed to set wallpaper dimensions", e);
                     }
@@ -840,7 +889,7 @@
                 }
                 if (mPaddingChanged) {
                     try {
-                        mEngine.setDisplayPadding(mWallpaper.padding);
+                        mEngine.setDisplayPadding(wpdData.mPadding);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failed to set wallpaper padding", e);
                     }
@@ -857,16 +906,16 @@
                     return;
                 }
 
+                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                        mDisplayId);
                 try {
-                    // TODO(b/115486823) Consider the size of non-default display
                     connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
-                            wallpaper.width, wallpaper.height,
-                            wallpaper.padding, mDisplayId);
+                            wpdData.mWidth, wpdData.mHeight,
+                            wpdData.mPadding, mDisplayId);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Failed attaching wallpaper on display", e);
-                    // TODO(b/115486823) Failed when attaching a new engine, however, other engines
-                    // may still working. Should we abandon them all or just ignore this one.
-                    if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating) {
+                    if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating
+                            && connection.getConnectedEngineSize() == 0) {
                         bindWallpaperComponentLocked(null /* componentName */, false /* force */,
                                 false /* fromUser */, wallpaper, null /* reply */);
                     }
@@ -952,11 +1001,20 @@
 
         void forEachDisplayConnector(Consumer<DisplayConnector> action) {
             for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
-                final DisplayConnector connector = mDisplayConnector.get(i);
+                final DisplayConnector connector = mDisplayConnector.valueAt(i);
                 action.accept(connector);
             }
         }
 
+        int getConnectedEngineSize() {
+            int engineSize = 0;
+            for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
+                final DisplayConnector connector = mDisplayConnector.valueAt(i);
+                if (connector.mEngine != null) engineSize++;
+            }
+            return engineSize;
+        }
+
         DisplayConnector getDisplayConnectorOrCreate(int displayId) {
             DisplayConnector connector = mDisplayConnector.get(displayId);
             if (connector == null) {
@@ -1128,7 +1186,8 @@
                         Slog.w(TAG, "Failed to set ambient mode state", e);
                     }
                 }
-                // TODO(b/115486823) Extends for secondary display.
+                // TODO(multi-display) So far, we have shared the same wallpaper on each display.
+                // Once we have multiple wallpapers on multiple displays, please complete here.
                 if (displayId == DEFAULT_DISPLAY) {
                     try {
                         // This will trigger onComputeColors in the wallpaper engine.
@@ -1560,7 +1619,7 @@
                     wallpaper.wallpaperComponent = wallpaper.nextWallpaperComponent;
                     final WallpaperData fallback = new WallpaperData(wallpaper.userId,
                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
-                    ensureSaneWallpaperData(fallback);
+                    ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY);
                     bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
                     mWaitingForUnlock = true;
                 }
@@ -1705,8 +1764,15 @@
         return false;
     }
 
-    // TODO(b/115486823) Extends this method with specific display.
-    public void setDimensionHints(int width, int height, String callingPackage)
+    private boolean isValidDisplay(int displayId) {
+        return mDisplayManager.getDisplay(displayId) != null;
+    }
+
+    /**
+     * Sets the dimension hint for the wallpaper. These hints indicate the desired
+     * minimum width and height for the wallpaper in a particular display.
+     */
+    public void setDimensionHints(int width, int height, String callingPackage, int displayId)
             throws RemoteException {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
         if (!isWallpaperSupported(callingPackage)) {
@@ -1719,90 +1785,113 @@
                 throw new IllegalArgumentException("width and height must be > 0");
             }
 
-            if (width != wallpaper.width || height != wallpaper.height) {
-                wallpaper.width = width;
-                wallpaper.height = height;
-                saveSettingsLocked(userId);
+            if (!isValidDisplay(displayId)) {
+                throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+            }
+
+            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+            if (width != wpdData.mWidth || height != wpdData.mHeight) {
+                wpdData.mWidth = width;
+                wpdData.mHeight = height;
+                if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
-                    // TODO(b/115486823) Extends this method with specific display.
-                    final IWallpaperEngine engine = wallpaper.connection
-                            .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+                    final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+                            .getDisplayConnectorOrCreate(displayId);
+                    final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
                     if (engine != null) {
                         try {
                             engine.setDesiredSize(width, height);
                         } catch (RemoteException e) {
                         }
                         notifyCallbacksLocked(wallpaper);
-                    } else if (wallpaper.connection.mService != null) {
+                    } else if (wallpaper.connection.mService != null && connector != null) {
                         // We've attached to the service but the engine hasn't attached back to us
                         // yet. This means it will be created with the previous dimensions, so we
                         // need to update it to the new dimensions once it attaches.
-                        // TODO(b/115486823) Extends this method with specific display.
-                        wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
-                                .mDimensionsChanged = true;
+                        connector.mDimensionsChanged = true;
                     }
                 }
             }
         }
     }
 
-    public int getWidthHint() throws RemoteException {
+    /**
+     * Returns the desired minimum width for the wallpaper in a particular display.
+     */
+    public int getWidthHint(int displayId) throws RemoteException {
         synchronized (mLock) {
+            if (!isValidDisplay(displayId)) {
+                throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+            }
             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             if (wallpaper != null) {
-                return wallpaper.width;
+                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                        displayId);
+                return wpdData.mWidth;
             } else {
                 return 0;
             }
         }
     }
 
-    public int getHeightHint() throws RemoteException {
+    /**
+     * Returns the desired minimum height for the wallpaper in a particular display.
+     */
+    public int getHeightHint(int displayId) throws RemoteException {
         synchronized (mLock) {
+            if (!isValidDisplay(displayId)) {
+                throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+            }
             WallpaperData wallpaper = mWallpaperMap.get(UserHandle.getCallingUserId());
             if (wallpaper != null) {
-                return wallpaper.height;
+                final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                        displayId);
+                return wpdData.mHeight;
             } else {
                 return 0;
             }
         }
     }
 
-    // TODO(b/115486823) Extends this method with specific display.
-    public void setDisplayPadding(Rect padding, String callingPackage) {
+    /**
+     * Sets extra padding that we would like the wallpaper to have outside of the display.
+     */
+    public void setDisplayPadding(Rect padding, String callingPackage, int displayId) {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
         if (!isWallpaperSupported(callingPackage)) {
             return;
         }
         synchronized (mLock) {
+            if (!isValidDisplay(displayId)) {
+                throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+            }
             int userId = UserHandle.getCallingUserId();
             WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
             if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
                 throw new IllegalArgumentException("padding must be positive: " + padding);
             }
 
-            if (!padding.equals(wallpaper.padding)) {
-                wallpaper.padding.set(padding);
-                saveSettingsLocked(userId);
+            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper, displayId);
+            if (!padding.equals(wpdData.mPadding)) {
+                wpdData.mPadding.set(padding);
+                if (displayId == DEFAULT_DISPLAY) saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
-                    // TODO(b/115486823) Extends this method with specific display.
-                    final IWallpaperEngine engine = wallpaper.connection
-                            .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+                    final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+                            .getDisplayConnectorOrCreate(displayId);
+                    final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
                     if (engine != null) {
                         try {
                             engine.setDisplayPadding(padding);
                         } catch (RemoteException e) {
                         }
                         notifyCallbacksLocked(wallpaper);
-                    } else if (wallpaper.connection.mService != null) {
+                    } else if (wallpaper.connection.mService != null && connector != null) {
                         // We've attached to the service but the engine hasn't attached back to us
                         // yet. This means it will be created with the previous dimensions, so we
                         // need to update it to the new dimensions once it attaches.
-                        // TODO(b/115486823) Extends this method with specific display.
-                        wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
-                                .mPaddingChanged = true;
+                        connector.mPaddingChanged = true;
                     }
                 }
             }
@@ -1850,10 +1939,13 @@
                 // user switch)
                 return null;
             }
+            // Only for default display.
+            final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                    DEFAULT_DISPLAY);
             try {
                 if (outParams != null) {
-                    outParams.putInt("width", wallpaper.width);
-                    outParams.putInt("height", wallpaper.height);
+                    outParams.putInt("width", wpdData.mWidth);
+                    outParams.putInt("height", wpdData.mHeight);
                 }
                 if (cb != null) {
                     wallpaper.callbacks.register(cb);
@@ -2075,10 +2167,14 @@
         // We know a-priori that there is no lock-only wallpaper currently
         WallpaperData lockWP = new WallpaperData(userId,
                 WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
+        final WallpaperData.DisplayData lockWPDData = getDisplayDataOrCreate(lockWP,
+                DEFAULT_DISPLAY);
+        final WallpaperData.DisplayData sysWPDData = getDisplayDataOrCreate(sysWP,
+                DEFAULT_DISPLAY);
         lockWP.wallpaperId = sysWP.wallpaperId;
         lockWP.cropHint.set(sysWP.cropHint);
-        lockWP.width = sysWP.width;
-        lockWP.height = sysWP.height;
+        lockWPDData.mWidth = sysWPDData.mWidth;
+        lockWPDData.mHeight = sysWPDData.mHeight;
         lockWP.allowBackup = sysWP.allowBackup;
         lockWP.primaryColors = sysWP.primaryColors;
 
@@ -2478,27 +2574,29 @@
         if (DEBUG) {
             Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
         }
+        final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                DEFAULT_DISPLAY);
         out.startTag(null, tag);
         out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
-        out.attribute(null, "width", Integer.toString(wallpaper.width));
-        out.attribute(null, "height", Integer.toString(wallpaper.height));
+        out.attribute(null, "width", Integer.toString(wpdData.mWidth));
+        out.attribute(null, "height", Integer.toString(wpdData.mHeight));
 
         out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
         out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
         out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
         out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
 
-        if (wallpaper.padding.left != 0) {
-            out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
+        if (wpdData.mPadding.left != 0) {
+            out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left));
         }
-        if (wallpaper.padding.top != 0) {
-            out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
+        if (wpdData.mPadding.top != 0) {
+            out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top));
         }
-        if (wallpaper.padding.right != 0) {
-            out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
+        if (wpdData.mPadding.right != 0) {
+            out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right));
         }
-        if (wallpaper.padding.bottom != 0) {
-            out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
+        if (wpdData.mPadding.bottom != 0) {
+            out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom));
         }
 
         if (wallpaper.primaryColors != null) {
@@ -2601,14 +2699,14 @@
                     wallpaper = new WallpaperData(userId,
                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
                     mLockWallpaperMap.put(userId, wallpaper);
-                    ensureSaneWallpaperData(wallpaper);
+                    ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
                 } else {
                     // sanity fallback: we're in bad shape, but establishing a known
                     // valid system+lock WallpaperData will keep us from dying.
                     Slog.wtf(TAG, "Didn't find wallpaper in non-lock case!");
                     wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
                     mWallpaperMap.put(userId, wallpaper);
-                    ensureSaneWallpaperData(wallpaper);
+                    ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
                 }
             }
         }
@@ -2637,6 +2735,8 @@
             }
         }
         boolean success = false;
+        final WallpaperData.DisplayData wpdData = getDisplayDataOrCreate(wallpaper,
+                DEFAULT_DISPLAY);
         try {
             stream = new FileInputStream(file);
             XmlPullParser parser = Xml.newPullParser();
@@ -2663,8 +2763,8 @@
                         }
 
                         if (DEBUG) {
-                            Slog.v(TAG, "mWidth:" + wallpaper.width);
-                            Slog.v(TAG, "mHeight:" + wallpaper.height);
+                            Slog.v(TAG, "mWidth:" + wpdData.mWidth);
+                            Slog.v(TAG, "mHeight:" + wpdData.mHeight);
                             Slog.v(TAG, "cropRect:" + wallpaper.cropHint);
                             Slog.v(TAG, "primaryColors:" + wallpaper.primaryColors);
                             Slog.v(TAG, "mName:" + wallpaper.name);
@@ -2700,10 +2800,10 @@
         IoUtils.closeQuietly(stream);
 
         if (!success) {
-            wallpaper.width = -1;
-            wallpaper.height = -1;
+            wpdData.mWidth = -1;
+            wpdData.mHeight = -1;
             wallpaper.cropHint.set(0, 0, 0, 0);
-            wallpaper.padding.set(0, 0, 0, 0);
+            wpdData.mPadding.set(0, 0, 0, 0);
             wallpaper.name = "";
 
             mLockWallpaperMap.remove(userId);
@@ -2717,26 +2817,22 @@
             }
         }
 
-        ensureSaneWallpaperData(wallpaper);
+        ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
         if (lockWallpaper != null) {
-            ensureSaneWallpaperData(lockWallpaper);
+            ensureSaneWallpaperData(lockWallpaper, DEFAULT_DISPLAY);
         }
     }
 
-    private void ensureSaneWallpaperData(WallpaperData wallpaper) {
-        // We always want to have some reasonable width hint.
-        int baseSize = getMaximumSizeDimension();
-        if (wallpaper.width < baseSize) {
-            wallpaper.width = baseSize;
-        }
-        if (wallpaper.height < baseSize) {
-            wallpaper.height = baseSize;
-        }
-        // and crop, if not previously specified
-        if (wallpaper.cropHint.width() <= 0
-                || wallpaper.cropHint.height() <= 0) {
-            wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height);
+    private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
+        final WallpaperData.DisplayData size = getDisplayDataOrCreate(wallpaper, displayId);
+
+        if (displayId == DEFAULT_DISPLAY) {
+            // crop, if not previously specified
+            if (wallpaper.cropHint.width() <= 0
+                    || wallpaper.cropHint.height() <= 0) {
+                wallpaper.cropHint.set(0, 0, size.mWidth, size.mHeight);
+            }
         }
     }
 
@@ -2752,19 +2848,20 @@
             wallpaper.wallpaperId = makeWallpaperIdLocked();
         }
 
+        final WallpaperData.DisplayData wpData = getDisplayDataOrCreate(wallpaper, DEFAULT_DISPLAY);
+
         if (!keepDimensionHints) {
-            wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
-            wallpaper.height = Integer.parseInt(parser
-                    .getAttributeValue(null, "height"));
+            wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
+            wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
         }
         wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
         wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
         wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
         wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
-        wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
-        wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
-        wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
-        wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
+        wpData.mPadding.left = getAttributeInt(parser, "paddingLeft", 0);
+        wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
+        wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
+        wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
         int colorsCount = getAttributeInt(parser, "colorsCount", 0);
         if (colorsCount > 0) {
             Color primary = null, secondary = null, tertiary = null;
@@ -2787,12 +2884,6 @@
         wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
     }
 
-    private int getMaximumSizeDimension() {
-        WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
-        Display d = wm.getDefaultDisplay();
-        return d.getMaximumSizeDimension();
-    }
-
     // Called by SystemBackupAgent after files are restored to disk.
     public void settingsRestored() {
         // Verify caller is the system
@@ -2832,7 +2923,7 @@
                 if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
                         + " id=" + wallpaper.wallpaperId);
                 if (success) {
-                    generateCrop(wallpaper);    // based on the new image + metadata
+                    generateCrop(wallpaper); // based on the new image + metadata
                     bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
                             wallpaper, null);
                 }
@@ -2937,12 +3028,16 @@
                 WallpaperData wallpaper = mWallpaperMap.valueAt(i);
                 pw.print(" User "); pw.print(wallpaper.userId);
                     pw.print(": id="); pw.println(wallpaper.wallpaperId);
-                pw.print("  mWidth=");
-                    pw.print(wallpaper.width);
-                    pw.print(" mHeight=");
-                    pw.println(wallpaper.height);
+                forEachDisplayData(wallpaper, wpSize -> {
+                    pw.print("  displayId=");
+                    pw.println(wpSize.mDisplayId);
+                    pw.print("  mWidth=");
+                    pw.print(wpSize.mWidth);
+                    pw.print("  mHeight=");
+                    pw.println(wpSize.mHeight);
+                    pw.print("  mPadding="); pw.println(wpSize.mPadding);
+                });
                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
-                pw.print("  mPadding="); pw.println(wallpaper.padding);
                 pw.print("  mName=");  pw.println(wallpaper.name);
                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
                 pw.print("  mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
@@ -2973,11 +3068,15 @@
             for (int i = 0; i < mLockWallpaperMap.size(); i++) {
                 WallpaperData wallpaper = mLockWallpaperMap.valueAt(i);
                 pw.print(" User "); pw.print(wallpaper.userId);
-                    pw.print(": id="); pw.println(wallpaper.wallpaperId);
-                pw.print("  mWidth="); pw.print(wallpaper.width);
-                    pw.print(" mHeight="); pw.println(wallpaper.height);
+                pw.print(": id="); pw.println(wallpaper.wallpaperId);
+                forEachDisplayData(wallpaper, wpSize -> {
+                    pw.print("  displayId=");
+                    pw.println(wpSize.mDisplayId);
+                    pw.print("  mWidth="); pw.print(wpSize.mWidth);
+                    pw.print("  mHeight="); pw.println(wpSize.mHeight);
+                    pw.print("  mPadding="); pw.println(wpSize.mPadding);
+                });
                 pw.print("  mCropHint="); pw.println(wallpaper.cropHint);
-                pw.print("  mPadding="); pw.println(wallpaper.padding);
                 pw.print("  mName=");  pw.println(wallpaper.name);
                 pw.print("  mAllowBackup="); pw.println(wallpaper.allowBackup);
             }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index e4d1cfe..fe0b5c2 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,9 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -706,7 +706,7 @@
                         mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
                         surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
                                 .setName(SURFACE_TITLE)
-                                .setSize(mTempPoint.x, mTempPoint.y) // not a typo
+                                .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo
                                 .setFormat(PixelFormat.TRANSLUCENT)
                                 .build();
                     } catch (OutOfResourcesException oore) {
@@ -784,7 +784,7 @@
                 public void updateSize() {
                     synchronized (mService.mGlobalLock) {
                         mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
-                        mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
+                        mSurfaceControl.setBufferSize(mTempPoint.x, mTempPoint.y);
                         invalidate(mDirtyRect);
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 94ab207..84750b3 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -39,8 +39,6 @@
 import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
 import static com.android.server.am.ActivityDisplayProto.STACKS;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.wm.ActivityStackSupervisor.TAG_STATES;
 import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
@@ -48,6 +46,8 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 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 android.annotation.Nullable;
 import android.app.ActivityOptions;
@@ -84,7 +84,8 @@
      */
     private static int sNextFreeStackId = 0;
 
-    private ActivityStackSupervisor mSupervisor;
+    private ActivityTaskManagerService mService;
+    private RootActivityContainer mRootActivityContainer;
     /** Actual Display this object tracks. */
     int mDisplayId;
     Display mDisplay;
@@ -141,8 +142,9 @@
 
     private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
 
-    ActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
-        mSupervisor = supervisor;
+    ActivityDisplay(RootActivityContainer root, Display display) {
+        mRootActivityContainer = root;
+        mService = root.mService;
         mDisplayId = display.getDisplayId();
         mDisplay = display;
         mWindowContainerController = createWindowContainerController();
@@ -168,7 +170,7 @@
         if (displayId != DEFAULT_DISPLAY) {
             final int displayState = mDisplay.getState();
             if (displayState == Display.STATE_OFF && mOffToken == null) {
-                mOffToken = mSupervisor.mService.acquireSleepToken("Display-off", displayId);
+                mOffToken = mService.acquireSleepToken("Display-off", displayId);
             } else if (displayState == Display.STATE_ON && mOffToken != null) {
                 mOffToken.release();
                 mOffToken = null;
@@ -194,7 +196,7 @@
                 + " to displayId=" + mDisplayId + " position=" + position);
         addStackReferenceIfNeeded(stack);
         positionChildAt(stack, position);
-        mSupervisor.mService.updateSleepIfNeededLocked();
+        mService.updateSleepIfNeededLocked();
     }
 
     void removeChild(ActivityStack stack) {
@@ -206,7 +208,7 @@
         }
         removeStackReferenceIfNeeded(stack);
         releaseSelfIfNeeded();
-        mSupervisor.mService.updateSleepIfNeededLocked();
+        mService.updateSleepIfNeededLocked();
         onStackOrderChanged();
     }
 
@@ -257,7 +259,7 @@
             final ActivityStack currentFocusedStack = getFocusedStack();
             if (currentFocusedStack != prevFocusedStack) {
                 mLastFocusedStack = prevFocusedStack;
-                EventLogTags.writeAmFocusedStack(mSupervisor.mCurrentUser, mDisplayId,
+                EventLogTags.writeAmFocusedStack(mRootActivityContainer.mCurrentUser, mDisplayId,
                         currentFocusedStack == null ? -1 : currentFocusedStack.getStackId(),
                         mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(),
                         updateLastFocusedStackReason);
@@ -414,10 +416,10 @@
             }
         }
 
-        final ActivityTaskManagerService service = mSupervisor.mService;
-        if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
-                service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
-                service.mSupportsPictureInPicture, activityType)) {
+        if (!isWindowingModeSupported(windowingMode, mService.mSupportsMultiWindow,
+                mService.mSupportsSplitScreenMultiWindow,
+                mService.mSupportsFreeformWindowManagement, mService.mSupportsPictureInPicture,
+                activityType)) {
             throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
                     + windowingMode);
         }
@@ -430,10 +432,12 @@
     <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
             int stackId, boolean onTop) {
         if (windowingMode == WINDOWING_MODE_PINNED) {
-            return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
+            return (T) new PinnedActivityStack(this, stackId,
+                    mRootActivityContainer.mStackSupervisor, onTop);
         }
-        return (T) new ActivityStack(
-                        this, stackId, mSupervisor, windowingMode, activityType, onTop);
+        return (T) new ActivityStack(this, stackId,
+                mRootActivityContainer.mStackSupervisor, windowingMode, activityType,
+                onTop);
     }
 
     /**
@@ -548,7 +552,7 @@
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = mStacks.get(stackNdx);
             // TODO(b/111541062): Check if resumed activity on this display instead
-            if (!mSupervisor.isTopDisplayFocusedStack(stack)
+            if (!mRootActivityContainer.isTopDisplayFocusedStack(stack)
                     && stack.getResumedActivity() != null) {
                 if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
                         " mResumedActivity=" + stack.getResumedActivity());
@@ -613,7 +617,7 @@
                 if (stack.getWindowingMode() != windowingMode) {
                     continue;
                 }
-                mSupervisor.removeStack(stack);
+                mRootActivityContainer.mStackSupervisor.removeStack(stack);
             }
         }
     }
@@ -628,7 +632,7 @@
             for (int i = mStacks.size() - 1; i >= 0; --i) {
                 final ActivityStack stack = mStacks.get(i);
                 if (stack.getActivityType() == activityType) {
-                    mSupervisor.removeStack(stack);
+                    mRootActivityContainer.mStackSupervisor.removeStack(stack);
                 }
             }
         }
@@ -690,7 +694,7 @@
     }
 
     private void onSplitScreenModeDismissed() {
-        mSupervisor.mWindowManager.deferSurfaceLayout();
+        mRootActivityContainer.mWindowManager.deferSurfaceLayout();
         try {
             // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
             for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -714,12 +718,12 @@
                 mHomeStack.moveToFront("onSplitScreenModeDismissed");
                 topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
             }
-            mSupervisor.mWindowManager.continueSurfaceLayout();
+            mRootActivityContainer.mWindowManager.continueSurfaceLayout();
         }
     }
 
     private void onSplitScreenModeActivated() {
-        mSupervisor.mWindowManager.deferSurfaceLayout();
+        mRootActivityContainer.mWindowManager.deferSurfaceLayout();
         try {
             // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
             for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -734,7 +738,7 @@
                         false /* creating */);
             }
         } finally {
-            mSupervisor.mWindowManager.continueSurfaceLayout();
+            mRootActivityContainer.mWindowManager.continueSurfaceLayout();
         }
     }
 
@@ -829,11 +833,10 @@
     int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r,
         @Nullable TaskRecord task, int activityType) {
         // Make sure the windowing mode we are trying to use makes sense for what is supported.
-        final ActivityTaskManagerService service = mSupervisor.mService;
-        boolean supportsMultiWindow = service.mSupportsMultiWindow;
-        boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow;
-        boolean supportsFreeform = service.mSupportsFreeformWindowManagement;
-        boolean supportsPip = service.mSupportsPictureInPicture;
+        boolean supportsMultiWindow = mService.mSupportsMultiWindow;
+        boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
+        boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
+        boolean supportsPip = mService.mSupportsPictureInPicture;
         if (supportsMultiWindow) {
             if (task != null) {
                 supportsMultiWindow = task.isResizeable();
@@ -937,7 +940,7 @@
         // This activity can be considered the top running activity if we are not considering
         // the locked state, the keyguard isn't locked, or we can show when locked.
         if (topRunning != null && considerKeyguardState
-                && mSupervisor.getKeyguardController().isKeyguardLocked()
+                && mRootActivityContainer.mStackSupervisor.getKeyguardController().isKeyguardLocked()
                 && !topRunning.canShowWhenLocked()) {
             return null;
         }
@@ -1015,7 +1018,7 @@
 
     @Override
     protected ConfigurationContainer getParent() {
-        return mSupervisor;
+        return mRootActivityContainer;
     }
 
     boolean isPrivate() {
@@ -1048,8 +1051,8 @@
         // released (no more ActivityStack). But, we cannot release it at that moment or the
         // related WindowContainer and WindowContainerController will also be removed. So, we
         // set display as removed after reparenting stack finished.
-        final ActivityDisplay toDisplay = mSupervisor.getDefaultDisplay();
-        mSupervisor.beginDeferResume();
+        final ActivityDisplay toDisplay = mRootActivityContainer.getDefaultDisplay();
+        mRootActivityContainer.mStackSupervisor.beginDeferResume();
         try {
             int numStacks = mStacks.size();
             // Keep the order from bottom to top.
@@ -1075,7 +1078,7 @@
                 numStacks = mStacks.size();
             }
         } finally {
-            mSupervisor.endDeferResume();
+            mRootActivityContainer.mStackSupervisor.endDeferResume();
         }
         mRemoved = true;
 
@@ -1087,9 +1090,9 @@
         releaseSelfIfNeeded();
 
         if (!mAllSleepTokens.isEmpty()) {
-            mSupervisor.mSleepTokens.removeAll(mAllSleepTokens);
+            mRootActivityContainer.mSleepTokens.removeAll(mAllSleepTokens);
             mAllSleepTokens.clear();
-            mSupervisor.mService.updateSleepIfNeededLocked();
+            mService.updateSleepIfNeededLocked();
         }
     }
 
@@ -1097,8 +1100,9 @@
         if (mStacks.isEmpty() && mRemoved) {
             mWindowContainerController.removeContainer();
             mWindowContainerController = null;
-            mSupervisor.removeChild(this);
-            mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId);
+            mRootActivityContainer.removeChild(this);
+            mRootActivityContainer.mStackSupervisor
+                    .getKeyguardController().onDisplayRemoved(mDisplayId);
         }
     }
 
@@ -1127,7 +1131,7 @@
 
     boolean shouldSleep() {
         return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
-                && (mSupervisor.mService.mRunningVoice == null);
+                && (mService.mRunningVoice == null);
     }
 
     void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
@@ -1218,7 +1222,7 @@
 
     @Nullable
     ActivityRecord getHomeActivity() {
-        return getHomeActivityForUser(mSupervisor.mCurrentUser);
+        return getHomeActivityForUser(mRootActivityContainer.mCurrentUser);
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 1c08d03..416e133 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -277,7 +277,8 @@
         mLastLogTimeSecs = now;
 
         mWindowState = WINDOW_STATE_INVALID;
-        ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+        ActivityStack stack =
+                mSupervisor.mRootActivityContainer.getTopDisplayFocusedStack();
         if (stack == null) {
             return;
         }
@@ -289,7 +290,7 @@
 
         @WindowingMode int windowingMode = stack.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_PINNED) {
-            stack = mSupervisor.findStackBehind(stack);
+            stack = mSupervisor.mRootActivityContainer.findStackBehind(stack);
             windowingMode = stack.getWindowingMode();
         }
         switch (windowingMode) {
@@ -850,9 +851,10 @@
         builder.addTaggedData(FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW,
                 targetUidHasAnyVisibleWindow ? 1 : 0);
         builder.addTaggedData(FIELD_TARGET_WHITELIST_TAG, targetWhitelistTag);
-        builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
         builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0);
-        builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
+        if (intent != null) {
+            builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
+        }
         if (callerApp != null) {
             builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.mName);
             builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE,
@@ -881,29 +883,34 @@
                         (nowUptime - callerApp.getWhenUnimportant()));
             }
         }
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, r.realActivity.toShortString());
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
-        if (r.lastVisibleTime != 0) {
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
-                    (nowUptime - r.lastVisibleTime));
-        }
-        if (r.resultTo != null) {
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, r.resultTo.packageName);
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
-                    r.resultTo.shortComponentName);
-        }
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
-                r.visibleIgnoringKeyguard ? 1 : 0);
-        if (r.lastLaunchTime != 0) {
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
-                    (nowUptime - r.lastLaunchTime));
+        if (r != null) {
+            builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY,
+                    r.realActivity.toShortString());
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
+            if (r.lastVisibleTime != 0) {
+                builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
+                        (nowUptime - r.lastVisibleTime));
+            }
+            if (r.resultTo != null) {
+                builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME,
+                        r.resultTo.packageName);
+                builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
+                        r.resultTo.shortComponentName);
+            }
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
+                    r.visibleIgnoringKeyguard ? 1 : 0);
+            if (r.lastLaunchTime != 0) {
+                builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
+                        (nowUptime - r.lastLaunchTime));
+            }
         }
         mMetricsLogger.write(builder);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5e92b9e..b4aec35 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -18,7 +18,17 @@
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
 import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -63,6 +73,7 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
 import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
 import static android.content.res.Configuration.EMPTY;
@@ -111,12 +122,18 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService
+        .RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
 import static com.android.server.wm.TaskPersister.DEBUG;
 import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -147,6 +164,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Build;
@@ -167,11 +185,14 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IApplicationToken;
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.util.XmlUtils;
@@ -200,7 +221,7 @@
 /**
  * An entry in the history stack, representing an activity.
  */
-final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
+final class ActivityRecord extends ConfigurationContainer {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
@@ -225,7 +246,9 @@
 
     final ActivityTaskManagerService service; // owner
     final IApplicationToken.Stub appToken; // window manager token
-    AppWindowContainerController mWindowContainerController;
+    // TODO: Remove after unification
+    AppWindowToken mAppWindowToken;
+
     final ActivityInfo info; // all about me
     // TODO: This is duplicated state already contained in info.applicationInfo - remove
     ApplicationInfo appInfo; // information about activity's app
@@ -322,6 +345,7 @@
 
     private boolean inHistory;  // are we in the history stack?
     final ActivityStackSupervisor mStackSupervisor;
+    final RootActivityContainer mRootActivityContainer;
 
     static final int STARTING_WINDOW_NOT_SHOWN = 0;
     static final int STARTING_WINDOW_SHOWN = 1;
@@ -769,10 +793,16 @@
     }
 
     /**
-     * See {@link AppWindowContainerController#setWillCloseOrEnterPip(boolean)}
+     * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
+     * This information helps AWT know that the app is in the process of pausing before it gets the
+     * signal on the WM side.
      */
     void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
-        getWindowContainerController().setWillCloseOrEnterPip(willCloseOrEnterPip);
+        if (mAppWindowToken == null) {
+            return;
+        }
+
+        mAppWindowToken.setWillCloseOrEnterPip(willCloseOrEnterPip);
     }
 
     static class Token extends IApplicationToken.Stub {
@@ -844,6 +874,7 @@
             boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
             ActivityOptions options, ActivityRecord sourceRecord) {
         service = _service;
+        mRootActivityContainer = _service.mRootActivityContainer;
         appToken = new Token(this, _intent);
         info = aInfo;
         launchedFromPid = _launchedFromPid;
@@ -991,13 +1022,9 @@
         return hasProcess() && app.hasThread();
     }
 
-    AppWindowContainerController getWindowContainerController() {
-        return mWindowContainerController;
-    }
-
-    void createWindowContainer() {
-        if (mWindowContainerController != null) {
-            throw new IllegalArgumentException("Window container=" + mWindowContainerController
+    void createAppWindowToken() {
+        if (mAppWindowToken != null) {
+            throw new IllegalArgumentException("App Window Token=" + mAppWindowToken
                     + " already created for r=" + this);
         }
 
@@ -1010,12 +1037,31 @@
         // Make sure override configuration is up-to-date before using to create window controller.
         updateOverrideConfiguration();
 
-        mWindowContainerController = new AppWindowContainerController(taskController, appToken,
-                realActivity, this, Integer.MAX_VALUE /* add on top */, info.screenOrientation,
-                fullscreen, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
-                task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
-                appInfo.targetSdkVersion, mRotationAnimationHint,
-                ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
+        // TODO: remove after unification
+        mAppWindowToken = service.mWindowManager.mRoot.getAppWindowToken(appToken.asBinder());
+        if (mAppWindowToken != null) {
+            // TODO: Should this throw an exception instead?
+            Slog.w(TAG, "Attempted to add existing app token: " + appToken);
+        } else {
+            final Task container = taskController.mContainer;
+            if (container == null) {
+                throw new IllegalArgumentException("AppWindowContainerController: invalid "
+                        + " controller=" + taskController);
+            }
+            mAppWindowToken = createAppWindow(service.mWindowManager, appToken,
+                    task.voiceSession != null, container.getDisplayContent(),
+                    ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this)
+                            * 1000000L, fullscreen,
+                    (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, appInfo.targetSdkVersion,
+                    info.screenOrientation, mRotationAnimationHint, info.configChanges,
+                    mLaunchTaskBehind, isAlwaysFocusable());
+            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) {
+                Slog.v(TAG, "addAppToken: "
+                        + mAppWindowToken + " controller=" + taskController + " at "
+                        + Integer.MAX_VALUE);
+            }
+            container.addChild(mAppWindowToken, Integer.MAX_VALUE /* add on top */);
+        }
 
         task.addActivityToTop(this);
 
@@ -1026,17 +1072,51 @@
         mLastReportedPictureInPictureMode = inPinnedWindowingMode();
     }
 
+    boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
+            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+        if (DEBUG_STARTING_WINDOW) {
+            Slog.v(TAG, "setAppStartingWindow: token=" + appToken
+                    + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
+                    + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
+                    + " allowTaskSnapshot=" + allowTaskSnapshot);
+        }
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + appToken);
+            return false;
+        }
+        return mAppWindowToken.addStartingWindow(pkg, theme, compatInfo, nonLocalizedLabel,
+                labelRes, icon, logo, windowFlags, transferFrom, newTask, taskSwitch,
+                processRunning, allowTaskSnapshot, activityCreated, fromRecents);
+    }
+
+    // TODO: Remove after unification
+    @VisibleForTesting
+    AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
+            boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
+            boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
+            int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
+            boolean alwaysFocusable) {
+        return new AppWindowToken(service, token, realActivity, voiceInteraction, dc,
+                inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
+                rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
+                this);
+    }
+
     void removeWindowContainer() {
-        // Do not try to remove a window container if we have already removed it.
-        if (mWindowContainerController == null) {
+        if (service.mWindowManager.mRoot == null) return;
+
+        final DisplayContent dc = service.mWindowManager.mRoot.getDisplayContent(
+                getDisplayId());
+        if (dc == null) {
+            Slog.w(TAG, "removeWindowContainer: Attempted to remove token: "
+                    + appToken + " from non-existing displayId=" + getDisplayId());
             return;
         }
-
         // Resume key dispatching if it is currently paused before we remove the container.
         resumeKeyDispatchingLocked();
-
-        mWindowContainerController.removeContainer(getDisplayId());
-        mWindowContainerController = null;
+        dc.removeAppToken(appToken.asBinder());
     }
 
     /**
@@ -1044,6 +1124,10 @@
      * should ensure that the {@param newTask} is not already the parent of this activity.
      */
     void reparent(TaskRecord newTask, int position, String reason) {
+        if (mAppWindowToken == null) {
+            Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken);
+            return;
+        }
         final TaskRecord prevTask = task;
         if (prevTask == newTask) {
             throw new IllegalArgumentException(reason + ": task=" + newTask
@@ -1059,8 +1143,7 @@
                     + " r=" + this + " (" + prevTask.getStackId() + ")");
         }
 
-        // Must reparent first in window manager
-        mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
+        mAppWindowToken.reparent(newTask.getWindowContainerController(), position);
 
         // Reparenting prevents informing the parent stack of activity removal in the case that
         // the new stack has the same parent. we must manually signal here if this is not the case.
@@ -1200,7 +1283,7 @@
     }
 
     boolean isFocusable() {
-        return mStackSupervisor.isFocusable(this, isAlwaysFocusable());
+        return mRootActivityContainer.isFocusable(this, isAlwaysFocusable());
     }
 
     boolean isResizeable() {
@@ -1353,7 +1436,7 @@
             return false;
         }
 
-        if (mStackSupervisor.getTopResumedActivity() == this) {
+        if (mRootActivityContainer.getTopResumedActivity() == this) {
             if (DEBUG_FOCUS) {
                 Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this);
             }
@@ -1366,7 +1449,7 @@
 
         stack.moveToFront(reason, task);
         // Report top activity change to tracking services and WM
-        if (mStackSupervisor.getTopResumedActivity() == this) {
+        if (mRootActivityContainer.getTopResumedActivity() == this) {
             // TODO(b/111361570): Support multiple focused apps in WM
             service.setResumedActivityUncheckLocked(this, reason);
         }
@@ -1490,7 +1573,7 @@
     void applyOptionsLocked() {
         if (pendingOptions != null
                 && pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) {
-            mWindowContainerController.applyOptionsLocked(pendingOptions, intent);
+            applyOptionsLocked(pendingOptions, intent);
             if (task == null) {
                 clearOptionsLocked(false /* withAbort */);
             } else {
@@ -1500,6 +1583,104 @@
         }
     }
 
+    /**
+     * Apply override app transition base on options & animation type.
+     */
+    void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
+        final int animationType = pendingOptions.getAnimationType();
+        final DisplayContent displayContent = mAppWindowToken.getDisplayContent();
+        switch (animationType) {
+            case ANIM_CUSTOM:
+                displayContent.mAppTransition.overridePendingAppTransition(
+                        pendingOptions.getPackageName(),
+                        pendingOptions.getCustomEnterResId(),
+                        pendingOptions.getCustomExitResId(),
+                        pendingOptions.getOnAnimationStartListener());
+                break;
+            case ANIM_CLIP_REVEAL:
+                displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
+                        pendingOptions.getStartX(), pendingOptions.getStartY(),
+                        pendingOptions.getWidth(), pendingOptions.getHeight());
+                if (intent.getSourceBounds() == null) {
+                    intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                            pendingOptions.getStartY(),
+                            pendingOptions.getStartX() + pendingOptions.getWidth(),
+                            pendingOptions.getStartY() + pendingOptions.getHeight()));
+                }
+                break;
+            case ANIM_SCALE_UP:
+                displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
+                        pendingOptions.getStartX(), pendingOptions.getStartY(),
+                        pendingOptions.getWidth(), pendingOptions.getHeight());
+                if (intent.getSourceBounds() == null) {
+                    intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                            pendingOptions.getStartY(),
+                            pendingOptions.getStartX() + pendingOptions.getWidth(),
+                            pendingOptions.getStartY() + pendingOptions.getHeight()));
+                }
+                break;
+            case ANIM_THUMBNAIL_SCALE_UP:
+            case ANIM_THUMBNAIL_SCALE_DOWN:
+                final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
+                final GraphicBuffer buffer = pendingOptions.getThumbnail();
+                displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
+                        pendingOptions.getStartX(), pendingOptions.getStartY(),
+                        pendingOptions.getOnAnimationStartListener(),
+                        scaleUp);
+                if (intent.getSourceBounds() == null && buffer != null) {
+                    intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                            pendingOptions.getStartY(),
+                            pendingOptions.getStartX() + buffer.getWidth(),
+                            pendingOptions.getStartY() + buffer.getHeight()));
+                }
+                break;
+            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
+            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
+                final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
+                final IAppTransitionAnimationSpecsFuture specsFuture =
+                        pendingOptions.getSpecsFuture();
+                if (specsFuture != null) {
+                    // TODO(multidisplay): Shouldn't be really used anymore from next CL.
+                    displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
+                            specsFuture, pendingOptions.getOnAnimationStartListener(),
+                            animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
+                } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
+                        && specs != null) {
+                    displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
+                            specs, pendingOptions.getOnAnimationStartListener(),
+                            pendingOptions.getAnimationFinishedListener(), false);
+                } else {
+                    displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
+                            pendingOptions.getThumbnail(),
+                            pendingOptions.getStartX(), pendingOptions.getStartY(),
+                            pendingOptions.getWidth(), pendingOptions.getHeight(),
+                            pendingOptions.getOnAnimationStartListener(),
+                            (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
+                    if (intent.getSourceBounds() == null) {
+                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                                pendingOptions.getStartY(),
+                                pendingOptions.getStartX() + pendingOptions.getWidth(),
+                                pendingOptions.getStartY() + pendingOptions.getHeight()));
+                    }
+                }
+                break;
+            case ANIM_OPEN_CROSS_PROFILE_APPS:
+                displayContent.mAppTransition
+                        .overridePendingAppTransitionStartCrossProfileApps();
+                break;
+            case ANIM_REMOTE_ANIMATION:
+                // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL.
+                displayContent.mAppTransition.overridePendingAppTransitionRemote(
+                        pendingOptions.getRemoteAnimationAdapter());
+                break;
+            case ANIM_NONE:
+                break;
+            default:
+                Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
+                break;
+        }
+    }
+
     ActivityOptions getOptionsForTargetActivityLocked() {
         return pendingOptions != null ? pendingOptions.forTargetActivity() : null;
     }
@@ -1532,8 +1713,11 @@
         if (!keysPaused) {
             keysPaused = true;
 
-            if (mWindowContainerController != null) {
-                mWindowContainerController.pauseKeyDispatching();
+            // TODO: remove the check after unification with AppWindowToken. The DC check is not
+            // needed after no mock mAppWindowToken in tests.
+            if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
+                mAppWindowToken.getDisplayContent().getInputMonitor().pauseDispatchingLw(
+                        mAppWindowToken);
             }
         }
     }
@@ -1542,8 +1726,11 @@
         if (keysPaused) {
             keysPaused = false;
 
-            if (mWindowContainerController != null) {
-                mWindowContainerController.resumeKeyDispatching();
+            // TODO: remove the check after unification with AppWindowToken. The DC check is not
+            // needed after no mock mAppWindowToken in tests.
+            if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
+                mAppWindowToken.getDisplayContent().getInputMonitor().resumeDispatchingLw(
+                        mAppWindowToken);
             }
         }
     }
@@ -1565,11 +1752,16 @@
     }
 
     void setVisibility(boolean visible) {
-        mWindowContainerController.setVisibility(visible, mDeferHidingClient);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
+                    + appToken);
+            return;
+        }
+        mAppWindowToken.setVisibility(visible, mDeferHidingClient);
         mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
     }
 
-    // TODO: Look into merging with #setVisibility()
+    // TODO: Look into merging with #commitVisibility()
     void setVisible(boolean newVisible) {
         visible = newVisible;
         mDeferHidingClient = !visible && mDeferHidingClient;
@@ -1599,7 +1791,12 @@
         // an indication that the Surface will eventually be destroyed.
         // This however isn't necessarily true if we are going to sleep.
         if (state == STOPPING && !isSleeping()) {
-            mWindowContainerController.notifyAppStopping();
+            if (mAppWindowToken == null) {
+                Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
+                        + appToken);
+                return;
+            }
+            mAppWindowToken.detachChildren();
         }
     }
 
@@ -1637,7 +1834,12 @@
     }
 
     void notifyAppResumed(boolean wasStopped) {
-        mWindowContainerController.notifyAppResumed(wasStopped);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: "
+                    + appToken);
+            return;
+        }
+        mAppWindowToken.notifyAppResumed(wasStopped);
     }
 
     void notifyUnknownVisibilityLaunched() {
@@ -1645,7 +1847,10 @@
         // No display activities never add a window, so there is no point in waiting them for
         // relayout.
         if (!noDisplay) {
-            mWindowContainerController.notifyUnknownVisibilityLaunched();
+            if (mAppWindowToken != null) {
+                mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController
+                        .notifyLaunched(mAppWindowToken);
+            }
         }
     }
 
@@ -1857,16 +2062,18 @@
             stopped = true;
             setState(STOPPED, "activityStoppedLocked");
 
-            mWindowContainerController.notifyAppStopped();
+            if (mAppWindowToken != null) {
+                mAppWindowToken.notifyAppStopped();
+            }
 
             if (finishing) {
                 clearOptionsLocked();
             } else {
                 if (deferRelaunchUntilPaused) {
                     stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config");
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 } else {
-                    mStackSupervisor.updatePreviousProcessLocked(this);
+                    mRootActivityContainer.updatePreviousProcess(this);
                 }
             }
         }
@@ -1918,14 +2125,33 @@
 
     public void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
         if (mayFreezeScreenLocked(app)) {
-            mWindowContainerController.startFreezingScreen(configChanges);
+            if (mAppWindowToken == null) {
+                Slog.w(TAG_WM,
+                        "Attempted to freeze screen with non-existing app token: " + appToken);
+                return;
+            }
+
+            if (configChanges == 0 && mAppWindowToken.okToDisplay()) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + appToken);
+                return;
+            }
+
+            mAppWindowToken.startFreezingScreen();
         }
     }
 
     public void stopFreezingScreenLocked(boolean force) {
         if (force || frozenBeforeDestroy) {
             frozenBeforeDestroy = false;
-            mWindowContainerController.stopFreezingScreen(force);
+            if (mAppWindowToken == null) {
+                return;
+            }
+            if (DEBUG_ORIENTATION) {
+                Slog.v(TAG_WM, "Clear freezing of " + appToken + ": hidden="
+                        + mAppWindowToken.isHidden() + " freezing="
+                        + mAppWindowToken.isFreezingScreen());
+            }
+            mAppWindowToken.stopFreezingScreen(true, force);
         }
     }
 
@@ -1937,7 +2163,10 @@
                     info.windowsFullyDrawnDelayMs);
         }
     }
-    @Override
+
+    /**
+     * Called when the starting window for this container is drawn.
+     */
     public void onStartingWindowDrawn(long timestamp) {
         synchronized (service.mGlobalLock) {
             mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
@@ -1945,7 +2174,7 @@
         }
     }
 
-    @Override
+    /** Called when the windows associated app window container are drawn. */
     public void onWindowsDrawn(boolean drawn, long timestamp) {
         synchronized (service.mGlobalLock) {
             mDrawn = drawn;
@@ -1965,7 +2194,7 @@
         }
     }
 
-    @Override
+    /** Called when the windows associated app window container are visible. */
     public void onWindowsVisible() {
         synchronized (service.mGlobalLock) {
             mStackSupervisor.reportActivityVisibleLocked(this);
@@ -1999,7 +2228,7 @@
         }
     }
 
-    @Override
+    /** Called when the windows associated app window container are no longer visible. */
     public void onWindowsGone() {
         synchronized (service.mGlobalLock) {
             if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
@@ -2007,7 +2236,14 @@
         }
     }
 
-    @Override
+    /**
+     * Called when the key dispatching to a window associated with the app window container
+     * timed-out.
+     *
+     * @param reason The reason for the key dispatching time out.
+     * @param windowPid The pid of the window key dispatching timed out on.
+     * @return True if input dispatching should be aborted.
+     */
     public boolean keyDispatchingTimedOut(String reason, int windowPid) {
         ActivityRecord anrActivity;
         WindowProcessController anrApp;
@@ -2036,7 +2272,7 @@
         // another activity to start or has stopped, then the key dispatching
         // timeout should not be caused by this.
         if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this) || stopped) {
-            final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
+            final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
             // Try to use the one which is closest to top.
             ActivityRecord r = stack.getResumedActivity();
             if (r == null) {
@@ -2183,7 +2419,7 @@
 
     void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
             boolean fromRecents) {
-        if (mWindowContainerController == null) {
+        if (mAppWindowToken == null) {
             return;
         }
         if (mTaskOverlay) {
@@ -2198,7 +2434,7 @@
 
         final CompatibilityInfo compatInfo =
                 service.compatibilityInfoForPackageLocked(info.applicationInfo);
-        final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
+        final boolean shown = addStartingWindow(packageName, theme,
                 compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                 prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
                 allowTaskSnapshot(),
@@ -2213,34 +2449,62 @@
         if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) {
             if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
             mStartingWindowState = STARTING_WINDOW_REMOVED;
-            mWindowContainerController.removeStartingWindow();
+            mAppWindowToken.removeStartingWindow();
         }
     }
 
     int getRequestedOrientation() {
-        return mWindowContainerController.getOrientation();
+        return getOrientation();
     }
 
     void setRequestedOrientation(int requestedOrientation) {
         final int displayId = getDisplayId();
         final Configuration displayConfig =
-                mStackSupervisor.getDisplayOverrideConfiguration(displayId);
+                mRootActivityContainer.getDisplayOverrideConfiguration(displayId);
 
-        final Configuration config = mWindowContainerController.setOrientation(requestedOrientation,
+        final Configuration config = setOrientation(requestedOrientation,
                 displayId, displayConfig, mayFreezeScreenLocked(app));
         if (config != null) {
             frozenBeforeDestroy = true;
             if (!service.updateDisplayOverrideConfigurationLocked(config, this,
                     false /* deferResume */, displayId)) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
             }
         }
         service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
                 task.taskId, requestedOrientation);
     }
 
+    Configuration setOrientation(int requestedOrientation, int displayId,
+            Configuration displayConfig, boolean freezeScreenIfNeeded) {
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM,
+                    "Attempted to set orientation of non-existing app token: " + appToken);
+            return null;
+        }
+
+        mAppWindowToken.setOrientation(requestedOrientation);
+
+        final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
+        return service.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder,
+                displayId);
+    }
+
+    int getOrientation() {
+        if (mAppWindowToken == null) {
+            return SCREEN_ORIENTATION_UNSPECIFIED;
+        }
+
+        return mAppWindowToken.getOrientationIgnoreVisibility();
+    }
+
     void setDisablePreviewScreenshots(boolean disable) {
-        mWindowContainerController.setDisablePreviewScreenshots(disable);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
+                    + " token: " + appToken);
+            return;
+        }
+        mAppWindowToken.setDisablePreviewScreenshots(disable);
     }
 
     /**
@@ -2288,8 +2552,8 @@
 
     /** Returns true if the configuration is compatible with this activity. */
     boolean isConfigurationCompatible(Configuration config) {
-        final int orientation = mWindowContainerController != null
-                ? mWindowContainerController.getOrientation() : info.screenOrientation;
+        final int orientation = mAppWindowToken != null
+                ? getOrientation() : info.screenOrientation;
         if (isFixedOrientationPortrait(orientation)
                 && config.orientation != ORIENTATION_PORTRAIT) {
             return false;
@@ -2867,7 +3131,7 @@
 
     void setShowWhenLocked(boolean showWhenLocked) {
         mShowWhenLocked = showWhenLocked;
-        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0 /* configChanges */,
+        mRootActivityContainer.ensureActivitiesVisible(null, 0 /* configChanges */,
                 false /* preserveWindows */);
     }
 
@@ -2905,7 +3169,7 @@
     }
 
     boolean isTopRunningActivity() {
-        return mStackSupervisor.topRunningActivityLocked() == this;
+        return mRootActivityContainer.topRunningActivity() == this;
     }
 
     /**
@@ -2918,7 +3182,12 @@
     }
 
     void registerRemoteAnimations(RemoteAnimationDefinition definition) {
-        mWindowContainerController.registerRemoteAnimations(definition);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
+                    + " token: " + appToken);
+            return;
+        }
+        mAppWindowToken.registerRemoteAnimations(definition);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index bd3e43c..9fbeaf8 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -63,7 +63,6 @@
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult;
 import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
@@ -103,6 +102,7 @@
 import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.RootActivityContainer.FindTaskResult;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -267,7 +267,7 @@
             mStackSupervisor.resizeDockedStackLocked(
                     getOverrideBounds(), mTmpRect2, mTmpRect2, null, null, PRESERVE_WINDOWS);
         }
-        mStackSupervisor.updateUIDsPresentOnDisplay();
+        mRootActivityContainer.updateUIDsPresentOnDisplay();
     }
 
     enum ActivityState {
@@ -390,6 +390,7 @@
 
     /** Run all ActivityStacks through this */
     protected final ActivityStackSupervisor mStackSupervisor;
+    protected final RootActivityContainer mRootActivityContainer;
 
     private boolean mTopActivityOccludesKeyguard;
     private ActivityRecord mTopDismissingKeyguardActivity;
@@ -489,6 +490,7 @@
             int windowingMode, int activityType, boolean onTop) {
         mStackSupervisor = supervisor;
         mService = supervisor.mService;
+        mRootActivityContainer = mService.mRootActivityContainer;
         mHandler = new ActivityStackHandler(supervisor.mLooper);
         mWindowManager = mService.mWindowManager;
         mStackId = stackId;
@@ -508,7 +510,7 @@
 
     T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
         return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
-                mStackSupervisor.mWindowManager);
+                mRootActivityContainer.mWindowManager);
     }
 
     T getWindowContainerController() {
@@ -532,7 +534,7 @@
             if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
                     + reason);
             setResumedActivity(record, reason + " - onActivityStateChanged");
-            if (record == mStackSupervisor.getTopResumedActivity()) {
+            if (record == mRootActivityContainer.getTopResumedActivity()) {
                 // TODO(b/111361570): Support multiple focused apps in WM
                 mService.setResumedActivityUncheckLocked(record, reason);
             }
@@ -622,7 +624,7 @@
             display.onStackWindowingModeChanged(this);
         }
         if (hasNewOverrideBounds) {
-            mStackSupervisor.resizeStackLocked(this, mTmpRect2, null, null, PRESERVE_WINDOWS,
+            mRootActivityContainer.resizeStack(this, mTmpRect2, null, null, PRESERVE_WINDOWS,
                     true /* allowResizeInDockedMode */, true /* deferResume */);
         }
         if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
@@ -819,8 +821,8 @@
         }
 
         if (!deferEnsuringVisibility) {
-            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
         }
     }
 
@@ -854,10 +856,10 @@
     /** Resume next focusable stack after reparenting to another display. */
     void postReparent() {
         adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */);
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
         // Update visibility of activities before notifying WM. This way it won't try to resize
         // windows that are no longer visible.
-        mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+        mRootActivityContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
                 !PRESERVE_WINDOWS);
     }
 
@@ -882,7 +884,7 @@
     }
 
     ActivityDisplay getDisplay() {
-        return mStackSupervisor.getActivityDisplay(mDisplayId);
+        return mRootActivityContainer.getActivityDisplay(mDisplayId);
     }
 
     /**
@@ -1034,7 +1036,7 @@
     }
 
     /**
-     * This is a simplified version of topRunningActivityLocked that provides a number of
+     * This is a simplified version of topRunningActivity that provides a number of
      * optional skip-over modes.  It is intended for use with the ActivityController hook only.
      *
      * @param token If non-null, any history records matching this token will be skipped.
@@ -1236,7 +1238,7 @@
 
     boolean isFocusable() {
         final ActivityRecord r = topRunningActivityLocked();
-        return mStackSupervisor.isFocusable(this, r != null && r.isFocusable());
+        return mRootActivityContainer.isFocusable(this, r != null && r.isFocusable());
     }
 
     boolean isFocusableAndVisible() {
@@ -1398,7 +1400,7 @@
             final TaskRecord task = mTaskHistory.get(i);
 
             if (task.okToShowLocked()) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUser: stack=" + getStackId() +
                         " moving " + task + " to top");
                 mTaskHistory.remove(i);
                 mTaskHistory.add(task);
@@ -1587,7 +1589,7 @@
         if (prev == null) {
             if (resuming == null) {
                 Slog.wtf(TAG, "Trying to pause when nothing is resumed");
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
             }
             return false;
         }
@@ -1665,7 +1667,7 @@
             // pause, so just treat it as being paused now.
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
             if (resuming == null) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
             }
             return false;
         }
@@ -1704,7 +1706,7 @@
                 }
             }
         }
-        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
     }
 
     private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
@@ -1757,9 +1759,9 @@
         }
 
         if (resumeNext) {
-            final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack();
+            final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();
             if (!topStack.shouldSleepOrShutDownActivities()) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(topStack, prev, null);
+                mRootActivityContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
             } else {
                 checkReadyForSleep();
                 ActivityRecord top = topStack.topRunningActivityLocked();
@@ -1768,7 +1770,7 @@
                     // something. Also if the top activity on the stack is not the just paused
                     // activity, we need to go ahead and resume it to ensure we complete an
                     // in-flight app switch.
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
             }
         }
@@ -1799,7 +1801,7 @@
             mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
         }
 
-        mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
+        mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
     }
 
     private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
@@ -2011,7 +2013,7 @@
     /**
      * Ensure visibility with an option to also update the configuration of visible activities.
      * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
-     * @see ActivityStackSupervisor#ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
+     * @see RootActivityContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
      */
     // TODO: Should be re-worked based on the fact that each task as a stack in most cases.
     final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
@@ -2032,7 +2034,7 @@
             boolean aboveTop = top != null;
             final boolean stackShouldBeVisible = shouldBeVisible(starting);
             boolean behindFullscreenActivity = !stackShouldBeVisible;
-            boolean resumeNextActivity = mStackSupervisor.isTopDisplayFocusedStack(this)
+            boolean resumeNextActivity = mRootActivityContainer.isTopDisplayFocusedStack(this)
                     && (isInStackLocked(starting) == null);
             final boolean isTopNotPinnedStack =
                     isAttached() && getDisplay().isTopNotPinnedStack(this);
@@ -2443,7 +2445,7 @@
      *
      * NOTE: It is not safe to call this method directly as it can cause an activity in a
      *       non-focused stack to be resumed.
-     *       Use {@link ActivityStackSupervisor#resumeFocusedStacksTopActivitiesLocked} to resume the
+     *       Use {@link RootActivityContainer#resumeFocusedStacksTopActivities} to resume the
      *       right activity for the current system state.
      */
     @GuardedBy("mService")
@@ -2513,7 +2515,7 @@
             return false;
         }
 
-        mStackSupervisor.cancelInitializingActivities();
+        mRootActivityContainer.cancelInitializingActivities();
 
         // Remember how we'll process this pause/resume situation, and ensure
         // that the state is reset however we wind up proceeding.
@@ -2536,7 +2538,6 @@
             executeAppTransition(options);
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "resumeTopActivityLocked: Top activity resumed " + next);
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return false;
         }
 
@@ -2544,7 +2545,7 @@
         // activity is paused, well that is the state we want.
         if (shouldSleepOrShutDownActivities()
                 && mLastPausedActivity == next
-                && mStackSupervisor.allPausedActivitiesComplete()) {
+                && mRootActivityContainer.allPausedActivitiesComplete()) {
             // If the current top activity may be able to occlude keyguard but the occluded state
             // has not been set, update visibility and check again if we should continue to resume.
             boolean nothingToResume = true;
@@ -2565,7 +2566,6 @@
                 executeAppTransition(options);
                 if (DEBUG_STATES) Slog.d(TAG_STATES,
                         "resumeTopActivityLocked: Going to sleep and all paused");
-                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
                 return false;
             }
         }
@@ -2576,7 +2576,6 @@
         if (!mService.mAmInternal.hasStartedUserState(next.userId)) {
             Slog.w(TAG, "Skipping resume of top activity " + next
                     + ": user " + next.userId + " is stopped");
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return false;
         }
 
@@ -2590,10 +2589,9 @@
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
 
         // If we are currently pausing an activity, then don't do anything until that is done.
-        if (!mStackSupervisor.allPausedActivitiesComplete()) {
+        if (!mRootActivityContainer.allPausedActivitiesComplete()) {
             if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
                     "resumeTopActivityLocked: Skip resume: some activity pausing.");
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return false;
         }
 
@@ -2640,7 +2638,6 @@
                 next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
                         true /* updateLru */, true /* activityChange */, false /* updateOomAdj */);
             }
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             if (lastResumed != null) {
                 lastResumed.setWillCloseOrEnterPip(true);
             }
@@ -2655,7 +2652,6 @@
             executeAppTransition(options);
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return true;
         }
 
@@ -2673,7 +2669,7 @@
 
         if (prev != null && prev != next) {
             if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev)
-                    && next != null && !next.nowVisible) {
+                    && !next.nowVisible) {
                 mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev);
                 if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
                         "Resuming top, waiting visible to hide: " + prev);
@@ -2814,7 +2810,7 @@
                 // result of invisible window resize.
                 // TODO: Remove this once visibilities are set correctly immediately when
                 // starting an activity.
-                notUpdated = !mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
+                notUpdated = !mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId,
                         true /* markFrozenIfConfigChanged */, false /* deferResume */);
             }
 
@@ -2836,7 +2832,6 @@
                     next.setVisibility(true);
                 }
                 next.completeResumeLocked();
-                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
                 return true;
             }
 
@@ -2899,7 +2894,6 @@
                             false /* taskSwitch */);
                 }
                 mStackSupervisor.startSpecificActivityLocked(next, true, false);
-                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
                 return true;
             }
 
@@ -2913,7 +2907,6 @@
                 Slog.w(TAG, "Exception thrown during resume of " + next, e);
                 requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
                         "resume-exception", true);
-                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
                 return true;
             }
         } else {
@@ -2931,7 +2924,6 @@
             mStackSupervisor.startSpecificActivityLocked(next, true, true);
         }
 
-        if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
         return true;
     }
 
@@ -2942,7 +2934,7 @@
             // Try to move focus to the next visible stack with a running activity if this
             // stack is not covering the entire screen or is on a secondary display (with no home
             // stack).
-            return mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(nextFocusedStack, prev,
+            return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack, prev,
                     null /* targetOptions */);
         }
 
@@ -2950,8 +2942,7 @@
         ActivityOptions.abort(options);
         if (DEBUG_STATES) Slog.d(TAG_STATES,
                 "resumeTopActivityInNextFocusableStack: " + reason + ", go home");
-        if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-        return mStackSupervisor.resumeHomeActivity(prev, reason, mDisplayId);
+        return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
     }
 
     /** Returns the position the input task should be placed in this stack. */
@@ -3043,7 +3034,7 @@
                     if (!startIt) {
                         if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
                                 + task, new RuntimeException("here").fillInStackTrace());
-                        r.createWindowContainer();
+                        r.createAppWindowToken();
                         ActivityOptions.abort(options);
                         return;
                     }
@@ -3073,9 +3064,10 @@
         // TODO: Need to investigate if it is okay for the controller to already be created by the
         // time we get to this point. I think it is, but need to double check.
         // Use test in b/34179495 to trace the call path.
-        if (r.getWindowContainerController() == null) {
-            r.createWindowContainer();
+        if (r.mAppWindowToken == null) {
+            r.createAppWindowToken();
         }
+
         task.setFrontOfTask();
 
         if (!isHomeOrRecentsStack() || numActivities() > 0) {
@@ -3536,7 +3528,7 @@
     }
 
     private void adjustFocusedActivityStack(ActivityRecord r, String reason) {
-        if (!mStackSupervisor.isTopDisplayFocusedStack(this) ||
+        if (!mRootActivityContainer.isTopDisplayFocusedStack(this) ||
                 ((mResumedActivity != r) && (mResumedActivity != null))) {
             return;
         }
@@ -3545,7 +3537,7 @@
         final String myReason = reason + " adjustFocus";
 
         if (next == r) {
-            final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
+            final ActivityRecord top = mRootActivityContainer.topRunningActivity();
             if (top != null) {
                 top.moveFocusableActivityToTop(myReason);
             }
@@ -3569,7 +3561,7 @@
         final ActivityStack nextFocusableStack = adjustFocusToNextFocusableStack(myReason);
         if (nextFocusableStack != null) {
             final ActivityRecord top = nextFocusableStack.topRunningActivityLocked();
-            if (top != null && top == mStackSupervisor.getTopResumedActivity()) {
+            if (top != null && top == mRootActivityContainer.getTopResumedActivity()) {
                 // TODO(b/111361570): Remove this and update focused app per-display in
                 // WindowManager every time an activity becomes resumed in
                 // ActivityTaskManagerService#setResumedActivityUncheckLocked().
@@ -3597,7 +3589,7 @@
      */
     private ActivityStack adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) {
         final ActivityStack stack =
-                mStackSupervisor.getNextFocusableStackLocked(this, !allowFocusSelf);
+                mRootActivityContainer.getNextFocusableStack(this, !allowFocusSelf);
         final String myReason = reason + " adjustFocusToNextFocusableStack";
         if (stack == null) {
             return null;
@@ -4018,11 +4010,11 @@
                 // stack, need to make something visible in its place. Also if the display does not
                 // have running activity, the configuration may need to be updated for restoring
                 // original orientation of the display.
-                mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
+                mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId,
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
             if (activityRemoved) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
             }
             if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
                     "destroyActivityLocked: finishCurrentActivityLocked r=" + r +
@@ -4035,7 +4027,7 @@
         if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
         mStackSupervisor.mFinishingActivities.add(r);
         r.resumeKeyDispatchingLocked();
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
         return r;
     }
 
@@ -4377,7 +4369,7 @@
             }
         }
         if (activityRemoved) {
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
         }
     }
 
@@ -4568,7 +4560,7 @@
             }
         }
 
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
     }
 
     private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
@@ -4712,7 +4704,7 @@
                 task.mLastTimeMoved *= -1;
             }
         }
-        mStackSupervisor.invalidateTaskLayers();
+        mRootActivityContainer.invalidateTaskLayers();
     }
 
     final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
@@ -4788,7 +4780,7 @@
                 topActivity.supportsEnterPipOnTaskSwitch = true;
             }
 
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
             EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
 
             mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId);
@@ -4860,7 +4852,7 @@
             return true;
         }
 
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
         return true;
     }
 
@@ -4907,7 +4899,7 @@
         if (updatedConfig) {
             // Ensure the resumed state of the focus activity if we updated the configuration of
             // any activity.
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
         }
     }
 
@@ -5099,7 +5091,7 @@
      */
     void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
             @WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) {
-        boolean focusedStack = mStackSupervisor.getTopDisplayFocusedStack() == this;
+        boolean focusedStack = mRootActivityContainer.getTopDisplayFocusedStack() == this;
         boolean topTask = true;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -5164,7 +5156,7 @@
         return removeHistoryRecordsForAppLocked(app);
     }
 
-    void handleAppCrashLocked(WindowProcessController app) {
+    void handleAppCrash(WindowProcessController app) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -5311,7 +5303,7 @@
             // We only need to adjust focused stack if this stack is in focus and we are not in the
             // process of moving the task to the top of the stack that will be focused.
             if (mode != REMOVE_TASK_MODE_MOVING_TO_TOP
-                    && mStackSupervisor.isTopDisplayFocusedStack(this)) {
+                    && mRootActivityContainer.isTopDisplayFocusedStack(this)) {
                 String myReason = reason + " leftTaskHistoryEmpty";
                 if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) {
                     getDisplay().moveHomeStackToFront(myReason);
@@ -5417,7 +5409,7 @@
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
     }
 
     private ActivityStack preAddTask(TaskRecord task, String reason, boolean toTop) {
@@ -5484,7 +5476,7 @@
         moveToFront(reason);
         // If the original state is resumed, there is no state change to update focused app.
         // So here makes sure the activity focus is set if it is the top.
-        if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) {
+        if (origState == RESUMED && r == mRootActivityContainer.getTopResumedActivity()) {
             // TODO(b/111361570): Support multiple focused apps in WM
             mService.setResumedActivityUncheckLocked(r, reason);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 694e9d1..3162ee3 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -25,28 +25,18 @@
 import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
 import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
 import static android.app.WaitResult.INVALID_DELAY;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.windowingModeToString;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -59,26 +49,13 @@
 import static android.view.Display.TYPE_VIRTUAL;
 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 
-import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
-import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
-import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
-import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
-import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
-import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -86,9 +63,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IDLE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -96,6 +71,10 @@
 import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
 import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.android.server.wm.RootActivityContainer.TAG_STATES;
 import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
@@ -103,20 +82,11 @@
 import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
 import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
 
-import static java.lang.Integer.MAX_VALUE;
-
 import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackInfo;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
-import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
@@ -139,17 +109,11 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.power.V1_0.PowerHint;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
-import android.os.FactoryTest;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -163,20 +127,13 @@
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.provider.MediaStore;
-import android.service.voice.IVoiceInteractionSession;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.DisplayMetrics;
 import android.util.EventLog;
-import android.util.IntArray;
 import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
-import android.view.Display;
-import android.view.DisplayInfo;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -185,34 +142,28 @@
 import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.LocalServices;
 import com.android.server.am.ActivityManagerService;
-import com.android.server.am.AppTimeTracker;
 import com.android.server.am.EventLogTags;
 import com.android.server.am.UserState;
-import com.android.server.wm.ActivityStack.ActivityState;
-import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
 
-public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
-        RecentTasks.Callbacks, RootWindowContainerListener {
+// TODO: This class has become a dumping ground. Let's
+// - Move things relating to the hierarchy to RootWindowContainer
+// - Move things relating to activity life cycles to maybe a new class called ActivityLifeCycler
+// - Move interface things to ActivityTaskManagerService.
+// - All other little things to other files.
+public class ActivityStackSupervisor implements RecentTasks.Callbacks {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_ATM;
     private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
     private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
-    private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
     private static final String TAG_STACK = TAG + POSTFIX_STACK;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
-    static final String TAG_STATES = TAG + POSTFIX_STATES;
     static final String TAG_TASKS = TAG + POSTFIX_TASKS;
 
     /** How long we wait until giving up on the last activity telling us it is idle. */
@@ -233,12 +184,6 @@
     static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
     static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
 
-    private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
-
-    // Used to indicate if an object (e.g. stack) that we are trying to get
-    // should be created if it doesn't exist already.
-    static final boolean CREATE_IF_NEEDED = true;
-
     // Used to indicate that windows of activities should be preserved during the resize.
     static final boolean PRESERVE_WINDOWS = true;
 
@@ -270,25 +215,6 @@
     private Rect mPendingTempOtherTaskBounds;
     private Rect mPendingTempOtherTaskInsetBounds;
 
-    /**
-     * The modes which affect which tasks are returned when calling
-     * {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}.
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            MATCH_TASK_IN_STACKS_ONLY,
-            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
-            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
-    })
-    public @interface AnyTaskForIdMatchTaskMode {}
-    // Match only tasks in the current stacks
-    static final int MATCH_TASK_IN_STACKS_ONLY = 0;
-    // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
-    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
-    // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
-    // provided stack id
-    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
-
     // Activity actions an app cannot start if it uses a permission which is not granted.
     private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
             new ArrayMap<>();
@@ -315,20 +241,20 @@
     /** The number of distinct task ids that can be assigned to the tasks of a single user */
     private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
 
-    ActivityTaskManagerService mService;
+    final ActivityTaskManagerService mService;
+    RootActivityContainer mRootActivityContainer;
 
     /** The historial list of recent tasks including inactive tasks */
     RecentTasks mRecentTasks;
 
     /** Helper class to abstract out logic for fetching the set of currently running tasks */
-    private RunningTasks mRunningTasks;
+    RunningTasks mRunningTasks;
 
     final ActivityStackSupervisorHandler mHandler;
     final Looper mLooper;
 
     /** Short cut */
     WindowManagerService mWindowManager;
-    DisplayManager mDisplayManager;
 
      /** Common synchronization logic used to save things to disks. */
     PersisterQueue mPersisterQueue;
@@ -341,9 +267,6 @@
      */
     private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20);
 
-    /** The current user */
-    int mCurrentUser;
-
     /** List of activities that are waiting for a new activity to become visible before completing
      * whatever operation they are supposed to do. */
     // TODO: Remove mActivitiesWaitingForVisibleActivity list and just remove activity from
@@ -392,9 +315,6 @@
      * is being brought in front of us. */
     boolean mUserLeaving = false;
 
-    /** Set when a power hint has started, but not ended. */
-    private boolean mPowerHintSent;
-
     /**
      * We don't want to allow the device to go to sleep while in the process
      * of launching an activity.  This is primarily to allow alarm intent
@@ -410,29 +330,6 @@
      */
     PowerManager.WakeLock mGoingToSleep;
 
-    /**
-     * A list of tokens that cause the top activity to be put to sleep.
-     * They are used by components that may hide and block interaction with underlying
-     * activities.
-     */
-    final ArrayList<SleepToken> mSleepTokens = new ArrayList<>();
-
-    /** Stack id of the front stack when user switched, indexed by userId. */
-    SparseIntArray mUserStackInFront = new SparseIntArray(2);
-
-    /** Reference to default display so we can quickly look it up. */
-    private ActivityDisplay mDefaultDisplay;
-
-    /**
-     * List of displays which contain activities, sorted by z-order.
-     * The last entry in the list is the topmost.
-     */
-    private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>();
-
-    private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
-
-    private DisplayManagerInternal mDisplayManagerInternal;
-
     /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
     boolean inResumeTopActivity;
 
@@ -443,50 +340,8 @@
     private final Rect tempRect = new Rect();
     private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
 
-    // The default minimal size that will be used if the activity doesn't specify its minimal size.
-    // It will be calculated when the default display gets added.
-    int mDefaultMinSizeOfResizeableTaskDp = -1;
-
-    // Whether tasks have moved and we need to rank the tasks before next OOM scoring
-    private boolean mTaskLayersChanged = true;
-
     private ActivityMetricsLogger mActivityMetricsLogger;
 
-    private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
-
-    @Override
-    protected int getChildCount() {
-        return mActivityDisplays.size();
-    }
-
-    @Override
-    protected ActivityDisplay getChildAt(int index) {
-        return mActivityDisplays.get(index);
-    }
-
-    @Override
-    protected ConfigurationContainer getParent() {
-        return null;
-    }
-
-    Configuration getDisplayOverrideConfiguration(int displayId) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException("No display found with id: " + displayId);
-        }
-
-        return activityDisplay.getOverrideConfiguration();
-    }
-
-    void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException("No display found with id: " + displayId);
-        }
-
-        activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
-    }
-
     /** Check if placing task or activity on specified display is allowed. */
     boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
             ActivityInfo activityInfo) {
@@ -508,44 +363,6 @@
     }
 
     /**
-     * Check if configuration of specified display matches current global config.
-     * Used to check if we can put a non-resizeable activity on a secondary display and it will get
-     * the same config as on the default display.
-     * @param displayId Id of the display to check.
-     * @return {@code true} if configuration matches.
-     */
-    private boolean displayConfigMatchesGlobal(int displayId) {
-        if (displayId == DEFAULT_DISPLAY) {
-            return true;
-        }
-        if (displayId == INVALID_DISPLAY) {
-            return false;
-        }
-        final ActivityDisplay targetDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (targetDisplay == null) {
-            throw new IllegalArgumentException("No display found with id: " + displayId);
-        }
-        return getConfiguration().equals(targetDisplay.getConfiguration());
-    }
-
-    static class FindTaskResult {
-        ActivityRecord mRecord;
-        boolean mIdealMatch;
-
-        void clear() {
-            mRecord = null;
-            mIdealMatch = false;
-        }
-
-        void setTo(FindTaskResult result) {
-            mRecord = result.mRecord;
-            mIdealMatch = result.mIdealMatch;
-        }
-    }
-
-    private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
-
-    /**
      * Used to keep track whether app visibilities got changed since the last pause. Useful to
      * determine whether to invoke the task stack change listener after pausing.
      */
@@ -565,11 +382,6 @@
      */
     private boolean mAllowDockedStackResize = true;
 
-    /**
-     * Is dock currently minimized.
-     */
-    boolean mIsDockMinimized;
-
     private KeyguardController mKeyguardController;
 
     private PowerManager mPowerManager;
@@ -577,8 +389,6 @@
 
     private boolean mInitialized;
 
-    private RootWindowContainerController mWindowContainerController;
-
     /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
@@ -617,16 +427,6 @@
         mHandler = new ActivityStackSupervisorHandler(looper);
     }
 
-    @VisibleForTesting
-    void setService(ActivityTaskManagerService service) {
-        mService = service;
-    }
-
-    @VisibleForTesting
-    void setWindowContainerController(RootWindowContainerController controller) {
-        mWindowContainerController = controller;
-    }
-
     public void initialize() {
         if (mInitialized) {
             return;
@@ -681,321 +481,16 @@
     void setWindowManager(WindowManagerService wm) {
         mWindowManager = wm;
         getKeyguardController().setWindowManager(wm);
-        setWindowContainerController(new RootWindowContainerController(this));
-
-        mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
-        mDisplayManager.registerDisplayListener(this, mHandler);
-        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
-
-        final Display[] displays = mDisplayManager.getDisplays();
-        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
-            final Display display = displays[displayNdx];
-            final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
-            if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
-                mDefaultDisplay = activityDisplay;
-            }
-            addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
-        }
-        calculateDefaultMinimalSizeOfResizeableTasks();
-
-        final ActivityDisplay defaultDisplay = getDefaultDisplay();
-
-        defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-        positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
-    }
-
-    /** Change the z-order of the given display. */
-    private void positionChildAt(ActivityDisplay display, int position) {
-        if (position >= mActivityDisplays.size()) {
-            position = mActivityDisplays.size() - 1;
-        } else if (position < 0) {
-            position = 0;
-        }
-
-        if (mActivityDisplays.isEmpty()) {
-            mActivityDisplays.add(display);
-        } else if (mActivityDisplays.get(position) != display) {
-            mActivityDisplays.remove(display);
-            mActivityDisplays.add(position, display);
-        }
-    }
-
-    @Override
-    public void onChildPositionChanged(DisplayWindowController childController, 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);
-        }
-    }
-
-    ActivityStack getTopDisplayFocusedStack() {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack();
-            if (focusedStack != null) {
-                return focusedStack;
-            }
-        }
-        return null;
-    }
-
-    ActivityRecord getTopResumedActivity() {
-        final ActivityStack focusedStack = getTopDisplayFocusedStack();
-        if (focusedStack == null) {
-            return null;
-        }
-        final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
-        if (resumedActivity != null && resumedActivity.app != null) {
-            return resumedActivity;
-        }
-        // The top focused stack might not have a resumed activity yet - look on all displays in
-        // focus order.
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
-            if (resumedActivityOnDisplay != null) {
-                return resumedActivityOnDisplay;
-            }
-        }
-        return null;
-    }
-
-    boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
-        if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
-            return false;
-        }
-
-        return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
-    }
-
-    boolean isTopDisplayFocusedStack(ActivityStack stack) {
-        return stack != null && stack == getTopDisplayFocusedStack();
     }
 
     void moveRecentsStackToFront(String reason) {
-        final ActivityStack recentsStack = getDefaultDisplay().getStack(
+        final ActivityStack recentsStack = mRootActivityContainer.getDefaultDisplay().getStack(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
         if (recentsStack != null) {
             recentsStack.moveToFront(reason);
         }
     }
 
-    boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
-        if (!mService.isBooting() && !mService.isBooted()) {
-            // Not ready yet!
-            return false;
-        }
-
-        if (displayId == INVALID_DISPLAY) {
-            displayId = DEFAULT_DISPLAY;
-        }
-
-        final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
-        final String myReason = reason + " resumeHomeActivity";
-
-        // Only resume home activity if isn't finishing.
-        if (r != null && !r.finishing) {
-            r.moveFocusableActivityToTop(myReason);
-            return resumeFocusedStacksTopActivitiesLocked(r.getStack(), prev, null);
-        }
-        return startHomeOnDisplay(mCurrentUser, myReason, displayId);
-    }
-
-    /**
-     * Check if home activity start should be allowed on a display.
-     * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched.
-     * @param displayId The id of the target display.
-     * @param allowInstrumenting Whether launching home should be allowed if being instrumented.
-     * @return {@code true} if allow to launch, {@code false} otherwise.
-     */
-    boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId,
-            boolean allowInstrumenting) {
-        if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
-                && mService.mTopAction == null) {
-            // We are running in factory test mode, but unable to find the factory test app, so
-            // just sit around displaying the error message and don't try to start anything.
-            return false;
-        }
-
-        final WindowProcessController app =
-                mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid);
-        if (!allowInstrumenting && app != null && app.isInstrumenting()) {
-            // Don't do this if the home app is currently being instrumented.
-            return false;
-        }
-
-        if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
-                && displayId == mService.mVr2dDisplayId)) {
-            // No restrictions to default display or vr 2d display.
-            return true;
-        }
-
-        final ActivityDisplay display = getActivityDisplay(displayId);
-        if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
-            // Can't launch home on display that doesn't support system decorations.
-            return false;
-        }
-
-        final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
-                && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
-                && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
-        if (!supportMultipleInstance) {
-            // Can't launch home on other displays if it requested to be single instance. Also we
-            // don't allow home applications that target before Q to have multiple home activity
-            // instances because they may not be expected to have multiple home scenario and
-            // haven't explicitly request for single instance.
-            return false;
-        }
-
-        return true;
-    }
-
-    TaskRecord anyTaskForIdLocked(int id) {
-        return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
-    }
-
-    TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode) {
-        return anyTaskForIdLocked(id, matchMode, null, !ON_TOP);
-    }
-
-    /**
-     * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
-     * @param id Id of the task we would like returned.
-     * @param matchMode The mode to match the given task id in.
-     * @param aOptions The activity options to use for restoration. Can be null.
-     * @param onTop If the stack for the task should be the topmost on the display.
-     */
-    TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode,
-            @Nullable ActivityOptions aOptions, boolean onTop) {
-        // If options are set, ensure that we are attempting to actually restore a task
-        if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
-            throw new IllegalArgumentException("Should not specify activity options for non-restore"
-                    + " lookup");
-        }
-
-        int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final TaskRecord task = stack.taskForIdLocked(id);
-                if (task == null) {
-                    continue;
-                }
-                if (aOptions != null) {
-                    // Resolve the stack the task should be placed in now based on options
-                    // and reparent if needed.
-                    final ActivityStack launchStack = getLaunchStack(null, aOptions, task, onTop);
-                    if (launchStack != null && stack != launchStack) {
-                        final int reparentMode = onTop
-                                ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
-                        task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
-                                "anyTaskForIdLocked");
-                    }
-                }
-                return task;
-            }
-        }
-
-        // If we are matching stack tasks only, return now
-        if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
-            return null;
-        }
-
-        // Otherwise, check the recent tasks and return if we find it there and we are not restoring
-        // the task from recents
-        if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
-        final TaskRecord task = mRecentTasks.getTask(id);
-
-        if (task == null) {
-            if (DEBUG_RECENTS) {
-                Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
-            }
-
-            return null;
-        }
-
-        if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
-            return task;
-        }
-
-        // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
-        if (!restoreRecentTaskLocked(task, aOptions, onTop)) {
-            if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
-                    "Couldn't restore task id=" + id + " found in recents");
-            return null;
-        }
-        if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
-        return task;
-    }
-
-    ActivityRecord isInAnyStackLocked(IBinder token) {
-        int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.isInStackLocked(token);
-                if (r != null) {
-                    return r;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Detects whether we should show a lock screen in front of this task for a locked user.
-     * <p>
-     * We'll do this if either of the following holds:
-     * <ul>
-     *   <li>The top activity explicitly belongs to {@param userId}.</li>
-     *   <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
-     * </ul>
-     *
-     * @return {@code true} if the top activity looks like it belongs to {@param userId}.
-     */
-    private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
-        // To handle the case that work app is in the task but just is not the top one.
-        final ActivityRecord activityRecord = task.getTopActivity();
-        final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
-
-        return (activityRecord != null && activityRecord.userId == userId)
-                || (resultTo != null && resultTo.userId == userId);
-    }
-
-    /**
-     * Find all visible task stacks containing {@param userId} and intercept them with an activity
-     * to block out the contents and possibly start a credential-confirming intent.
-     *
-     * @param userId user handle for the locked managed profile.
-     */
-    void lockAllProfileTasks(@UserIdInt int userId) {
-        mWindowManager.deferSurfaceLayout();
-        try {
-            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
-                    final List<TaskRecord> tasks = stack.getAllTasks();
-                    for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
-                        final TaskRecord task = tasks.get(taskNdx);
-
-                        // Check the task for a top activity belonging to userId, or returning a
-                        // result to an activity belonging to userId. Example case: a document
-                        // picker for personal files, opened by a work app, should still get locked.
-                        if (taskTopActivityIsUser(task, userId)) {
-                            mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
-                                    task.taskId, userId);
-                        }
-                    }
-                }
-            }
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-        }
-    }
-
     void setNextTaskIdForUserLocked(int taskId, int userId) {
         final int currentTaskId = mCurTaskIdForUser.get(userId, -1);
         if (taskId > currentTaskId) {
@@ -1019,7 +514,7 @@
         // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
         int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
         while (mRecentTasks.containsTaskId(candidateTaskId, userId)
-                || anyTaskForIdLocked(
+                || mRootActivityContainer.anyTaskForId(
                         candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
             candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
             if (candidateTaskId == currentTaskId) {
@@ -1034,145 +529,6 @@
         return candidateTaskId;
     }
 
-    boolean attachApplicationLocked(WindowProcessController app) throws RemoteException {
-        final String processName = app.mName;
-        boolean didSomething = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (!isTopDisplayFocusedStack(stack)) {
-                    continue;
-                }
-                stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
-                final ActivityRecord top = stack.topRunningActivityLocked();
-                final int size = mTmpActivityList.size();
-                for (int i = 0; i < size; i++) {
-                    final ActivityRecord activity = mTmpActivityList.get(i);
-                    if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
-                            && processName.equals(activity.processName)) {
-                        try {
-                            if (realStartActivityLocked(activity, app,
-                                    top == activity /* andResume */, true /* checkConfig */)) {
-                                didSomething = true;
-                            }
-                        } catch (RemoteException e) {
-                            Slog.w(TAG, "Exception in new application when starting activity "
-                                    + top.intent.getComponent().flattenToShortString(), e);
-                            throw e;
-                        }
-                    }
-                }
-            }
-        }
-        if (!didSomething) {
-            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        }
-        return didSomething;
-    }
-
-    boolean allResumedActivitiesIdle() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            // TODO(b/117135575): Check resumed activities on all visible stacks.
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            if (display.isSleeping()) {
-                // No resumed activities while display is sleeping.
-                continue;
-            }
-
-            // If the focused stack is not null or not empty, there should have some activities
-            // resuming or resumed. Make sure these activities are idle.
-            final ActivityStack stack = display.getFocusedStack();
-            if (stack == null || stack.numActivities() == 0) {
-                continue;
-            }
-            final ActivityRecord resumedActivity = stack.getResumedActivity();
-            if (resumedActivity == null || !resumedActivity.idle) {
-                if (DEBUG_STATES) {
-                    Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
-                            + stack.mStackId + " " + resumedActivity + " not idle");
-                }
-                return false;
-            }
-        }
-        // Send launch end powerhint when idle
-        sendPowerHintForLaunchEndIfNeeded();
-        return true;
-    }
-
-    private boolean allResumedActivitiesVisible() {
-        boolean foundResumed = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.getResumedActivity();
-                if (r != null) {
-                    if (!r.nowVisible || mActivitiesWaitingForVisibleActivity.contains(r)) {
-                        return false;
-                    }
-                    foundResumed = true;
-                }
-            }
-        }
-        return foundResumed;
-    }
-
-    private void executeAppTransitionForAllDisplay() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            display.getWindowContainerController().executeAppTransition();
-        }
-    }
-
-    /**
-     * Pause all activities in either all of the stacks or just the back stacks.
-     * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
-     * @param resuming The resuming activity.
-     * @param dontWait The resuming activity isn't going to wait for all activities to be paused
-     *                 before resuming.
-     * @return true if any activity was paused as a result of this call.
-     */
-    boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
-        boolean someActivityPaused = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            someActivityPaused |= mActivityDisplays.get(displayNdx)
-                    .pauseBackStacks(userLeaving, resuming, dontWait);
-        }
-        return someActivityPaused;
-    }
-
-    boolean allPausedActivitiesComplete() {
-        boolean pausing = true;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.mPausingActivity;
-                if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
-                    if (DEBUG_STATES) {
-                        Slog.d(TAG_STATES,
-                                "allPausedActivitiesComplete: r=" + r + " state=" + r.getState());
-                        pausing = false;
-                    } else {
-                        return false;
-                    }
-                }
-            }
-        }
-        return pausing;
-    }
-
-    void cancelInitializingActivities() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.cancelInitializingActivities();
-            }
-        }
-    }
-
     void waitActivityVisible(ComponentName name, WaitResult result, long startTimeMs) {
         final WaitInfo waitInfo = new WaitInfo(name, result, startTimeMs);
         mWaitingForActivityVisible.add(waitInfo);
@@ -1263,24 +619,6 @@
         }
     }
 
-    ActivityRecord topRunningActivityLocked() {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
-            if (topActivity != null) {
-                return topActivity;
-            }
-        }
-        return null;
-    }
-
-    @VisibleForTesting
-    void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
-            @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
-            int callingUid, boolean allowed) {
-        mRunningTasks.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode,
-                mActivityDisplays, callingUid, allowed);
-    }
-
     ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
             ProfilerInfo profilerInfo) {
         final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
@@ -1360,10 +698,10 @@
         return resolveActivity(intent, rInfo, startFlags, profilerInfo);
     }
 
-    private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
+    boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
             boolean andResume, boolean checkConfig) throws RemoteException {
 
-        if (!allPausedActivitiesComplete()) {
+        if (!mRootActivityContainer.allPausedActivitiesComplete()) {
             // While there are activities pausing we skipping starting any new activities until
             // pauses are complete. NOTE: that we also do this for activities that are starting in
             // the paused state because they will first be resumed then paused on the client side.
@@ -1398,7 +736,7 @@
                 // Deferring resume here because we're going to launch new activity shortly.
                 // We don't want to perform a redundant launch of the same record while ensuring
                 // configurations and trying to resume top activity of focused stack.
-                ensureVisibilityAndConfig(r, r.getDisplayId(),
+                mRootActivityContainer.ensureVisibilityAndConfig(r, r.getDisplayId(),
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
 
@@ -1568,7 +906,7 @@
         // launching the initial activity (that is, home), so that it can have
         // a chance to initialize itself while in the background, making the
         // switch back to it faster and look better.
-        if (isTopDisplayFocusedStack(stack)) {
+        if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
             mService.getActivityStartController().startSetupActivity();
         }
 
@@ -1581,47 +919,6 @@
         return true;
     }
 
-    /**
-     * Ensure all activities visibility, update orientation and configuration.
-     *
-     * @param starting The currently starting activity or {@code null} if there is none.
-     * @param displayId The id of the display where operation is executed.
-     * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
-     *                                  {@code true} if config changed.
-     * @param deferResume Whether to defer resume while updating config.
-     * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
-     *         because of configuration update.
-     */
-    boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
-            boolean markFrozenIfConfigChanged, boolean deferResume) {
-        // First ensure visibility without updating the config just yet. We need this to know what
-        // activities are affecting configuration now.
-        // Passing null here for 'starting' param value, so that visibility of actual starting
-        // activity will be properly updated.
-        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
-                false /* preserveWindows */, false /* notifyClients */);
-
-        if (displayId == INVALID_DISPLAY) {
-            // The caller didn't provide a valid display id, skip updating config.
-            return true;
-        }
-
-        // Force-update the orientation from the WindowManager, since we need the true configuration
-        // to send to the client now.
-        final Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                getDisplayOverrideConfiguration(displayId),
-                starting != null && starting.mayFreezeScreenLocked(starting.app)
-                        ? starting.appToken : null,
-                displayId, true /* forceUpdate */);
-        if (starting != null && markFrozenIfConfigChanged && config != null) {
-            starting.frozenBeforeDestroy = true;
-        }
-
-        // Update the configuration of the activities on the display.
-        return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
-                displayId);
-    }
-
     private void logIfTransactionTooLarge(Intent intent, Bundle icicle) {
         int extrasSize = 0;
         if (intent != null) {
@@ -1677,47 +974,6 @@
         mService.mH.sendMessage(msg);
     }
 
-    void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
-        boolean sendHint = forceSend;
-
-        if (!sendHint) {
-            // Send power hint if we don't know what we're launching yet
-            sendHint = targetActivity == null || targetActivity.app == null;
-        }
-
-        if (!sendHint) { // targetActivity != null
-            // Send power hint when the activity's process is different than the current resumed
-            // activity on all displays, or if there are no resumed activities in the system.
-            boolean noResumedActivities = true;
-            boolean allFocusedProcessesDiffer = true;
-            for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
-                final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-                final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
-                final WindowProcessController resumedActivityProcess =
-                    resumedActivity == null ? null : resumedActivity.app;
-
-                noResumedActivities &= resumedActivityProcess == null;
-                if (resumedActivityProcess != null) {
-                    allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
-                }
-            }
-            sendHint = noResumedActivities || allFocusedProcessesDiffer;
-        }
-
-        if (sendHint && mService.mPowerManagerInternal != null) {
-            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
-            mPowerHintSent = true;
-        }
-    }
-
-    void sendPowerHintForLaunchEndIfNeeded() {
-        // Trigger launch power hint if activity is launched
-        if (mPowerHintSent && mService.mPowerManagerInternal != null) {
-            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
-            mPowerHintSent = false;
-        }
-    }
-
     boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
             int requestCode, int callingPid, int callingUid, String callingPackage,
             boolean ignoreTargetSecurity, boolean launchingInTask,
@@ -1796,7 +1052,8 @@
             return true;
         }
 
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(launchDisplayId);
+        final ActivityDisplay activityDisplay =
+                mRootActivityContainer.getActivityDisplayOrCreate(launchDisplayId);
         if (activityDisplay == null || activityDisplay.isRemoved()) {
             Slog.w(TAG, "Launch on display check: display not found");
             return false;
@@ -1858,21 +1115,6 @@
         return false;
     }
 
-    /** Update lists of UIDs that are present on displays and have access to them. */
-    void updateUIDsPresentOnDisplay() {
-        mDisplayAccessUIDs.clear();
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-            // Only bother calculating the whitelist for private displays
-            if (activityDisplay.isPrivate()) {
-                mDisplayAccessUIDs.append(
-                        activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
-            }
-        }
-        // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
-        mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
-    }
-
     UserInfo getUserInfo(int userId) {
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -2024,7 +1266,8 @@
 
             // Check if able to finish booting when device is booting and all resumed activities
             // are idle.
-            if ((mService.isBooting() && allResumedActivitiesIdle()) || fromTimeout) {
+            if ((mService.isBooting() && mRootActivityContainer.allResumedActivitiesIdle())
+                    || fromTimeout) {
                 booting = checkFinishBootingLocked();
             }
 
@@ -2033,7 +1276,7 @@
             r.mRelaunchReason = RELAUNCH_REASON_NONE;
         }
 
-        if (allResumedActivitiesIdle()) {
+        if (mRootActivityContainer.allResumedActivitiesIdle()) {
             if (r != null) {
                 mService.scheduleAppGcsLocked();
             }
@@ -2046,7 +1289,7 @@
                 }
                 mLaunchingActivity.release();
             }
-            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
         }
 
         // Atomically retrieve all of the other things to do.
@@ -2102,186 +1345,13 @@
         //mWindowManager.dump();
 
         if (activityRemoved) {
-            resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
         }
 
         return r;
     }
 
-    boolean handleAppDiedLocked(WindowProcessController app) {
-        boolean hasVisibleActivities = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                hasVisibleActivities |= stack.handleAppDiedLocked(app);
-            }
-        }
-        return hasVisibleActivities;
-    }
-
-    void closeSystemDialogsLocked() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.closeSystemDialogsLocked();
-            }
-        }
-    }
-
-    void removeUserLocked(int userId) {
-        mUserStackInFront.delete(userId);
-    }
-
-    /**
-     * Update the last used stack id for non-current user (current user's last
-     * used stack is the focused stack)
-     */
-    void updateUserStackLocked(int userId, ActivityStack stack) {
-        if (userId != mCurrentUser) {
-            mUserStackInFront.put(userId, stack != null ? stack.getStackId()
-                    : getDefaultDisplay().getHomeStack().mStackId);
-        }
-    }
-
-    /**
-     * @return true if some activity was finished (or would have finished if doit were true).
-     */
-    boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses,
-            boolean doit, boolean evenPersistent, int userId) {
-        boolean didSomething = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (stack.finishDisabledPackageActivitiesLocked(
-                        packageName, filterByClasses, doit, evenPersistent, userId)) {
-                    didSomething = true;
-                }
-            }
-        }
-        return didSomething;
-    }
-
-    void updatePreviousProcessLocked(ActivityRecord r) {
-        // Now that this process has stopped, we may want to consider
-        // it to be the previous app to try to keep around in case
-        // the user wants to return to it.
-
-        // First, found out what is currently the foreground app, so that
-        // we don't blow away the previous app if this activity is being
-        // hosted by the process that is actually still the foreground.
-        WindowProcessController fgApp = null;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (isTopDisplayFocusedStack(stack)) {
-                    final ActivityRecord resumedActivity = stack.getResumedActivity();
-                    if (resumedActivity != null) {
-                        fgApp = resumedActivity.app;
-                    } else if (stack.mPausingActivity != null) {
-                        fgApp = stack.mPausingActivity.app;
-                    }
-                    break;
-                }
-            }
-        }
-
-        // Now set this one as the previous process, only if that really
-        // makes sense to.
-        if (r.hasProcess() && fgApp != null && r.app != fgApp
-                && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
-                && r.app != mService.mHomeProcess) {
-            mService.mPreviousProcess = r.app;
-            mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
-        }
-    }
-
-    boolean resumeFocusedStacksTopActivitiesLocked() {
-        return resumeFocusedStacksTopActivitiesLocked(null, null, null);
-    }
-
-    boolean resumeFocusedStacksTopActivitiesLocked(
-            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
-
-        if (!readyToResume()) {
-            return false;
-        }
-
-        if (targetStack != null && (targetStack.isTopStackOnDisplay()
-                || getTopDisplayFocusedStack() == targetStack)) {
-            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
-        }
-
-        // Resume all top activities in focused stacks on all displays.
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final ActivityStack focusedStack = display.getFocusedStack();
-            if (focusedStack == null) {
-                continue;
-            }
-            final ActivityRecord r = focusedStack.topRunningActivityLocked();
-            if (r == null || !r.isState(RESUMED)) {
-                focusedStack.resumeTopActivityUncheckedLocked(null, null);
-            } else if (r.isState(RESUMED)) {
-                // Kick off any lingering app transitions form the MoveTaskToFront operation.
-                focusedStack.executeAppTransition(targetOptions);
-            }
-        }
-
-        return false;
-    }
-
-    void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.updateActivityApplicationInfoLocked(aInfo);
-            }
-        }
-    }
-
-    /**
-     * Finish the topmost activities in all stacks that belong to the crashed app.
-     * @param app The app that crashed.
-     * @param reason Reason to perform this action.
-     * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
-     */
-    int finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
-        TaskRecord finishedTask = null;
-        ActivityStack focusedStack = getTopDisplayFocusedStack();
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            // It is possible that request to finish activity might also remove its task and stack,
-            // so we need to be careful with indexes in the loop and check child count every time.
-            for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason);
-                if (stack == focusedStack || finishedTask == null) {
-                    finishedTask = t;
-                }
-            }
-        }
-        return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
-    }
-
-    void finishVoiceTask(IVoiceInteractionSession session) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final int numStacks = display.getChildCount();
-            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.finishVoiceTask(session);
-            }
-        }
-    }
-
-    /**
-     * This doesn't just find a task, it also moves the task to front.
-     */
+    /** This doesn't just find a task, it also moves the task to front. */
     void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
             boolean forceNonResizeable) {
         ActivityStack currentStack = task.getStack();
@@ -2301,7 +1371,8 @@
             final Rect bounds = options.getLaunchBounds();
             task.updateOverrideConfiguration(bounds);
 
-            ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
+            ActivityStack stack =
+                    mRootActivityContainer.getLaunchStack(null, options, task, ON_TOP);
 
             if (stack != currentStack) {
                 moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason);
@@ -2313,7 +1384,7 @@
                 // still need moveTaskToFrontLocked() below for any transition settings.
             }
             if (stack.resizeStackWithLaunchBounds()) {
-                resizeStackLocked(stack, bounds, null /* tempTaskBounds */,
+                mRootActivityContainer.resizeStack(stack, bounds, null /* tempTaskBounds */,
                         null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
                         true /* allowResizeInDockedMode */, !DEFER_RESUME);
             } else {
@@ -2366,387 +1437,22 @@
         return mLaunchParamsController;
     }
 
-    protected <T extends ActivityStack> T getStack(int stackId) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final T stack = mActivityDisplays.get(i).getStack(stackId);
-            if (stack != null) {
-                return stack;
-            }
-        }
-        return null;
+    private void deferUpdateRecentsHomeStackBounds() {
+        mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
+        mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_HOME);
     }
 
-    /** @see ActivityDisplay#getStack(int, int) */
-    private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
-            if (stack != null) {
-                return stack;
-            }
-        }
-        return null;
-    }
-
-    int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
-            @Nullable TaskRecord task) {
-        // Preference is given to the activity type for the activity then the task since the type
-        // once set shouldn't change.
-        int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
-        if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
-            activityType = task.getActivityType();
-        }
-        if (activityType != ACTIVITY_TYPE_UNDEFINED) {
-            return activityType;
-        }
-        if (options != null) {
-            activityType = options.getLaunchActivityType();
-        }
-        return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD;
-    }
-
-    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
-            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
-        return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */);
-    }
-
-    /**
-     * Returns the right stack to use for launching factoring in all the input parameters.
-     *
-     * @param r The activity we are trying to launch. Can be null.
-     * @param options The activity options used to the launch. Can be null.
-     * @param candidateTask The possible task the activity might be launched in. Can be null.
-     * @params launchParams The resolved launch params to use.
-     *
-     * @return The stack to use for the launch or INVALID_STACK_ID.
-     */
-    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
-            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
-            @Nullable LaunchParamsController.LaunchParams launchParams) {
-        int taskId = INVALID_TASK_ID;
-        int displayId = INVALID_DISPLAY;
-        //Rect bounds = null;
-
-        // We give preference to the launch preference in activity options.
-        if (options != null) {
-            taskId = options.getLaunchTaskId();
-        }
-
-        // First preference for stack goes to the task Id set in the activity options. Use the stack
-        // associated with that if possible.
-        if (taskId != INVALID_TASK_ID) {
-            // Temporarily set the task id to invalid in case in re-entry.
-            options.setLaunchTaskId(INVALID_TASK_ID);
-            final TaskRecord task = anyTaskForIdLocked(taskId,
-                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
-            options.setLaunchTaskId(taskId);
-            if (task != null) {
-                return task.getStack();
-            }
-        }
-
-        final int activityType = resolveActivityType(r, options, candidateTask);
-        T stack;
-
-        // Next preference for stack goes to the display Id set the candidate display.
-        if (launchParams != null) {
-            displayId = launchParams.mPreferredDisplayId;
-        }
-        if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
-            if (r != null) {
-                stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options,
-                        launchParams);
-                if (stack != null) {
-                    return stack;
-                }
-            }
-            final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
-            if (display != null) {
-                stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
-                if (stack != null) {
-                    return stack;
-                }
-            }
-        }
-
-        // Give preference to the stack and display of the input task and activity if they match the
-        // mode we want to launch into.
-        stack = null;
-        ActivityDisplay display = null;
-        if (candidateTask != null) {
-            stack = candidateTask.getStack();
-        }
-        if (stack == null && r != null) {
-            stack = r.getStack();
-        }
-        if (stack != null) {
-            display = stack.getDisplay();
-            if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
-                int windowingMode = launchParams != null ? launchParams.mWindowingMode
-                        : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-                if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
-                    windowingMode = display.resolveWindowingMode(r, options, candidateTask,
-                            activityType);
-                }
-                if (stack.isCompatible(windowingMode, activityType)) {
-                    return stack;
-                }
-                if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
-                        && display.getSplitScreenPrimaryStack() == stack
-                        && candidateTask == stack.topTask()) {
-                    // This is a special case when we try to launch an activity that is currently on
-                    // top of split-screen primary stack, but is targeting split-screen secondary.
-                    // In this case we don't want to move it to another stack.
-                    // TODO(b/78788972): Remove after differentiating between preferred and required
-                    // launch options.
-                    return stack;
-                }
-            }
-        }
-
-        if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
-            display = getDefaultDisplay();
-        }
-
-        return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
-    }
-
-    /** @return true if activity record is null or can be launched on provided display. */
-    private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) {
-        if (r == null) {
-            return true;
-        }
-        return r.canBeLaunchedOnDisplay(displayId);
-    }
-
-    /**
-     * Get a topmost stack on the display, that is a valid launch stack for specified activity.
-     * If there is no such stack, new dynamic stack can be created.
-     * @param displayId Target display.
-     * @param r Activity that should be launched there.
-     * @param candidateTask The possible task the activity might be put in.
-     * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
-     */
-    private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
-            @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options,
-            @Nullable LaunchParamsController.LaunchParams launchParams) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException(
-                    "Display with displayId=" + displayId + " not found.");
-        }
-
-        if (!r.canBeLaunchedOnDisplay(displayId)) {
-            return null;
-        }
-
-        // If {@code r} is already in target display and its task is the same as the candidate task,
-        // the intention should be getting a launch stack for the reusable activity, so we can use
-        // the existing stack.
-        if (r.getDisplayId() == displayId && r.getTask() == candidateTask) {
-            return candidateTask.getStack();
-        }
-
-        // Return the topmost valid stack on the display.
-        for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = activityDisplay.getChildAt(i);
-            if (isValidLaunchStack(stack, displayId, r)) {
-                return stack;
-            }
-        }
-
-        // If there is no valid stack on the external display - check if new dynamic stack will do.
-        if (displayId != DEFAULT_DISPLAY) {
-            final int windowingMode;
-            if (launchParams != null) {
-                // When launch params is not null, we always defer to its windowing mode. Sometimes
-                // it could be unspecified, which indicates it should inherit windowing mode from
-                // display.
-                windowingMode = launchParams.mWindowingMode;
-            } else {
-                windowingMode = options != null ? options.getLaunchWindowingMode()
-                        : r.getWindowingMode();
-            }
-            return activityDisplay.createStack(
-                    windowingMode,
-                    options != null ? options.getLaunchActivityType() : r.getActivityType(),
-                    true /*onTop*/);
-        }
-
-        Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
-        return null;
-    }
-
-    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
-            @Nullable ActivityOptions options,
-            @Nullable LaunchParamsController.LaunchParams launchParams) {
-        return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options,
-                launchParams);
-    }
-
-    // TODO: Can probably be consolidated into getLaunchStack()...
-    private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) {
-        switch (stack.getActivityType()) {
-            case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
-            case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
-            case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
-        }
-        // There is a 1-to-1 relationship between stack and task when not in
-        // primary split-windowing mode.
-        if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            return false;
-        } else {
-            return r.supportsSplitScreenWindowingMode();
-        }
-    }
-
-    /**
-     * Get next focusable stack in the system. This will search through the stack on the same
-     * display as the current focused stack, looking for a focusable and visible stack, different
-     * from the target stack. If no valid candidates will be found, it will then go through all
-     * displays and stacks in last-focused order.
-     *
-     * @param currentFocus The stack that previously had focus.
-     * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
-     *                     candidate.
-     * @return Next focusable {@link ActivityStack}, {@code null} if not found.
-     */
-    ActivityStack getNextFocusableStackLocked(@NonNull ActivityStack currentFocus,
-            boolean ignoreCurrent) {
-        // First look for next focusable stack on the same display
-        final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
-        final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
-                currentFocus, ignoreCurrent);
-        if (preferredFocusableStack != null) {
-            return preferredFocusableStack;
-        }
-        if (preferredDisplay.supportsSystemDecorations()) {
-            // Stop looking for focusable stack on other displays because the preferred display
-            // supports system decorations. Home activity would be launched on the same display if
-            // no focusable stack found.
-            return null;
-        }
-
-        // Now look through all displays
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            if (display == preferredDisplay) {
-                // We've already checked this one
-                continue;
-            }
-            final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
-                    ignoreCurrent);
-            if (nextFocusableStack != null) {
-                return nextFocusableStack;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Get next valid stack for launching provided activity in the system. This will search across
-     * displays and stacks in last-focused order for a focusable and visible stack, except those
-     * that are on a currently focused display.
-     *
-     * @param r The activity that is being launched.
-     * @param currentFocus The display that previously had focus and thus needs to be ignored when
-     *                     searching for the next candidate.
-     * @return Next valid {@link ActivityStack}, null if not found.
-     */
-    ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            if (display.mDisplayId == currentFocus) {
-                continue;
-            }
-            final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r,
-                    null /* options */, null /* launchParams */);
-            if (stack != null) {
-                return stack;
-            }
-        }
-        return null;
-    }
-
-    ActivityRecord getDefaultDisplayHomeActivity() {
-        return getDefaultDisplayHomeActivityForUser(mCurrentUser);
-    }
-
-    ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
-        return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
-    }
-
-    void resizeStackLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
-            Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
-            boolean deferResume) {
-
-        if (stack.inSplitScreenPrimaryWindowingMode()) {
-            resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
-                    preserveWindows, deferResume);
-            return;
-        }
-
-        final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
-        if (!allowResizeInDockedMode
-                && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
-            // If the docked stack exists, don't resize non-floating stacks independently of the
-            // size computed from the docked stack size (otherwise they will be out of sync)
-            return;
-        }
-
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
-        mWindowManager.deferSurfaceLayout();
-        try {
-            if (stack.affectedBySplitScreenResize()) {
-                if (bounds == null && stack.inSplitScreenWindowingMode()) {
-                    // null bounds = fullscreen windowing mode...at least for now.
-                    stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-                } else if (splitScreenActive) {
-                    // If we are in split-screen mode and this stack support split-screen, then
-                    // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
-                    stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-                }
-            }
-            stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
-            if (!deferResume) {
-                stack.ensureVisibleActivitiesConfigurationLocked(
-                        stack.topRunningActivityLocked(), preserveWindows);
-            }
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    void deferUpdateRecentsHomeStackBounds() {
-        deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
-        deferUpdateBounds(ACTIVITY_TYPE_HOME);
-    }
-
-    void deferUpdateBounds(int activityType) {
-        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
-        if (stack != null) {
-            stack.deferUpdateBounds();
-        }
-    }
-
-    void continueUpdateRecentsHomeStackBounds() {
-        continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
-        continueUpdateBounds(ACTIVITY_TYPE_HOME);
-    }
-
-    void continueUpdateBounds(int activityType) {
-        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
-        if (stack != null) {
-            stack.continueUpdateBounds();
-        }
+    private void continueUpdateRecentsHomeStackBounds() {
+        mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
+        mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_HOME);
     }
 
     void notifyAppTransitionDone() {
         continueUpdateRecentsHomeStackBounds();
         for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
             final int taskId = mResizingTasksDuringAnimation.valueAt(i);
-            final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY);
+            final TaskRecord task =
+                    mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY);
             if (task != null) {
                 task.setTaskDockedResizing(false);
             }
@@ -2765,7 +1471,8 @@
         try {
             final int windowingMode = fromStack.getWindowingMode();
             final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
-            final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId);
+            final ActivityDisplay toDisplay =
+                    mRootActivityContainer.getActivityDisplay(toDisplayId);
 
             if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 // Tell the display we are exiting split-screen mode.
@@ -2822,8 +1529,8 @@
                 }
             }
 
-            ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-            resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
         } finally {
             mAllowDockedStackResize = true;
             mWindowManager.continueSurfaceLayout();
@@ -2869,7 +1576,7 @@
                 false /* deferResume */);
     }
 
-    private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+    void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
             Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
             boolean preserveWindows, boolean deferResume) {
 
@@ -2878,7 +1585,8 @@
             return;
         }
 
-        final ActivityStack stack = getDefaultDisplay().getSplitScreenPrimaryStack();
+        final ActivityStack stack =
+                mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
         if (stack == null) {
             Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
             return;
@@ -2917,7 +1625,7 @@
                 // static stacks need to be adjusted so they don't overlap with the docked stack.
                 // We get the bounds to use from window manager which has been adjusted for any
                 // screen controls and is also the same for all stacks.
-                final ActivityDisplay display = getDefaultDisplay();
+                final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
                 final Rect otherTaskRect = new Rect();
                 for (int i = display.getChildCount() - 1; i >= 0; --i) {
                     final ActivityStack current = display.getChildAt(i);
@@ -2937,7 +1645,8 @@
                             tempRect /* outStackBounds */,
                             otherTaskRect /* outTempTaskBounds */);
 
-                    resizeStackLocked(current, !tempRect.isEmpty() ? tempRect : null,
+                    mRootActivityContainer.resizeStack(current,
+                            !tempRect.isEmpty() ? tempRect : null,
                             !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
                             tempOtherTaskInsetBounds, preserveWindows,
                             true /* allowResizeInDockedMode */, deferResume);
@@ -2955,7 +1664,8 @@
 
     void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
         // TODO(multi-display): Pinned stack display should be passed in.
-        final PinnedActivityStack stack = getDefaultDisplay().getPinnedStack();
+        final PinnedActivityStack stack =
+                mRootActivityContainer.getDefaultDisplay().getPinnedStack();
         if (stack == null) {
             Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
             return;
@@ -3036,22 +1746,6 @@
     }
 
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
-     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
-     */
-    void removeStacksInWindowingModes(int... windowingModes) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes);
-        }
-    }
-
-    void removeStacksWithActivityTypes(int... activityTypes) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes);
-        }
-    }
-
-    /**
      * See {@link #removeTaskByIdLocked(int, boolean, boolean, boolean)}
      */
     boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
@@ -3072,7 +1766,8 @@
      */
     boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
             boolean pauseImmediately, String reason) {
-        final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+        final TaskRecord tr =
+                mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
         if (tr != null) {
             tr.removeTaskActivitiesLocked(pauseImmediately, reason);
             cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
@@ -3161,7 +1856,8 @@
      * @return true if the task has been restored successfully.
      */
     boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions, boolean onTop) {
-        final ActivityStack stack = getLaunchStack(null, aOptions, task, onTop);
+        final ActivityStack stack =
+                mRootActivityContainer.getLaunchStack(null, aOptions, task, onTop);
         final ActivityStack currentStack = task.getStack();
         if (currentStack != null) {
             // Task has already been restored once. See if we need to do anything more
@@ -3181,7 +1877,7 @@
                 "Added restored task=" + task + " to stack=" + stack);
         final ArrayList<ActivityRecord> activities = task.mActivities;
         for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-            activities.get(activityNdx).createWindowContainer();
+            activities.get(activityNdx).createAppWindowToken();
         }
         return true;
     }
@@ -3203,39 +1899,6 @@
     }
 
     /**
-     * Move stack with all its existing content to specified display.
-     * @param stackId Id of stack to move.
-     * @param displayId Id of display to move stack to.
-     * @param onTop Indicates whether container should be place on top or on bottom.
-     */
-    void moveStackToDisplayLocked(int stackId, int displayId, boolean onTop) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId="
-                    + displayId);
-        }
-        final ActivityStack stack = getStack(stackId);
-        if (stack == null) {
-            throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId="
-                    + stackId);
-        }
-
-        final ActivityDisplay currentDisplay = stack.getDisplay();
-        if (currentDisplay == null) {
-            throw new IllegalStateException("moveStackToDisplayLocked: Stack with stack=" + stack
-                    + " is not attached to any display.");
-        }
-
-        if (currentDisplay.mDisplayId == displayId) {
-            throw new IllegalArgumentException("Trying to move stack=" + stack
-                    + " to its current displayId=" + displayId);
-        }
-
-        stack.reparent(activityDisplay, onTop, false /* displayRemoved */);
-        // TODO(multi-display): resize stacks properly if moved from split-screen.
-    }
-
-    /**
      * Returns the reparent target stack, creating the stack if necessary.  This call also enforces
      * the various checks on tasks that are going to be reparented from one stack to another.
      */
@@ -3287,159 +1950,6 @@
         return stack;
     }
 
-    boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) {
-        final ActivityStack stack = getStack(stackId);
-        if (stack == null) {
-            throw new IllegalArgumentException(
-                    "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
-        }
-
-        final ActivityRecord r = stack.topRunningActivityLocked();
-        if (r == null) {
-            Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity"
-                    + " in stack=" + stack);
-            return false;
-        }
-
-        if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
-            Slog.w(TAG,
-                    "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
-                            + " r=" + r);
-            return false;
-        }
-
-        moveActivityToPinnedStackLocked(r, null /* sourceBounds */, 0f /* aspectRatio */,
-                "moveTopActivityToPinnedStack");
-        return true;
-    }
-
-    void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
-            String reason) {
-
-        mWindowManager.deferSurfaceLayout();
-
-        final ActivityDisplay display = r.getStack().getDisplay();
-        PinnedActivityStack stack = display.getPinnedStack();
-
-        // This will clear the pinned stack by moving an existing task to the full screen stack,
-        // ensuring only one task is present.
-        if (stack != null) {
-            moveTasksToFullscreenStackLocked(stack, !ON_TOP);
-        }
-
-        // Need to make sure the pinned stack exist so we can resize it below...
-        stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
-
-        // Calculate the target bounds here before the task is reparented back into pinned windowing
-        // mode (which will reset the saved bounds)
-        final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
-
-        try {
-            final TaskRecord task = r.getTask();
-            // Resize the pinned stack to match the current size of the task the activity we are
-            // going to be moving is currently contained in. We do this to have the right starting
-            // animation bounds for the pinned stack to the desired bounds the caller wants.
-            resizeStackLocked(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
-                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
-                    true /* allowResizeInDockedMode */, !DEFER_RESUME);
-
-            if (task.mActivities.size() == 1) {
-                // Defer resume until below, and do not schedule PiP changes until we animate below
-                task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
-                        false /* schedulePictureInPictureModeChange */, reason);
-            } else {
-                // There are multiple activities in the task and moving the top activity should
-                // reveal/leave the other activities in their original task.
-
-                // Currently, we don't support reparenting activities across tasks in two different
-                // stacks, so instead, just create a new task in the same stack, reparent the
-                // activity into that task, and then reparent the whole task to the new stack. This
-                // ensures that all the necessary work to migrate states in the old and new stacks
-                // is also done.
-                final TaskRecord newTask = task.getStack().createTaskRecord(
-                        getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true);
-                r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
-
-                // Defer resume until below, and do not schedule PiP changes until we animate below
-                newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
-                        DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
-            }
-
-            // Reset the state that indicates it can enter PiP while pausing after we've moved it
-            // to the pinned stack
-            r.supportsEnterPipOnTaskSwitch = false;
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-        }
-
-        stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
-                true /* fromFullscreen */);
-
-        // Update the visibility of all activities after the they have been reparented to the new
-        // stack.  This MUST run after the animation above is scheduled to ensure that the windows
-        // drawn signal is scheduled after the bounds animation start call on the bounds animator
-        // thread.
-        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        resumeFocusedStacksTopActivitiesLocked();
-
-        mService.getTaskChangeNotificationController().notifyActivityPinned(r);
-    }
-
-    ActivityRecord findTaskLocked(ActivityRecord r, int preferredDisplayId) {
-        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
-        mTmpFindTaskResult.clear();
-
-        // Looking up task on preferred display first
-        final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId);
-        if (preferredDisplay != null) {
-            preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
-            if (mTmpFindTaskResult.mIdealMatch) {
-                return mTmpFindTaskResult.mRecord;
-            }
-        }
-
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            if (display.mDisplayId == preferredDisplayId) {
-                continue;
-            }
-
-            display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
-            if (mTmpFindTaskResult.mIdealMatch) {
-                return mTmpFindTaskResult.mRecord;
-            }
-        }
-
-        if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
-        return mTmpFindTaskResult.mRecord;
-    }
-
-    ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
-            boolean compareIntentFilters) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord ar = stack.findActivityLocked(
-                        intent, info, compareIntentFilters);
-                if (ar != null) {
-                    return ar;
-                }
-            }
-        }
-        return null;
-    }
-
-    boolean hasAwakeDisplay() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            if (!display.shouldSleep()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void goingToSleepLocked() {
         scheduleSleepTimeout();
         if (!mGoingToSleep.isHeld()) {
@@ -3453,24 +1963,19 @@
             }
         }
 
-        applySleepTokensLocked(false /* applyToStacks */);
+        mRootActivityContainer.applySleepTokens(false /* applyToStacks */);
 
         checkReadyForSleepLocked(true /* allowDelay */);
     }
 
-    void prepareForShutdownLocked() {
-        for (int i = 0; i < mActivityDisplays.size(); i++) {
-            createSleepTokenLocked("shutdown", mActivityDisplays.get(i).mDisplayId);
-        }
-    }
-
     boolean shutdownLocked(int timeout) {
         goingToSleepLocked();
 
         boolean timedout = false;
         final long endTime = System.currentTimeMillis() + timeout;
         while (true) {
-            if (!putStacksToSleepLocked(true /* allowDelay */, true /* shuttingDown */)) {
+            if (!mRootActivityContainer.putStacksToSleep(
+                    true /* allowDelay */, true /* shuttingDown */)) {
                 long timeRemaining = endTime - System.currentTimeMillis();
                 if (timeRemaining > 0) {
                     try {
@@ -3500,51 +2005,6 @@
         }
     }
 
-    void applySleepTokensLocked(boolean applyToStacks) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            // Set the sleeping state of the display.
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final boolean displayShouldSleep = display.shouldSleep();
-            if (displayShouldSleep == display.isSleeping()) {
-                continue;
-            }
-            display.setIsSleeping(displayShouldSleep);
-
-            if (!applyToStacks) {
-                continue;
-            }
-
-            // Set the sleeping state of the stacks on the display.
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (displayShouldSleep) {
-                    stack.goToSleepIfPossible(false /* shuttingDown */);
-                } else {
-                    stack.awakeFromSleepingLocked();
-                    if (stack.isFocusedStackOnDisplay() && !getKeyguardController()
-                            .isKeyguardOrAodShowing(display.mDisplayId)) {
-                        // If the keyguard is unlocked - resume immediately.
-                        // It is possible that the display will not be awake at the time we
-                        // process the keyguard going away, which can happen before the sleep token
-                        // is released. As a result, it is important we resume the activity here.
-                        resumeFocusedStacksTopActivitiesLocked();
-                    }
-                }
-            }
-
-            if (displayShouldSleep || mGoingToSleepActivities.isEmpty()) {
-                continue;
-            }
-            // The display is awake now, so clean up the going to sleep list.
-            for (Iterator<ActivityRecord> it = mGoingToSleepActivities.iterator(); it.hasNext(); ) {
-                final ActivityRecord r = it.next();
-                if (r.getDisplayId() == display.mDisplayId) {
-                    it.remove();
-                }
-            }
-        }
-    }
-
     void activitySleptLocked(ActivityRecord r) {
         mGoingToSleepActivities.remove(r);
         final ActivityStack s = r.getStack();
@@ -3561,12 +2021,13 @@
             return;
         }
 
-        if (!putStacksToSleepLocked(allowDelay, false /* shuttingDown */)) {
+        if (!mRootActivityContainer.putStacksToSleep(
+                allowDelay, false /* shuttingDown */)) {
             return;
         }
 
         // Send launch end powerhint before going sleep
-        sendPowerHintForLaunchEndIfNeeded();
+        mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded();
 
         removeSleepTimeouts();
 
@@ -3578,52 +2039,24 @@
         }
     }
 
-    // Tries to put all activity stacks to sleep. Returns true if all stacks were
-    // successfully put to sleep.
-    private boolean putStacksToSleepLocked(boolean allowDelay, boolean shuttingDown) {
-        boolean allSleep = true;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (allowDelay) {
-                    allSleep &= stack.goToSleepIfPossible(shuttingDown);
-                } else {
-                    stack.goToSleep();
-                }
-            }
-        }
-        return allSleep;
-    }
-
     boolean reportResumedActivityLocked(ActivityRecord r) {
         // A resumed activity cannot be stopping. remove from list
         mStoppingActivities.remove(r);
 
         final ActivityStack stack = r.getStack();
-        if (isTopDisplayFocusedStack(stack)) {
+        if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
             mService.updateUsageStats(r, true);
         }
         if (stack.getDisplay().allResumedActivitiesComplete()) {
-            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
             // Make sure activity & window visibility should be identical
             // for all displays in this stage.
-            executeAppTransitionForAllDisplay();
+            mRootActivityContainer.executeAppTransitionForAllDisplay();
             return true;
         }
         return false;
     }
 
-    void handleAppCrashLocked(WindowProcessController app) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.handleAppCrashLocked(app);
-            }
-        }
-    }
-
     // Called when WindowManager has finished animating the launchingBehind activity to the back.
     private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
         final TaskRecord task = r.getTask();
@@ -3646,157 +2079,9 @@
         mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
     }
 
-    /**
-     * Make sure that all activities that need to be visible in the system actually are and update
-     * their configuration.
-     */
-    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
-            boolean preserveWindows) {
-        ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
-                true /* notifyClients */);
-    }
-
-    /**
-     * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
-     */
-    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients) {
-        getKeyguardController().beginActivityVisibilityUpdate();
-        try {
-            // First the front stacks. In case any are not fullscreen and are in front of home.
-            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
-                    stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
-                            notifyClients);
-                }
-            }
-        } finally {
-            getKeyguardController().endActivityVisibilityUpdate();
-        }
-    }
-
-    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.addStartingWindowsForVisibleActivities(taskSwitch);
-            }
-        }
-    }
-
-    void invalidateTaskLayers() {
-        mTaskLayersChanged = true;
-    }
-
-    void rankTaskLayersIfNeeded() {
-        if (!mTaskLayersChanged) {
-            return;
-        }
-        mTaskLayersChanged = false;
-        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            int baseLayer = 0;
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                baseLayer += stack.rankTaskLayers(baseLayer);
-            }
-        }
-    }
-
-    void clearOtherAppTimeTrackers(AppTimeTracker except) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.clearOtherAppTimeTrackers(except);
-            }
-        }
-    }
-
-    void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.scheduleDestroyActivities(app, reason);
-            }
-        }
-    }
-
-    void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
-        // Tasks is non-null only if two or more tasks are found.
-        ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
-        if (tasks == null) {
-            if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
-            return;
-        }
-        // If we have activities in multiple tasks that are in a position to be destroyed,
-        // let's iterate through the tasks and release the oldest one.
-        final int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final int stackCount = display.getChildCount();
-            // Step through all stacks starting from behind, to hit the oldest things first.
-            for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                // Try to release activities in this stack; if we manage to, we are done.
-                if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
-                    return;
-                }
-            }
-        }
-    }
-
-    boolean switchUserLocked(int userId, UserState uss) {
-        final int focusStackId = getTopDisplayFocusedStack().getStackId();
-        // We dismiss the docked stack whenever we switch users.
-        final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
-        if (dockedStack != null) {
-            moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay());
-        }
-        // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
-        // also cause all tasks to be moved to the fullscreen stack at a position that is
-        // appropriate.
-        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
-
-        mUserStackInFront.put(mCurrentUser, focusStackId);
-        final int restoreStackId =
-                mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
-        mCurrentUser = userId;
-
-        mStartingUsers.add(uss);
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.switchUserLocked(userId);
-                TaskRecord task = stack.topTask();
-                if (task != null) {
-                    stack.positionChildWindowContainerAtTop(task);
-                }
-            }
-        }
-
-        ActivityStack stack = getStack(restoreStackId);
-        if (stack == null) {
-            stack = getDefaultDisplay().getHomeStack();
-        }
-        final boolean homeInFront = stack.isActivityTypeHome();
-        if (stack.isOnHomeDisplay()) {
-            stack.moveToFront("switchUserOnHomeDisplay");
-        } else {
-            // Stack was moved to another display while user was swapped out.
-            resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
-        }
-        return homeInFront;
-    }
-
     /** Checks whether the userid is a profile of the current user. */
     boolean isCurrentProfileLocked(int userId) {
-        if (userId == mCurrentUser) return true;
+        if (userId == mRootActivityContainer.mCurrentUser) return true;
         return mService.mAmInternal.isCurrentProfile(userId);
     }
 
@@ -3821,7 +2106,7 @@
             boolean remove, boolean processPausingActivities) {
         ArrayList<ActivityRecord> stops = null;
 
-        final boolean nowVisible = allResumedActivitiesVisible();
+        final boolean nowVisible = mRootActivityContainer.allResumedActivitiesVisible();
         for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
             ActivityRecord s = mStoppingActivities.get(activityNdx);
             boolean waitingVisible = mActivitiesWaitingForVisibleActivity.contains(s);
@@ -3871,134 +2156,26 @@
         return stops;
     }
 
-    void validateTopActivitiesLocked() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.topRunningActivityLocked();
-                final ActivityState state = r == null ? DESTROYED : r.getState();
-                if (isTopDisplayFocusedStack(stack)) {
-                    if (r == null) Slog.e(TAG,
-                            "validateTop...: null top activity, stack=" + stack);
-                    else {
-                        final ActivityRecord pausing = stack.mPausingActivity;
-                        if (pausing != null && pausing == r) Slog.e(TAG,
-                                "validateTop...: top stack has pausing activity r=" + r
-                                + " state=" + state);
-                        if (state != INITIALIZING && state != RESUMED) Slog.e(TAG,
-                                "validateTop...: activity in front not resumed r=" + r
-                                + " state=" + state);
-                    }
-                } else {
-                    final ActivityRecord resumed = stack.getResumedActivity();
-                    if (resumed != null && resumed == r) Slog.e(TAG,
-                            "validateTop...: back stack has resumed activity r=" + r
-                            + " state=" + state);
-                    if (r != null && (state == INITIALIZING || state == RESUMED)) Slog.e(TAG,
-                            "validateTop...: activity in back resumed r=" + r + " state=" + state);
-                }
-            }
-        }
-    }
-
-    public void dumpDisplays(PrintWriter pw) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            pw.print("[id:" + display.mDisplayId + " stacks:");
-            display.dumpStacks(pw);
-            pw.print("]");
-        }
-    }
-
     public void dump(PrintWriter pw, String prefix) {
         pw.println();
         pw.println("ActivityStackSupervisor state:");
-        pw.print(prefix);
-        pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+        mRootActivityContainer.dump(pw, prefix);
         pw.print(prefix);
         pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
-        pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            display.dump(pw, prefix);
-        }
+        pw.println(prefix + "mUserStackInFront=" + mRootActivityContainer.mUserStackInFront);
         if (!mWaitingForActivityVisible.isEmpty()) {
-            pw.print(prefix); pw.println("mWaitingForActivityVisible=");
+            pw.println(prefix + "mWaitingForActivityVisible=");
             for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) {
-                pw.print(prefix); pw.print(prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
+                pw.print(prefix + prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
             }
         }
         pw.print(prefix); pw.print("isHomeRecentsComponent=");
-        pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+        pw.print(mRecentTasks.isRecentsComponentHomeActivity(mRootActivityContainer.mCurrentUser));
 
         getKeyguardController().dump(pw, prefix);
         mService.getLockTaskController().dump(pw, prefix);
     }
 
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
-        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-            activityDisplay.writeToProto(proto, DISPLAYS);
-        }
-        getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
-        // TODO(b/111541062): Update tests to look for resumed activities on all displays
-        final ActivityStack focusedStack = getTopDisplayFocusedStack();
-        if (focusedStack != null) {
-            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
-            final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
-            if (focusedActivity != null) {
-                focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
-            }
-        } else {
-            proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
-        }
-        proto.write(IS_HOME_RECENTS_COMPONENT,
-                mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
-        mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
-        proto.end(token);
-    }
-
-    /**
-     * Dump all connected displays' configurations.
-     * @param prefix Prefix to apply to each line of the dump.
-     */
-    void dumpDisplayConfigs(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.println("Display override configurations:");
-        final int displayCount = mActivityDisplays.size();
-        for (int i = 0; i < displayCount; i++) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
-            pw.print(prefix); pw.print("  "); pw.print(activityDisplay.mDisplayId); pw.print(": ");
-                    pw.println(activityDisplay.getOverrideConfiguration());
-        }
-    }
-
-    /**
-     * Dumps the activities matching the given {@param name} in the either the focused stack
-     * or all visible stacks if {@param dumpVisibleStacks} is true.
-     */
-    ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly,
-            boolean dumpFocusedStackOnly) {
-        if (dumpFocusedStackOnly) {
-            return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
-        } else {
-            ArrayList<ActivityRecord> activities = new ArrayList<>();
-            int numDisplays = mActivityDisplays.size();
-            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
-                    if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
-                        activities.addAll(stack.getDumpActivitiesLocked(name));
-                    }
-                }
-            }
-            return activities;
-        }
-    }
-
     static boolean printThisActivity(PrintWriter pw, ActivityRecord activity, String dumpPackage,
             boolean needSep, String prefix) {
         if (activity != null) {
@@ -4014,73 +2191,6 @@
         return false;
     }
 
-    boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
-            boolean dumpClient, String dumpPackage) {
-        boolean printed = false;
-        boolean needSep = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-            pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
-                    pw.println(" (activities from top to bottom):");
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                pw.println();
-                pw.println("  Stack #" + stack.mStackId
-                        + ": type=" + activityTypeToString(stack.getActivityType())
-                        + " mode=" + windowingModeToString(stack.getWindowingMode()));
-                pw.println("  isSleeping=" + stack.shouldSleepActivities());
-                pw.println("  mBounds=" + stack.getOverrideBounds());
-
-                printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
-                        needSep);
-
-                printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, "    ", "Run", false,
-                        !dumpAll, false, dumpPackage, true,
-                        "    Running activities (most recent first):", null);
-
-                needSep = printed;
-                boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
-                        "    mPausingActivity: ");
-                if (pr) {
-                    printed = true;
-                    needSep = false;
-                }
-                pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep,
-                        "    mResumedActivity: ");
-                if (pr) {
-                    printed = true;
-                    needSep = false;
-                }
-                if (dumpAll) {
-                    pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
-                            "    mLastPausedActivity: ");
-                    if (pr) {
-                        printed = true;
-                        needSep = true;
-                    }
-                    printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
-                            needSep, "    mLastNoHistoryActivity: ");
-                }
-                needSep = printed;
-            }
-            printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
-                    " ResumedActivity:");
-        }
-
-        printed |= dumpHistoryList(fd, pw, mFinishingActivities, "  ", "Fin", false, !dumpAll,
-                false, dumpPackage, true, "  Activities waiting to finish:", null);
-        printed |= dumpHistoryList(fd, pw, mStoppingActivities, "  ", "Stop", false, !dumpAll,
-                false, dumpPackage, true, "  Activities waiting to stop:", null);
-        printed |= dumpHistoryList(fd, pw, mActivitiesWaitingForVisibleActivity, "  ", "Wait",
-                false, !dumpAll, false, dumpPackage, true,
-                "  Activities waiting for another to become visible:", null);
-        printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, "  ", "Sleep", false, !dumpAll,
-                false, dumpPackage, true, "  Activities waiting to sleep:", null);
-
-        return printed;
-    }
-
     static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
             String prefix, String label, boolean complete, boolean brief, boolean client,
             String dumpPackage, boolean needNL, String header, TaskRecord lastTask) {
@@ -4190,294 +2300,6 @@
         mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT);
     }
 
-    @Override
-    public void onDisplayAdded(int displayId) {
-        if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
-        synchronized (mService.mGlobalLock) {
-            getActivityDisplayOrCreateLocked(displayId);
-            // Do not start home before booting, or it may accidentally finish booting before it
-            // starts. Instead, we expect home activities to be launched when the system is ready
-            // (ActivityManagerService#systemReady).
-            if (mService.isBooted() || mService.isBooting()) {
-                startHomeOnDisplay(mCurrentUser, "displayAdded", displayId);
-            }
-        }
-    }
-
-    @Override
-    public void onDisplayRemoved(int displayId) {
-        if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
-        if (displayId == DEFAULT_DISPLAY) {
-            throw new IllegalArgumentException("Can't remove the primary display.");
-        }
-
-        synchronized (mService.mGlobalLock) {
-            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
-            if (activityDisplay == null) {
-                return;
-            }
-
-            activityDisplay.remove();
-        }
-    }
-
-    @Override
-    public void onDisplayChanged(int displayId) {
-        if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
-        synchronized (mService.mGlobalLock) {
-            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
-            if (activityDisplay != null) {
-                activityDisplay.onDisplayChanged();
-            }
-        }
-    }
-
-    /** Check if display with specified id is added to the list. */
-    boolean isDisplayAdded(int displayId) {
-        return getActivityDisplayOrCreateLocked(displayId) != null;
-    }
-
-    // TODO: Look into consolidating with getActivityDisplayOrCreateLocked()
-    ActivityDisplay getActivityDisplay(int displayId) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
-            if (activityDisplay.mDisplayId == displayId) {
-                return activityDisplay;
-            }
-        }
-        return null;
-    }
-
-    // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
-    ActivityDisplay getDefaultDisplay() {
-        return mDefaultDisplay;
-    }
-
-    /**
-     * Get an existing instance of {@link ActivityDisplay} or create new if there is a
-     * corresponding record in display manager.
-     */
-    // TODO: Look into consolidating with getActivityDisplay()
-    ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
-        ActivityDisplay activityDisplay = getActivityDisplay(displayId);
-        if (activityDisplay != null) {
-            return activityDisplay;
-        }
-        if (mDisplayManager == null) {
-            // The system isn't fully initialized yet.
-            return null;
-        }
-        final Display display = mDisplayManager.getDisplay(displayId);
-        if (display == null) {
-            // The display is not registered in DisplayManager.
-            return null;
-        }
-        // The display hasn't been added to ActivityManager yet, create a new record now.
-        activityDisplay = new ActivityDisplay(this, display);
-        addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
-        return activityDisplay;
-    }
-
-    /**
-     * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is
-     * defined in {@link DisplayInfo#uniqueId}.
-     *
-     * @param uniqueId the unique ID of the display
-     * @return the {@link ActivityDisplay} or {@code null} if nothing is found.
-     */
-    ActivityDisplay getActivityDisplay(String uniqueId) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            final boolean isValid = display.mDisplay.isValid();
-            if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) {
-                return display;
-            }
-        }
-
-        return null;
-    }
-
-    boolean startHomeOnAllDisplays(int userId, String reason) {
-        boolean homeStarted = false;
-        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
-            final int displayId = mActivityDisplays.get(i).mDisplayId;
-            homeStarted |= startHomeOnDisplay(userId, reason, displayId);
-        }
-        return homeStarted;
-    }
-
-    /**
-     * This starts home activity on displays that can have system decorations and only if the
-     * home activity can have multiple instances.
-     */
-    boolean startHomeOnDisplay(int userId, String reason, int displayId) {
-        final Intent homeIntent = mService.getHomeIntent();
-        final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
-        if (aInfo == null) {
-            return false;
-        }
-
-        if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
-            return false;
-        }
-
-        // Update the reason for ANR debugging to verify if the user activity is the one that
-        // actually launched.
-        final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
-                aInfo.applicationInfo.uid);
-        mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
-                displayId);
-        return true;
-    }
-
-    /**
-     * This resolves the home activity info and updates the home component of the given intent.
-     * @return the home activity info if any.
-     */
-    private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
-        final int flags = ActivityManagerService.STOCK_PM_FLAGS;
-        final ComponentName comp = homeIntent.getComponent();
-        ActivityInfo aInfo = null;
-        try {
-            if (comp != null) {
-                // Factory test.
-                aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
-            } else {
-                final String resolvedType =
-                        homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
-                final ResolveInfo info = AppGlobals.getPackageManager()
-                        .resolveIntent(homeIntent, resolvedType, flags, userId);
-                if (info != null) {
-                    aInfo = info.activityInfo;
-                }
-            }
-        } catch (RemoteException e) {
-            // ignore
-        }
-
-        if (aInfo == null) {
-            Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable());
-            return null;
-        }
-
-        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
-        aInfo = new ActivityInfo(aInfo);
-        aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
-        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
-        return aInfo;
-    }
-
-    @VisibleForTesting
-    void addChild(ActivityDisplay activityDisplay, int position) {
-        positionChildAt(activityDisplay, position);
-        mWindowContainerController.positionChildAt(
-                activityDisplay.getWindowContainerController(), position);
-    }
-
-    void removeChild(ActivityDisplay activityDisplay) {
-        // The caller must tell the controller of {@link ActivityDisplay} to release its container
-        // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}).
-        mActivityDisplays.remove(activityDisplay);
-    }
-
-    private void calculateDefaultMinimalSizeOfResizeableTasks() {
-        final Resources res = mService.mContext.getResources();
-        final float minimalSize = res.getDimension(
-                com.android.internal.R.dimen.default_minimal_size_resizable_task);
-        final DisplayMetrics dm = res.getDisplayMetrics();
-
-        mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
-    }
-
-    SleepToken createSleepTokenLocked(String tag, int displayId) {
-        final ActivityDisplay display = getActivityDisplay(displayId);
-        if (display == null) {
-            throw new IllegalArgumentException("Invalid display: " + displayId);
-        }
-
-        final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
-        mSleepTokens.add(token);
-        display.mAllSleepTokens.add(token);
-        return token;
-    }
-
-    private void removeSleepTokenLocked(SleepTokenImpl token) {
-        mSleepTokens.remove(token);
-
-        final ActivityDisplay display = getActivityDisplay(token.mDisplayId);
-        if (display != null) {
-            display.mAllSleepTokens.remove(token);
-            if (display.mAllSleepTokens.isEmpty()) {
-                mService.updateSleepIfNeededLocked();
-            }
-        }
-    }
-
-    private StackInfo getStackInfo(ActivityStack stack) {
-        final int displayId = stack.mDisplayId;
-        final ActivityDisplay display = getActivityDisplay(displayId);
-        StackInfo info = new StackInfo();
-        stack.getWindowContainerBounds(info.bounds);
-        info.displayId = displayId;
-        info.stackId = stack.mStackId;
-        info.userId = stack.mCurrentUser;
-        info.visible = stack.shouldBeVisible(null);
-        // A stack might be not attached to a display.
-        info.position = display != null ? display.getIndexOf(stack) : 0;
-        info.configuration.setTo(stack.getConfiguration());
-
-        ArrayList<TaskRecord> tasks = stack.getAllTasks();
-        final int numTasks = tasks.size();
-        int[] taskIds = new int[numTasks];
-        String[] taskNames = new String[numTasks];
-        Rect[] taskBounds = new Rect[numTasks];
-        int[] taskUserIds = new int[numTasks];
-        for (int i = 0; i < numTasks; ++i) {
-            final TaskRecord task = tasks.get(i);
-            taskIds[i] = task.taskId;
-            taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
-                    : task.realActivity != null ? task.realActivity.flattenToString()
-                    : task.getTopActivity() != null ? task.getTopActivity().packageName
-                    : "unknown";
-            taskBounds[i] = new Rect();
-            task.getWindowContainerBounds(taskBounds[i]);
-            taskUserIds[i] = task.userId;
-        }
-        info.taskIds = taskIds;
-        info.taskNames = taskNames;
-        info.taskBounds = taskBounds;
-        info.taskUserIds = taskUserIds;
-
-        final ActivityRecord top = stack.topRunningActivityLocked();
-        info.topActivity = top != null ? top.intent.getComponent() : null;
-        return info;
-    }
-
-    StackInfo getStackInfo(int stackId) {
-        ActivityStack stack = getStack(stackId);
-        if (stack != null) {
-            return getStackInfo(stack);
-        }
-        return null;
-    }
-
-    StackInfo getStackInfo(int windowingMode, int activityType) {
-        final ActivityStack stack = getStack(windowingMode, activityType);
-        return (stack != null) ? getStackInfo(stack) : null;
-    }
-
-    ArrayList<StackInfo> getAllStackInfosLocked() {
-        ArrayList<StackInfo> list = new ArrayList<>();
-        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                list.add(getStackInfo(stack));
-            }
-        }
-        return list;
-    }
-
     void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
             int preferredDisplayId, ActivityStack actualStack) {
         handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId,
@@ -4623,21 +2445,6 @@
         }
     }
 
-    void setDockedStackMinimized(boolean minimized) {
-        // Get currently focused stack before setting mIsDockMinimized. We do this because if
-        // split-screen is active, primary stack will not be focusable (see #isFocusable) while
-        // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
-        final ActivityStack current = getTopDisplayFocusedStack();
-        mIsDockMinimized = minimized;
-        if (mIsDockMinimized) {
-            if (current.inSplitScreenPrimaryWindowingMode()) {
-                // The primary split-screen stack can't be focused while it is minimize, so move
-                // focus to something else.
-                current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
-            }
-        }
-    }
-
     void wakeUp(String reason) {
         mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.am:TURN_ON:" + reason);
     }
@@ -4656,10 +2463,8 @@
         mDeferResumeCount--;
     }
 
-    /**
-     * @return True if resume can be called.
-     */
-    private boolean readyToResume() {
+    /** @return True if resume can be called. */
+    boolean readyToResume() {
         return mDeferResumeCount == 0;
     }
 
@@ -4711,7 +2516,7 @@
                 } break;
                 case RESUME_TOP_ACTIVITY_MSG: {
                     synchronized (mService.mGlobalLock) {
-                        resumeFocusedStacksTopActivitiesLocked();
+                        mRootActivityContainer.resumeFocusedStacksTopActivities();
                     }
                 } break;
                 case SLEEP_TIMEOUT_MSG: {
@@ -4747,19 +2552,6 @@
         }
     }
 
-    ActivityStack findStackBehind(ActivityStack stack) {
-        final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
-        if (display != null) {
-            for (int i = display.getChildCount() - 1; i >= 0; i--) {
-                if (display.getChildAt(i) == stack && i > 0) {
-                    return display.getChildAt(i - 1);
-                }
-            }
-        }
-        throw new IllegalStateException("Failed to find a stack behind stack=" + stack
-                + " in=" + display);
-    }
-
     /**
      * Puts a task into resizing mode during the next app transition.
      *
@@ -4804,8 +2596,8 @@
                 mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
             }
 
-            task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
-                    activityOptions, ON_TOP);
+            task = mRootActivityContainer.anyTaskForId(taskId,
+                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
             if (task == null) {
                 continueUpdateRecentsHomeStackBounds();
                 mWindowManager.executeAppTransition();
@@ -4818,7 +2610,8 @@
                 // from whatever is started from the recents activity, so move the home stack
                 // forward.
                 // TODO (b/115289124): Multi-display supports for recents.
-                getDefaultDisplay().moveHomeStackToFront("startActivityFromRecents");
+                mRootActivityContainer.getDefaultDisplay().moveHomeStackToFront(
+                        "startActivityFromRecents");
             }
 
             // If the user must confirm credentials (e.g. when first launching a work app and the
@@ -4827,7 +2620,8 @@
                     && task.getRootActivity() != null) {
                 final ActivityRecord targetActivity = task.getTopActivity();
 
-                sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity);
+                mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+                        true /* forceSend */, targetActivity);
                 mActivityMetricsLogger.notifyActivityLaunching(task.intent);
                 try {
                     mService.moveTaskToFrontLocked(task.taskId, 0, options,
@@ -4880,35 +2674,6 @@
     }
 
     /**
-     * @return a list of activities which are the top ones in each visible stack. The first
-     * entry will be the focused activity.
-     */
-    List<IBinder> getTopVisibleActivities() {
-        final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
-        final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
-        // Traverse all displays.
-        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            // Traverse all stacks on a display.
-            for (int j = display.getChildCount() - 1; j >= 0; --j) {
-                final ActivityStack stack = display.getChildAt(j);
-                // Get top activity from a visible stack and add it to the list.
-                if (stack.shouldBeVisible(null /* starting */)) {
-                    final ActivityRecord top = stack.getTopActivity();
-                    if (top != null) {
-                        if (stack == topFocusedStack) {
-                            topActivityTokens.add(0, top.appToken);
-                        } else {
-                            topActivityTokens.add(top.appToken);
-                        }
-                    }
-                }
-            }
-        }
-        return topActivityTokens;
-    }
-
-    /**
      * Internal container to store a match qualifier alongside a WaitResult.
      */
     static class WaitInfo {
@@ -4946,30 +2711,4 @@
             mResult.dump(pw, prefix);
         }
     }
-
-    private final class SleepTokenImpl extends SleepToken {
-        private final String mTag;
-        private final long mAcquireTime;
-        private final int mDisplayId;
-
-        public SleepTokenImpl(String tag, int displayId) {
-            mTag = tag;
-            mDisplayId = displayId;
-            mAcquireTime = SystemClock.uptimeMillis();
-        }
-
-        @Override
-        public void release() {
-            synchronized (mService.mGlobalLock) {
-                removeSleepTokenLocked(this);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "{\"" + mTag + "\", display " + mDisplayId
-                    + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
-        }
-    }
-
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index ee5a43c..54a63a1 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -70,6 +70,7 @@
 
     private final ActivityTaskManagerService mService;
     private final ActivityStackSupervisor mSupervisor;
+    private final RootActivityContainer mRootActivityContainer;
     private final Context mServiceContext;
 
     // UserManager cannot be final as it's not ready when this class is instantiated during boot
@@ -102,14 +103,15 @@
 
     ActivityStartInterceptor(
             ActivityTaskManagerService service, ActivityStackSupervisor supervisor) {
-        this(service, supervisor, service.mContext);
+        this(service, supervisor, service.mRootActivityContainer, service.mContext);
     }
 
     @VisibleForTesting
     ActivityStartInterceptor(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
-            Context context) {
+            RootActivityContainer root, Context context) {
         mService = service;
         mSupervisor = supervisor;
+        mRootActivityContainer = root;
         mServiceContext = context;
     }
 
@@ -279,7 +281,7 @@
             mActivityOptions = ActivityOptions.makeBasic();
         }
 
-        ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity();
+        ActivityRecord homeActivityRecord = mRootActivityContainer.getDefaultDisplayHomeActivity();
         if (homeActivityRecord != null && homeActivityRecord.getTask() != null) {
             // Showing credential confirmation activity in home task to avoid stopping multi-windowed
             // mode after showing the full-screen credential confirmation activity.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d4c1bca..cba1044 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -98,6 +98,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -108,6 +109,7 @@
 import android.util.EventLog;
 import android.util.Pools.SynchronizedPool;
 import android.util.Slog;
+import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
@@ -137,6 +139,7 @@
     private static final int INVALID_LAUNCH_MODE = -1;
 
     private final ActivityTaskManagerService mService;
+    private final RootActivityContainer mRootActivityContainer;
     private final ActivityStackSupervisor mSupervisor;
     private final ActivityStartInterceptor mInterceptor;
     private final ActivityStartController mController;
@@ -421,6 +424,7 @@
             ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
         mController = controller;
         mService = service;
+        mRootActivityContainer = service.mRootActivityContainer;
         mSupervisor = supervisor;
         mInterceptor = interceptor;
         reset(true);
@@ -617,7 +621,7 @@
         ActivityRecord sourceRecord = null;
         ActivityRecord resultRecord = null;
         if (resultTo != null) {
-            sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
+            sourceRecord = mRootActivityContainer.isInAnyStack(resultTo);
             if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
                     "Will send result to " + resultTo + " " + sourceRecord);
             if (sourceRecord != null) {
@@ -731,6 +735,12 @@
         abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
 
+        // not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking
+        // on START_ABORTED
+        if (!abort) {
+            abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, callerApp);
+        }
+
         // Merge the two options bundles, while realCallerOptions takes precedence.
         ActivityOptions checkedOptions = options != null
                 ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
@@ -774,6 +784,8 @@
             // We pretend to the caller that it was really started, but
             // they will just get a cancel result.
             ActivityOptions.abort(checkedOptions);
+            maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp,
+                    null /*r*/, originatingPendingIntent, true /*abortedStart*/);
             return START_ABORTED;
         }
 
@@ -811,7 +823,8 @@
                         null /*profilerInfo*/);
 
                 if (DEBUG_PERMISSIONS_REVIEW) {
-                    final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+                    final ActivityStack focusedStack =
+                            mRootActivityContainer.getTopDisplayFocusedStack();
                     Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
                             true, false) + "} from uid " + callingUid + " on display "
                             + (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId));
@@ -847,7 +860,7 @@
             r.appTimeTracker = sourceRecord.appTimeTracker;
         }
 
-        final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
 
         // If we are starting an activity that is not from the same uid as the currently resumed
         // one, check whether app switches are allowed.
@@ -866,19 +879,54 @@
         mController.doPendingActivityLaunches(false);
 
         maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r,
-                originatingPendingIntent);
+                originatingPendingIntent, false /*abortedStart*/);
 
         return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                 true /* doResume */, checkedOptions, inTask, outActivity);
     }
 
+    private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage,
+            WindowProcessController callerApp) {
+        if (mService.isBackgroundActivityStartsEnabled()) {
+            return false;
+        }
+        // don't abort for the most important UIDs
+        if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) {
+            return false;
+        }
+        // don't abort if the callerApp has any visible activity
+        if (callerApp != null && callerApp.hasForegroundActivities()) {
+            return false;
+        }
+        // don't abort if the callingUid's process is important enough
+        if (mService.getUidStateLocked(callingUid) <= ActivityManager.PROCESS_STATE_TOP) {
+            return false;
+        }
+        // don't abort if the callingUid has any visible window
+        if (mService.mWindowManager.isAnyWindowVisibleForUid(callingUid)) {
+            return false;
+        }
+        // don't abort if the caller has the same uid as the recents component
+        if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
+            return false;
+        }
+        // anything that has fallen through will currently be aborted
+        // TODO: remove this toast after feature development is done
+        mService.mUiHandler.post(() -> {
+            Toast.makeText(mService.mContext,
+                    "Blocking background activity start for " + callingPackage,
+                    Toast.LENGTH_SHORT).show();
+        });
+        return true;
+    }
+
     private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
             Intent intent, WindowProcessController callerApp, ActivityRecord r,
-            PendingIntentRecord originatingPendingIntent) {
+            PendingIntentRecord originatingPendingIntent, boolean abortedStart) {
         boolean callerAppHasForegroundActivity =
                 callerApp != null && callerApp.hasForegroundActivities();
         if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
-                || r == null) {
+                || (!abortedStart && r == null)) {
             // skip logging in this case
             return;
         }
@@ -894,8 +942,8 @@
             final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
                     ? callingUidHasAnyVisibleWindow
                     : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
-            final String targetPackage = r.packageName;
-            final int targetUid = (r.appInfo != null) ? r.appInfo.uid : -1;
+            final String targetPackage = (r != null) ? r.packageName : null;
+            final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1;
             final int targetUidProcState = mService.getUidStateLocked(targetUid);
             final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
                     ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
@@ -1063,7 +1111,7 @@
         ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
 
         synchronized (mService.mGlobalLock) {
-            final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+            final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
             stack.mConfigWillChange = globalConfig != null
                     && mService.getGlobalConfiguration().diff(globalConfig) != 0;
             if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -1249,7 +1297,8 @@
                     final ActivityRecord currentTop =
                             startedActivityStack.topRunningActivityLocked();
                     if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
-                        mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),
+                        mRootActivityContainer.ensureVisibilityAndConfig(
+                                currentTop, currentTop.getDisplayId(),
                                 true /* markFrozenIfConfigChanged */, false /* deferResume */);
                     }
                 }
@@ -1284,7 +1333,7 @@
         // Do not start home activity if it cannot be launched on preferred display. We are not
         // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
         // fallback to launch on other displays.
-        if (r.isActivityTypeHome() && !mSupervisor.canStartHomeOnDisplay(r.info,
+        if (r.isActivityTypeHome() && !mRootActivityContainer.canStartHomeOnDisplay(r.info,
                 mPreferredDisplayId, true /* allowInstrumenting */)) {
             Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
             return START_CANCELED;
@@ -1361,7 +1410,8 @@
                 }
             }
 
-            mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);
+            mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded
+                    (false /* forceSend */, reusedActivity);
 
             reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
 
@@ -1413,7 +1463,7 @@
 
         // If the activity being launched is the same as the one currently at the top, then
         // we need to check if it should only be launched once.
-        final ActivityStack topStack = mSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();
         final ActivityRecord topFocused = topStack.getTopActivity();
         final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
         final boolean dontStart = top != null && mStartActivity.resultTo == null
@@ -1430,7 +1480,7 @@
             // For paranoia, make sure we have correctly resumed the top activity.
             topStack.mLastPausedActivity = null;
             if (mDoResume) {
-                mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
             }
             ActivityOptions.abort(mOptions);
             if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -1485,7 +1535,8 @@
                 EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
         mTargetStack.mLastPausedActivity = null;
 
-        mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
+        mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+                false /* forceSend */, mStartActivity);
 
         mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
                 mOptions);
@@ -1512,16 +1563,16 @@
                 // task stack to be focusable, then ensure that we now update the focused stack
                 // accordingly.
                 if (mTargetStack.isFocusable()
-                        && !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) {
+                        && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
                     mTargetStack.moveToFront("startActivityUnchecked");
                 }
-                mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, mStartActivity,
-                        mOptions);
+                mRootActivityContainer.resumeFocusedStacksTopActivities(
+                        mTargetStack, mStartActivity, mOptions);
             }
         } else if (mStartActivity != null) {
             mSupervisor.mRecentTasks.add(mStartActivity.getTask());
         }
-        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+        mRootActivityContainer.updateUserStack(mStartActivity.userId, mTargetStack);
 
         mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
                 mPreferredDisplayId, mTargetStack);
@@ -1642,7 +1693,7 @@
             if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
                 r.mTaskOverlay = true;
                 if (!mOptions.canTaskOverlayResume()) {
-                    final TaskRecord task = mSupervisor.anyTaskForIdLocked(
+                    final TaskRecord task = mRootActivityContainer.anyTaskForId(
                             mOptions.getLaunchTaskId());
                     final ActivityRecord top = task != null ? task.getTopActivity() : null;
                     if (top != null && !top.isState(RESUMED)) {
@@ -1678,7 +1729,7 @@
         if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
             ActivityRecord checkedCaller = sourceRecord;
             if (checkedCaller == null) {
-                checkedCaller = mSupervisor.getTopDisplayFocusedStack()
+                checkedCaller = mRootActivityContainer.getTopDisplayFocusedStack()
                         .topRunningNonDelayedActivityLocked(mNotTop);
             }
             if (!checkedCaller.realActivity.equals(r.realActivity)) {
@@ -1840,26 +1891,28 @@
         putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
         ActivityRecord intentActivity = null;
         if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
-            final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
+            final TaskRecord task = mRootActivityContainer.anyTaskForId(mOptions.getLaunchTaskId());
             intentActivity = task != null ? task.getTopActivity() : null;
         } else if (putIntoExistingTask) {
             if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
                 // There can be one and only one instance of single instance activity in the
                 // history, and it is always in its own unique task, so we do a special search.
-               intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
+               intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info,
                        mStartActivity.isActivityTypeHome());
             } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
                 // For the launch adjacent case we only want to put the activity in an existing
                 // task if the activity already exists in the history.
-                intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
+                intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info,
                         !(LAUNCH_SINGLE_TASK == mLaunchMode));
             } else {
                 // Otherwise find the best task to put the activity in.
-                intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
+                intentActivity =
+                        mRootActivityContainer.findTask(mStartActivity, mPreferredDisplayId);
             }
         }
 
-        if (mStartActivity.isActivityTypeHome() && intentActivity != null
+        if (intentActivity != null
+                && (mStartActivity.isActivityTypeHome() || intentActivity.isActivityTypeHome())
                 && intentActivity.getDisplayId() != mPreferredDisplayId) {
             // Do not reuse home activity on other displays.
             intentActivity = null;
@@ -2066,11 +2119,11 @@
 
     private void resumeTargetStackIfNeeded() {
         if (mDoResume) {
-            mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, null, mOptions);
+            mRootActivityContainer.resumeFocusedStacksTopActivities(mTargetStack, null, mOptions);
         } else {
             ActivityOptions.abort(mOptions);
         }
-        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+        mRootActivityContainer.updateUserStack(mStartActivity.userId, mTargetStack);
     }
 
     private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
@@ -2144,13 +2197,13 @@
             // be not suitable. Let's check other displays.
             if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
                 // Can't use target display, lets find a stack on the source display.
-                mTargetStack = mSupervisor.getValidLaunchStackOnDisplay(
+                mTargetStack = mRootActivityContainer.getValidLaunchStackOnDisplay(
                         sourceStack.mDisplayId, mStartActivity, mOptions, mLaunchParams);
             }
             if (mTargetStack == null) {
                 // There are no suitable stacks on the target and source display(s). Look on all
                 // displays.
-                mTargetStack = mSupervisor.getNextValidLaunchStackLocked(
+                mTargetStack = mRootActivityContainer.getNextValidLaunchStack(
                         mStartActivity, -1 /* currentFocus */);
             }
         }
@@ -2181,7 +2234,7 @@
                 // For paranoia, make sure we have correctly resumed the top activity.
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
-                    mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
                 ActivityOptions.abort(mOptions);
                 return START_DELIVERED_TO_TOP;
@@ -2199,7 +2252,7 @@
                 deliverNewIntent(top);
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
-                    mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
                 return START_DELIVERED_TO_TOP;
             }
@@ -2253,7 +2306,8 @@
 
         if (!mLaunchParams.mBounds.isEmpty()) {
             // TODO: Shouldn't we already know what stack to use by the time we get here?
-            ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
+            ActivityStack stack = mRootActivityContainer.getLaunchStack(
+                    null, null, mInTask, ON_TOP);
             if (stack != mInTask.getStack()) {
                 mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
                         DEFER_RESUME, "inTaskToFront");
@@ -2347,7 +2401,7 @@
         }
 
         final ActivityStack currentStack = task != null ? task.getStack() : null;
-        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
         if (currentStack != null) {
             if (focusedStack != currentStack) {
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
@@ -2368,18 +2422,18 @@
 
         if (mPreferredDisplayId != DEFAULT_DISPLAY) {
             // Try to put the activity in a stack on a secondary display.
-            stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r, aOptions,
-                    mLaunchParams);
+            stack = mRootActivityContainer.getValidLaunchStackOnDisplay(
+                    mPreferredDisplayId, r, aOptions, mLaunchParams);
             if (stack == null) {
                 // If source display is not suitable - look for topmost valid stack in the system.
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
                         "computeStackFocus: Can't launch on mPreferredDisplayId="
                                 + mPreferredDisplayId + ", looking on all displays.");
-                stack = mSupervisor.getNextValidLaunchStackLocked(r, mPreferredDisplayId);
+                stack = mRootActivityContainer.getNextValidLaunchStack(r, mPreferredDisplayId);
             }
         }
         if (stack == null) {
-            stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
+            stack = mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP);
         }
         if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
                 + r + " stackId=" + stack.mStackId);
@@ -2389,7 +2443,7 @@
     /** Check if provided activity record can launch in currently focused stack. */
     // TODO: This method can probably be consolidated into getLaunchStack() below.
     private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
-        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
         final boolean canUseFocusedStack;
         if (focusedStack.isActivityTypeAssistant()) {
             canUseFocusedStack = r.isActivityTypeAssistant();
@@ -2435,14 +2489,14 @@
             // full resolution.
             mLaunchParams.mPreferredDisplayId =
                     mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY;
-            final ActivityStack stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP,
-                    mLaunchParams);
+            final ActivityStack stack =
+                    mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP, mLaunchParams);
             mLaunchParams.mPreferredDisplayId = mPreferredDisplayId;
             return stack;
         }
         // Otherwise handle adjacent launch.
 
-        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
         // The parent activity doesn't want to launch the activity on top of itself, but
         // instead tries to put it onto other side in side-by-side mode.
         final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
@@ -2460,7 +2514,8 @@
             if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
                 // If parent was in docked stack, the natural place to launch another activity
                 // will be fullscreen, so it can appear alongside the docked window.
-                final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
+                final int activityType =
+                        mRootActivityContainer.resolveActivityType(r, mOptions, task);
                 return parentStack.getDisplay().getOrCreateStack(
                         WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
             } else {
@@ -2468,10 +2523,10 @@
                 // and if yes, we will launch into that stack. If not, we just put the new
                 // activity into parent's stack, because we can't find a better place.
                 final ActivityStack dockedStack =
-                        mSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+                        mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
                 if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
                     // There is a docked stack, but it isn't visible, so we can't launch into that.
-                    return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
+                    return mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP);
                 } else {
                     return dockedStack;
                 }
@@ -2659,7 +2714,7 @@
         prefix = prefix + "  ";
         pw.print(prefix);
         pw.print("mCurrentUser=");
-        pw.println(mSupervisor.mCurrentUser);
+        pw.println(mRootActivityContainer.mCurrentUser);
         pw.print(prefix);
         pw.print("mLastStartReason=");
         pw.println(mLastStartReason);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 4f01d699..8f99dae 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -91,8 +91,6 @@
         .PACKAGE;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
@@ -122,6 +120,8 @@
 import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
 import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
@@ -341,7 +341,8 @@
     ActivityManagerInternal mAmInternal;
     UriGrantsManagerInternal mUgmInternal;
     private PackageManagerInternal mPmInternal;
-    private ActivityTaskManagerInternal mInternal;
+    @VisibleForTesting
+    final ActivityTaskManagerInternal mInternal;
     PowerManagerInternal mPowerManagerInternal;
     private UsageStatsManagerInternal mUsageStatsInternal;
 
@@ -351,6 +352,7 @@
     /* Global service lock used by the package the owns this service. */
     final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
     ActivityStackSupervisor mStackSupervisor;
+    RootActivityContainer mRootActivityContainer;
     WindowManagerService mWindowManager;
     private UserManagerService mUserManager;
     private AppOpsService mAppOpsService;
@@ -643,6 +645,7 @@
         mSystemThread = ActivityThread.currentActivityThread();
         mUiContext = mSystemThread.getSystemUiContext();
         mLifecycleManager = new ClientLifecycleManager();
+        mInternal = new LocalService();
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
     }
 
@@ -764,7 +767,8 @@
         mTempConfig.setLocales(LocaleList.getDefault());
         mConfigurationSeq = mTempConfig.seq = 1;
         mStackSupervisor = createStackSupervisor();
-        mStackSupervisor.onConfigurationChanged(mTempConfig);
+        mRootActivityContainer = new RootActivityContainer(this);
+        mRootActivityContainer.onConfigurationChanged(mTempConfig);
 
         mTaskChangeNotificationController =
                 new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
@@ -799,6 +803,7 @@
             mWindowManager = wm;
             mLockTaskController.setWindowManager(wm);
             mStackSupervisor.setWindowManager(wm);
+            mRootActivityContainer.setWindowManager(wm);
         }
     }
 
@@ -893,7 +898,6 @@
     }
 
     private void start() {
-        mInternal = new LocalService();
         LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
     }
 
@@ -1254,7 +1258,7 @@
                 sourceToken = resultTo;
             }
 
-            sourceRecord = mStackSupervisor.isInAnyStackLocked(sourceToken);
+            sourceRecord = mRootActivityContainer.isInAnyStack(sourceToken);
             if (sourceRecord == null) {
                 throw new SecurityException("Called with bad activity token: " + sourceToken);
             }
@@ -1798,7 +1802,7 @@
                 }
                 final boolean translucentChanged = r.changeWindowTranslucency(true);
                 if (translucentChanged) {
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                    mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                 }
                 mWindowManager.setAppFullscreen(token, true);
                 return translucentChanged;
@@ -1828,7 +1832,7 @@
                 if (translucentChanged) {
                     r.getStack().convertActivityToTranslucent(r);
                 }
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                 mWindowManager.setAppFullscreen(token, false);
                 return translucentChanged;
             }
@@ -1841,7 +1845,7 @@
     public void notifyActivityDrawn(IBinder token) {
         if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
         synchronized (mGlobalLock) {
-            ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
+            ActivityRecord r = mRootActivityContainer.isInAnyStack(token);
             if (r != null) {
                 r.getStack().notifyActivityDrawnLocked(r);
             }
@@ -1878,7 +1882,7 @@
             synchronized (mGlobalLock) {
                 ActivityStack focusedStack = getTopDisplayFocusedStack();
                 if (focusedStack != null) {
-                    return mStackSupervisor.getStackInfo(focusedStack.mStackId);
+                    return mRootActivityContainer.getStackInfo(focusedStack.mStackId);
                 }
                 return null;
             }
@@ -1894,14 +1898,14 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                final ActivityStack stack = mRootActivityContainer.getStack(stackId);
                 if (stack == null) {
                     Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
                     return;
                 }
                 final ActivityRecord r = stack.topRunningActivityLocked();
                 if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
             }
         } finally {
@@ -1916,14 +1920,14 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     return;
                 }
                 final ActivityRecord r = task.topRunningActivityLocked();
                 if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
             }
         } finally {
@@ -2008,7 +2012,7 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
                 if (task != null) {
                     return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
                 }
@@ -2026,7 +2030,7 @@
         Rect rect = new Rect();
         try {
             synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
                 if (task == null) {
                     Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
@@ -2057,7 +2061,7 @@
         synchronized (mGlobalLock) {
             enforceCallerIsRecentsOrHasPermission(
                     MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
-            final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
+            final TaskRecord tr = mRootActivityContainer.anyTaskForId(id,
                     MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
             if (tr != null) {
                 return tr.lastTaskDescription;
@@ -2077,7 +2081,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
@@ -2166,7 +2170,7 @@
         }
         final long origId = Binder.clearCallingIdentity();
         try {
-            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+            final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
             if (task == null) {
                 Slog.d(TAG, "Could not find task for id: "+ taskId);
                 SafeActivityOptions.abort(options);
@@ -2283,7 +2287,7 @@
 
             final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
                     callingUid);
-            mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
+            mRootActivityContainer.getRunningTasks(maxNum, list, ignoreActivityType,
                     ignoreWindowingMode, callingUid, allowed);
         }
 
@@ -2319,7 +2323,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
                 if (task == null) {
                     Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
                     return;
@@ -2328,7 +2332,7 @@
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
                         + " to stackId=" + stackId + " toTop=" + toTop);
 
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                final ActivityStack stack = mRootActivityContainer.getStack(stackId);
                 if (stack == null) {
                     throw new IllegalStateException(
                             "moveTaskToStack: No stack for stackId=" + stackId);
@@ -2358,7 +2362,7 @@
         try {
             synchronized (mGlobalLock) {
                 if (animate) {
-                    final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
+                    final PinnedActivityStack stack = mRootActivityContainer.getStack(stackId);
                     if (stack == null) {
                         Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
                         return;
@@ -2370,12 +2374,12 @@
                     stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
                             animationDuration, false /* fromFullscreen */);
                 } else {
-                    final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                    final ActivityStack stack = mRootActivityContainer.getStack(stackId);
                     if (stack == null) {
                         Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
                         return;
                     }
-                    mStackSupervisor.resizeStackLocked(stack, destBounds,
+                    mRootActivityContainer.resizeStack(stack, destBounds,
                             null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
                             preserveWindows, allowResizeInDockedMode, !DEFER_RESUME);
                 }
@@ -2409,7 +2413,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
@@ -2451,7 +2455,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mStackSupervisor.removeStacksInWindowingModes(windowingModes);
+                mRootActivityContainer.removeStacksInWindowingModes(windowingModes);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2466,7 +2470,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
+                mRootActivityContainer.removeStacksWithActivityTypes(activityTypes);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2497,7 +2501,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.getAllStackInfosLocked();
+                return mRootActivityContainer.getAllStackInfos();
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -2510,7 +2514,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.getStackInfo(windowingMode, activityType);
+                return mRootActivityContainer.getStackInfo(windowingMode, activityType);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -2552,7 +2556,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     return;
@@ -2594,7 +2598,7 @@
             return;
         }
 
-        final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
         if (stack == null || task != stack.topTask()) {
             throw new IllegalArgumentException("Invalid task, not in foreground");
         }
@@ -2609,7 +2613,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             // When a task is locked, dismiss the pinned stack if it exists
-            mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+            mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
 
             getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
         } finally {
@@ -2711,7 +2715,7 @@
             try {
                 // TODO: VI Consider treating local voice interactions and voice tasks
                 // differently here
-                mStackSupervisor.finishVoiceTask(session);
+                mRootActivityContainer.finishVoiceTask(session);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -2901,7 +2905,7 @@
     @Override
     public void setTaskResizeable(int taskId, int resizeableMode) {
         synchronized (mGlobalLock) {
-            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+            final TaskRecord task = mRootActivityContainer.anyTaskForId(
                     taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
             if (task == null) {
                 Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
@@ -2917,7 +2921,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
@@ -2982,7 +2986,7 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 final WindowProcessController app = getProcessController(appInt);
-                mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
+                mRootActivityContainer.releaseSomeActivitiesLocked(app, "low-mem");
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -3076,7 +3080,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                final ActivityStack stack = mRootActivityContainer.getStack(stackId);
                 if (stack == null) {
                     Slog.w(TAG, "removeStack: No stack with id=" + stackId);
                     return;
@@ -3101,7 +3105,7 @@
             try {
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId
                         + " to displayId=" + displayId);
-                mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP);
+                mRootActivityContainer.moveStackToDisplay(stackId, displayId, ON_TOP);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -3563,13 +3567,13 @@
             try {
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task="
                         + taskId + " in stackId=" + stackId + " at position=" + position);
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
                 if (task == null) {
                     throw new IllegalArgumentException("positionTaskInStack: no task for id="
                             + taskId);
                 }
 
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                final ActivityStack stack = mRootActivityContainer.getStack(stackId);
 
                 if (stack == null) {
                     throw new IllegalArgumentException("positionTaskInStack: no stack for id="
@@ -3624,7 +3628,7 @@
         try {
             synchronized (mGlobalLock) {
                 final ActivityStack stack =
-                        mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+                        mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
                 if (stack == null) {
                     Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
                     return;
@@ -3634,7 +3638,7 @@
                     // Caller wants the current split-screen primary stack to be the top stack after
                     // it goes fullscreen, so move it to the front.
                     stack.moveToFront("dismissSplitScreenMode");
-                } else if (mStackSupervisor.isTopDisplayFocusedStack(stack)) {
+                } else if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
                     // In this case the current split-screen primary stack shouldn't be the top
                     // stack after it goes fullscreen, but it current has focus, so we move the
                     // focus to the top-most split-screen secondary stack next to it.
@@ -3665,7 +3669,7 @@
         try {
             synchronized (mGlobalLock) {
                 final PinnedActivityStack stack =
-                        mStackSupervisor.getDefaultDisplay().getPinnedStack();
+                        mRootActivityContainer.getDefaultDisplay().getPinnedStack();
                 if (stack == null) {
                     Slog.w(TAG, "dismissPip: pinned stack not found.");
                     return;
@@ -3707,7 +3711,7 @@
         synchronized (mGlobalLock) {
             final long origId = Binder.clearCallingIdentity();
             try {
-                final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
+                final ActivityStack stack = mRootActivityContainer.getStack(fromStackId);
                 if (stack != null){
                     if (!stack.isActivityTypeStandardOrUndefined()) {
                         throw new IllegalArgumentException(
@@ -3742,7 +3746,7 @@
 
             long ident = Binder.clearCallingIdentity();
             try {
-                return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
+                return mRootActivityContainer.moveTopStackActivityToPinnedStack(stackId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -3820,7 +3824,7 @@
                         // Adjust the source bounds by the insets for the transition down
                         final Rect sourceBounds = new Rect(
                                 r.pictureInPictureArgs.getSourceRectHint());
-                        mStackSupervisor.moveActivityToPinnedStackLocked(
+                        mRootActivityContainer.moveActivityToPinnedStack(
                                 r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
                         final PinnedActivityStack stack = r.getStack();
                         stack.setPictureInPictureAspectRatio(aspectRatio);
@@ -4099,7 +4103,7 @@
 
         synchronized (mGlobalLock) {
             // Check if display is initialized in AM.
-            if (!mStackSupervisor.isDisplayAdded(displayId)) {
+            if (!mRootActivityContainer.isDisplayAdded(displayId)) {
                 // Call might come when display is not yet added or has already been removed.
                 if (DEBUG_CONFIGURATION) {
                     Slog.w(TAG, "Trying to update display configuration for non-existing displayId="
@@ -4189,7 +4193,7 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
@@ -4209,7 +4213,7 @@
         try {
             final TaskRecord task;
             synchronized (mGlobalLock) {
-                task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
                 if (task == null) {
                     Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
@@ -4429,7 +4433,7 @@
         if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
             Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
                     + " to main stack for VR");
-            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+            final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
                     WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
             moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
         }
@@ -4443,7 +4447,7 @@
                 if (disableNonVrUi) {
                     // If we are in a VR mode where Picture-in-Picture mode is unsupported,
                     // then remove the pinned stack.
-                    mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+                    mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
                 }
             }
         });
@@ -4495,7 +4499,7 @@
     }
 
     ActivityStack getTopDisplayFocusedStack() {
-        return mStackSupervisor.getTopDisplayFocusedStack();
+        return mRootActivityContainer.getTopDisplayFocusedStack();
     }
 
     /** Pokes the task persister. */
@@ -4556,12 +4560,12 @@
             int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) {
         pw.println(header);
 
-        boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
+        boolean printedAnything = mRootActivityContainer.dumpActivities(fd, pw, dumpAll, dumpClient,
                 dumpPackage);
         boolean needSep = printedAnything;
 
         boolean printed = ActivityStackSupervisor.printThisActivity(pw,
-                mStackSupervisor.getTopResumedActivity(),  dumpPackage, needSep,
+                mRootActivityContainer.getTopResumedActivity(),  dumpPackage, needSep,
                 "  ResumedActivity: ");
         if (printed) {
             printedAnything = true;
@@ -4583,7 +4587,7 @@
 
     void dumpActivityContainersLocked(PrintWriter pw) {
         pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
-        mStackSupervisor.dumpChildrenNames(pw, " ");
+        mRootActivityContainer.dumpChildrenNames(pw, " ");
         pw.println(" ");
     }
 
@@ -4607,7 +4611,7 @@
         ArrayList<ActivityRecord> activities;
 
         synchronized (mGlobalLock) {
-            activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
+            activities = mRootActivityContainer.getDumpActivities(name, dumpVisibleStacksOnly,
                     dumpFocusedStackOnly);
         }
 
@@ -4682,7 +4686,7 @@
     }
 
     void writeSleepStateToProto(ProtoOutputStream proto) {
-        for (ActivityTaskManagerInternal.SleepToken st : mStackSupervisor.mSleepTokens) {
+        for (ActivityTaskManagerInternal.SleepToken st : mRootActivityContainer.mSleepTokens) {
             proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS,
                     st.toString());
         }
@@ -4727,7 +4731,7 @@
      * also corresponds to the merged configuration of the default display.
      */
     Configuration getGlobalConfiguration() {
-        return mStackSupervisor.getConfiguration();
+        return mRootActivityContainer.getConfiguration();
     }
 
     boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
@@ -4859,7 +4863,7 @@
         mTempConfig.seq = increaseConfigurationSeqLocked();
 
         // Update stored global config and notify everyone about the change.
-        mStackSupervisor.onConfigurationChanged(mTempConfig);
+        mRootActivityContainer.onConfigurationChanged(mTempConfig);
 
         Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
         // TODO(multi-display): Update UsageEvents#Event to include displayId.
@@ -4906,7 +4910,7 @@
 
         // Override configuration of the default display duplicates global config, so we need to
         // update it also. This will also notify WindowManager about changes.
-        performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
+        performDisplayOverrideConfigUpdate(mRootActivityContainer.getConfiguration(), deferResume,
                 DEFAULT_DISPLAY);
 
         return changes;
@@ -4960,12 +4964,12 @@
 
     private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
             int displayId) {
-        mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+        mTempConfig.setTo(mRootActivityContainer.getDisplayOverrideConfiguration(displayId));
         final int changes = mTempConfig.updateFrom(values);
         if (changes != 0) {
             Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
                     + mTempConfig + " for displayId=" + displayId);
-            mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
+            mRootActivityContainer.setDisplayOverrideConfiguration(mTempConfig, displayId);
 
             final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
             if (isDensityChange && displayId == DEFAULT_DISPLAY) {
@@ -5015,6 +5019,10 @@
         return mAmInternal.isActivityStartsLoggingEnabled();
     }
 
+    boolean isBackgroundActivityStartsEnabled() {
+        return mAmInternal.isBackgroundActivityStartsEnabled();
+    }
+
     void enableScreenAfterBoot(boolean booted) {
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
                 SystemClock.uptimeMillis());
@@ -5095,7 +5103,7 @@
                     mCurAppTimeTracker.stop();
                     mH.obtainMessage(
                             REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
-                    mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
+                    mRootActivityContainer.clearOtherAppTimeTrackers(r.appTimeTracker);
                     mCurAppTimeTracker = null;
                 }
                 if (r.appTimeTracker != null) {
@@ -5156,14 +5164,15 @@
 
     ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) {
         synchronized (mGlobalLock) {
-            final ActivityTaskManagerInternal.SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
+            final ActivityTaskManagerInternal.SleepToken token =
+                    mRootActivityContainer.createSleepToken(tag, displayId);
             updateSleepIfNeededLocked();
             return token;
         }
     }
 
     void updateSleepIfNeededLocked() {
-        final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
+        final boolean shouldSleep = !mRootActivityContainer.hasAwakeDisplay();
         final boolean wasSleeping = mSleeping;
         boolean updateOomAdj = false;
 
@@ -5179,7 +5188,7 @@
                 mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
                 mStackSupervisor.comeOutOfSleepIfNeededLocked();
             }
-            mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
+            mRootActivityContainer.applySleepTokens(true /* applyToStacks */);
             if (wasSleeping) {
                 updateOomAdj = true;
             }
@@ -5355,7 +5364,7 @@
 
     // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
     private void startTimeTrackingFocusedActivityLocked() {
-        final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
+        final ActivityRecord resumedActivity = mRootActivityContainer.getTopResumedActivity();
         if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
             mCurAppTimeTracker.start(resumedActivity.packageName);
         }
@@ -5380,7 +5389,7 @@
     /** Applies latest configuration and/or visibility updates if needed. */
     private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
         boolean kept = true;
-        final ActivityStack mainStack = mStackSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack mainStack = mRootActivityContainer.getTopDisplayFocusedStack();
         // mainStack is null during startup.
         if (mainStack != null) {
             if (changes != 0 && starting == null) {
@@ -5395,7 +5404,7 @@
                         false /* preserveWindow */);
                 // And we need to make sure at this point that all other activities
                 // are made visible with the correct configuration.
-                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
+                mRootActivityContainer.ensureActivitiesVisible(starting, changes,
                         !PRESERVE_WINDOWS);
             }
         }
@@ -5611,8 +5620,8 @@
         @Override
         public ComponentName getHomeActivityForUser(int userId) {
             synchronized (mGlobalLock) {
-                ActivityRecord homeActivity =
-                        mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId);
+                final ActivityRecord homeActivity =
+                        mRootActivityContainer.getDefaultDisplayHomeActivityForUser(userId);
                 return homeActivity == null ? null : homeActivity.realActivity;
             }
         }
@@ -5650,14 +5659,14 @@
         @Override
         public List<IBinder> getTopVisibleActivities() {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.getTopVisibleActivities();
+                return mRootActivityContainer.getTopVisibleActivities();
             }
         }
 
         @Override
         public void notifyDockedStackMinimizedChanged(boolean minimized) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.setDockedStackMinimized(minimized);
+                mRootActivityContainer.setDockedStackMinimized(minimized);
             }
         }
 
@@ -5738,7 +5747,7 @@
                 // We might change the visibilities here, so prepare an empty app transition which
                 // might be overridden later if we actually change visibilities.
                 final ActivityDisplay activityDisplay =
-                        mStackSupervisor.getActivityDisplay(displayId);
+                        mRootActivityContainer.getActivityDisplay(displayId);
                 if (activityDisplay == null) {
                     return;
                 }
@@ -5747,7 +5756,7 @@
                 if (!wasTransitionSet) {
                     dwc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
                 }
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                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.
@@ -5764,7 +5773,7 @@
         public void notifyKeyguardTrustedChanged() {
             synchronized (mGlobalLock) {
                 if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                    mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                 }
             }
         }
@@ -5791,7 +5800,7 @@
                             "setFocusedActivity: No activity record matching token=" + token);
                 }
                 if (r.moveFocusableActivityToTop("setFocusedActivity")) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
             }
         }
@@ -5942,7 +5951,7 @@
         public boolean shuttingDown(boolean booted, int timeout) {
             synchronized (mGlobalLock) {
                 mShuttingDown = true;
-                mStackSupervisor.prepareForShutdownLocked();
+                mRootActivityContainer.prepareForShutdown();
                 updateEventDispatchingLocked(booted);
                 notifyTaskPersisterLocked(null, true);
                 return mStackSupervisor.shutdownLocked(timeout);
@@ -6049,7 +6058,7 @@
         @Override
         public void onPackageReplaced(ApplicationInfo aInfo) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+                mRootActivityContainer.updateActivityApplicationInfo(aInfo);
             }
         }
 
@@ -6079,7 +6088,7 @@
             mH.post(() -> {
                 synchronized (mGlobalLock) {
                     final ActivityDisplay activityDisplay =
-                            mStackSupervisor.getActivityDisplay(displayId);
+                            mRootActivityContainer.getActivityDisplay(displayId);
                     if (activityDisplay == null) {
                         // Call might come when display is not yet added or has been removed.
                         if (DEBUG_CONFIGURATION) {
@@ -6162,14 +6171,14 @@
         @Override
         public boolean startHomeActivity(int userId, String reason) {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY);
+                return mRootActivityContainer.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY);
             }
         }
 
         @Override
         public boolean startHomeOnAllDisplays(int userId, String reason) {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.startHomeOnAllDisplays(userId, reason);
+                return mRootActivityContainer.startHomeOnAllDisplays(userId, reason);
             }
         }
 
@@ -6233,7 +6242,7 @@
                 Runnable finishInstrumentationCallback) {
             synchronized (mGlobalLock) {
                 // Remove this application's activities from active lists.
-                boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(wpc);
+                boolean hasVisibleActivities = mRootActivityContainer.handleAppDied(wpc);
 
                 wpc.clearRecentTasks();
                 wpc.clearActivities();
@@ -6245,12 +6254,12 @@
                 mWindowManager.deferSurfaceLayout();
                 try {
                     if (!restarting && hasVisibleActivities
-                            && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
+                            && !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.
-                        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                        mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                     }
                 } finally {
                     mWindowManager.continueSurfaceLayout();
@@ -6279,7 +6288,7 @@
                     }
                     mWindowManager.closeSystemDialogs(reason);
 
-                    mStackSupervisor.closeSystemDialogsLocked();
+                    mRootActivityContainer.closeSystemDialogs();
                 }
                 // Call into AM outside the synchronized block.
                 mAmInternal.broadcastCloseSystemDialogs(reason);
@@ -6293,9 +6302,9 @@
                 String packageName, Set<String> disabledClasses, int userId, boolean booted) {
             synchronized (mGlobalLock) {
                 // Clean-up disabled activities.
-                if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
+                if (mRootActivityContainer.finishDisabledPackageActivities(
                         packageName, disabledClasses, true, false, userId) && booted) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                     mStackSupervisor.scheduleIdleLocked();
                 }
 
@@ -6312,7 +6321,7 @@
 
                 boolean didSomething =
                         getActivityStartController().clearPendingActivityLaunches(packageName);
-                didSomething |= mStackSupervisor.finishDisabledPackageActivitiesLocked(packageName,
+                didSomething |= mRootActivityContainer.finishDisabledPackageActivities(packageName,
                         null, doit, evenPersistent, userId);
                 return didSomething;
             }
@@ -6321,7 +6330,7 @@
         @Override
         public void resumeTopActivities(boolean scheduleIdle) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
                 if (scheduleIdle) {
                     mStackSupervisor.scheduleIdleLocked();
                 }
@@ -6338,7 +6347,7 @@
         @Override
         public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.attachApplicationLocked(wpc);
+                return mRootActivityContainer.attachApplication(wpc);
             }
         }
 
@@ -6360,7 +6369,7 @@
                             // Showing launcher to avoid user entering credential twice.
                             startHomeActivity(currentUserId, "notifyLockedProfile");
                         }
-                        mStackSupervisor.lockAllProfileTasks(userId);
+                        mRootActivityContainer.lockAllProfileTasks(userId);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -6381,7 +6390,7 @@
                     ActivityOptions activityOptions = options != null
                             ? new ActivityOptions(options) : ActivityOptions.makeBasic();
                     final ActivityRecord homeActivity =
-                            mStackSupervisor.getDefaultDisplayHomeActivity();
+                            mRootActivityContainer.getDefaultDisplayHomeActivity();
                     if (homeActivity != null) {
                         activityOptions.setLaunchTaskId(homeActivity.getTask().taskId);
                     }
@@ -6398,7 +6407,7 @@
             synchronized (mGlobalLock) {
                 // The output proto of "activity --proto activities"
                 // is ActivityManagerServiceDumpActivitiesProto
-                mStackSupervisor.writeToProto(proto,
+                mRootActivityContainer.writeToProto(proto,
                         ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
             }
         }
@@ -6493,7 +6502,7 @@
                 }
                 if (dumpPackage == null) {
                     pw.println("  mGlobalConfiguration: " + getGlobalConfiguration());
-                    mStackSupervisor.dumpDisplayConfigs(pw, "  ");
+                    mRootActivityContainer.dumpDisplayConfigs(pw, "  ");
                 }
                 if (dumpAll) {
                     if (dumpPackage == null) {
@@ -6521,7 +6530,7 @@
                 if (dumpPackage == null) {
                     pw.println("  mWakefulness="
                             + PowerManagerInternal.wakefulnessToString(wakefulness));
-                    pw.println("  mSleepTokens=" + mStackSupervisor.mSleepTokens);
+                    pw.println("  mSleepTokens=" + mRootActivityContainer.mSleepTokens);
                     if (mRunningVoice != null) {
                         pw.println("  mRunningVoice=" + mRunningVoice);
                         pw.println("  mVoiceWakeLock" + mVoiceWakeLock);
@@ -6648,14 +6657,14 @@
         @Override
         public boolean canGcNow() {
             synchronized (mGlobalLock) {
-                return isSleeping() || mStackSupervisor.allResumedActivitiesIdle();
+                return isSleeping() || mRootActivityContainer.allResumedActivitiesIdle();
             }
         }
 
         @Override
         public WindowProcessController getTopApp() {
             synchronized (mGlobalLock) {
-                final ActivityRecord top = mStackSupervisor.getTopResumedActivity();
+                final ActivityRecord top = mRootActivityContainer.getTopResumedActivity();
                 return top != null ? top.app : null;
             }
         }
@@ -6663,8 +6672,8 @@
         @Override
         public void rankTaskLayersIfNeeded() {
             synchronized (mGlobalLock) {
-                if (mStackSupervisor != null) {
-                    mStackSupervisor.rankTaskLayersIfNeeded();
+                if (mRootActivityContainer != null) {
+                    mRootActivityContainer.rankTaskLayersIfNeeded();
                 }
             }
         }
@@ -6672,35 +6681,35 @@
         @Override
         public void scheduleDestroyAllActivities(String reason) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.scheduleDestroyAllActivities(null, reason);
+                mRootActivityContainer.scheduleDestroyAllActivities(null, reason);
             }
         }
 
         @Override
         public void removeUser(int userId) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.removeUserLocked(userId);
+                mRootActivityContainer.removeUser(userId);
             }
         }
 
         @Override
         public boolean switchUser(int userId, UserState userState) {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.switchUserLocked(userId, userState);
+                return mRootActivityContainer.switchUser(userId, userState);
             }
         }
 
         @Override
         public void onHandleAppCrash(WindowProcessController wpc) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.handleAppCrashLocked(wpc);
+                mRootActivityContainer.handleAppCrash(wpc);
             }
         }
 
         @Override
         public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.finishTopCrashedActivitiesLocked(crashedApp, reason);
+                return mRootActivityContainer.finishTopCrashedActivities(crashedApp, reason);
             }
         }
 
@@ -6871,4 +6880,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 04fef02..441c593 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -16,8 +16,8 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
 
 import android.app.ActivityManager;
 import android.app.IAppTask;
@@ -77,7 +77,7 @@
         synchronized (mService.mGlobalLock) {
             long origId = Binder.clearCallingIdentity();
             try {
-                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+                TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
                 if (tr == null) {
                     throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
@@ -115,7 +115,7 @@
         TaskRecord tr;
         IApplicationThread appThread;
         synchronized (mService.mGlobalLock) {
-            tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+            tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
                     MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
             if (tr == null) {
                 throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
@@ -143,7 +143,7 @@
         synchronized (mService.mGlobalLock) {
             long origId = Binder.clearCallingIdentity();
             try {
-                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+                TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
                 if (tr == null) {
                     throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 32a6f74..bf00ffb 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -305,7 +305,7 @@
             AppWindowToken wtoken = openingApps.valueAt(i);
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
 
-            if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) {
+            if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) {
                 // This token isn't going to be animating. Add it to the list of tokens to
                 // be notified of app transition complete since the notification will not be
                 // sent be the app window animator.
@@ -341,7 +341,7 @@
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
             // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
             //       animating?
-            wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
+            wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction);
             wtoken.updateReportedVisibilityLocked();
             // Force the allDrawn flag, because we want to start
             // this guy's animations regardless of whether it's
@@ -350,9 +350,8 @@
             wtoken.deferClearAllDrawn = false;
             // Ensure that apps that are mid-starting are also scheduled to have their
             // starting windows removed after the animation is complete
-            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
-                    && wtoken.getController() != null) {
-                wtoken.getController().removeStartingWindow();
+            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) {
+                wtoken.removeStartingWindow();
             }
 
             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
deleted file mode 100644
index bd1460a..0000000
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ /dev/null
@@ -1,914 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
-import static android.app.ActivityOptions.ANIM_CUSTOM;
-import static android.app.ActivityOptions.ANIM_NONE;
-import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
-import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
-import static android.app.ActivityOptions.ANIM_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_UNSET;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.app.ActivityManager.TaskSnapshot;
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.graphics.GraphicBuffer;
-import android.graphics.Rect;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Slog;
-import android.view.AppTransitionAnimationSpec;
-import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.IApplicationToken;
-import android.view.RemoteAnimationDefinition;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.AttributeCache;
-import com.android.server.policy.WindowManagerPolicy.StartingSurface;
-
-/**
- * Controller for the app window token container. This is created by activity manager to link
- * activity records to the app window token container they use in window manager.
- *
- * Test class: {@link AppWindowContainerControllerTests}
- */
-public class AppWindowContainerController
-        extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
-
-    private static final int STARTING_WINDOW_TYPE_NONE = 0;
-    private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
-    private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
-
-    private final IApplicationToken mToken;
-    private final Handler mHandler;
-
-    private final class H extends Handler {
-        public static final int NOTIFY_WINDOWS_DRAWN = 1;
-        public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2;
-        public static final int NOTIFY_WINDOWS_NOTDRAWN = 3;
-
-        public H(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case NOTIFY_WINDOWS_DRAWN:
-                    if (mListener == null) {
-                        return;
-                    }
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
-                            + AppWindowContainerController.this.mToken);
-                    mListener.onWindowsDrawn(true /* drawn */, msg.getWhen());
-                    break;
-                case NOTIFY_STARTING_WINDOW_DRAWN:
-                    if (mListener == null) {
-                        return;
-                    }
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting starting window drawn in "
-                            + AppWindowContainerController.this.mToken);
-                    mListener.onStartingWindowDrawn(msg.getWhen());
-                    break;
-                case NOTIFY_WINDOWS_NOTDRAWN:
-                    if (mListener == null) {
-                        return;
-                    }
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting not drawn in "
-                            + AppWindowContainerController.this.mToken);
-                    mListener.onWindowsDrawn(false /* drawn */, msg.getWhen());
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    private final Runnable mOnWindowsVisible = () -> {
-        if (mListener == null) {
-            return;
-        }
-        if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting visible in "
-                + AppWindowContainerController.this.mToken);
-        mListener.onWindowsVisible();
-    };
-
-    private final Runnable mOnWindowsGone = () -> {
-        if (mListener == null) {
-            return;
-        }
-        if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in "
-                + AppWindowContainerController.this.mToken);
-        mListener.onWindowsGone();
-    };
-
-    private final Runnable mAddStartingWindow = new Runnable() {
-
-        @Override
-        public void run() {
-            final StartingData startingData;
-            final AppWindowToken container;
-
-            synchronized (mGlobalLock) {
-                if (mContainer == null) {
-                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
-                            + " add starting window");
-                    return;
-                }
-
-                // There can only be one adding request, silly caller!
-                mService.mAnimationHandler.removeCallbacks(this);
-
-                startingData = mContainer.startingData;
-                container = mContainer;
-            }
-
-            if (startingData == null) {
-                // Animation has been canceled... do nothing.
-                if (DEBUG_STARTING_WINDOW)
-                    Slog.v(TAG_WM, "startingData was nulled out before handling"
-                            + " mAddStartingWindow: " + mContainer);
-                return;
-            }
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
-                    + AppWindowContainerController.this + ": startingData="
-                    + container.startingData);
-
-            StartingSurface surface = null;
-            try {
-                surface = startingData.createStartingSurface(container);
-            } catch (Exception e) {
-                Slog.w(TAG_WM, "Exception when adding starting window", e);
-            }
-            if (surface != null) {
-                boolean abort = false;
-                synchronized (mGlobalLock) {
-                    // If the window was successfully added, then
-                    // we need to remove it.
-                    if (container.removed || container.startingData == null) {
-                        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                                "Aborted starting " + container
-                                        + ": removed=" + container.removed
-                                        + " startingData=" + container.startingData);
-                        container.startingWindow = null;
-                        container.startingData = null;
-                        abort = true;
-                    } else {
-                        container.startingSurface = surface;
-                    }
-                    if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
-                            "Added starting " + mContainer
-                                    + ": startingWindow="
-                                    + container.startingWindow + " startingView="
-                                    + container.startingSurface);
-                }
-                if (abort) {
-                    surface.remove();
-                }
-            } else if (DEBUG_STARTING_WINDOW) {
-                Slog.v(TAG_WM, "Surface returned was null: " + mContainer);
-            }
-        }
-    };
-
-    public AppWindowContainerController(TaskWindowContainerController taskController,
-            IApplicationToken token, ComponentName activityComponent,
-            AppWindowContainerListener listener, int index, int requestedOrientation,
-            boolean fullscreen, boolean showForAllUsers, int configChanges,
-            boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
-            int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos) {
-        this(taskController, token, activityComponent, listener, index, requestedOrientation,
-                fullscreen, showForAllUsers, configChanges, voiceInteraction, launchTaskBehind,
-                alwaysFocusable, targetSdkVersion, rotationAnimationHint,
-                inputDispatchingTimeoutNanos, WindowManagerService.getInstance());
-    }
-
-    public AppWindowContainerController(TaskWindowContainerController taskController,
-            IApplicationToken token, ComponentName activityComponent,
-            AppWindowContainerListener listener, int index, int requestedOrientation,
-            boolean fullscreen, boolean showForAllUsers, int configChanges,
-            boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
-            int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
-            WindowManagerService service) {
-        super(listener, service);
-        mHandler = new H(service.mH.getLooper());
-        mToken = token;
-        synchronized (mGlobalLock) {
-            AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
-            if (atoken != null) {
-                // TODO: Should this throw an exception instead?
-                Slog.w(TAG_WM, "Attempted to add existing app token: " + mToken);
-                return;
-            }
-
-            final Task task = taskController.mContainer;
-            if (task == null) {
-                throw new IllegalArgumentException("AppWindowContainerController: invalid "
-                        + " controller=" + taskController);
-            }
-
-            atoken = createAppWindow(mService, token, activityComponent, voiceInteraction,
-                    task.getDisplayContent(), inputDispatchingTimeoutNanos, fullscreen,
-                    showForAllUsers, targetSdkVersion, requestedOrientation, rotationAnimationHint,
-                    configChanges, launchTaskBehind, alwaysFocusable, this);
-            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
-                    + " controller=" + taskController + " at " + index);
-            task.addChild(atoken, index);
-        }
-    }
-
-    @VisibleForTesting
-    AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
-            ComponentName component, boolean voiceInteraction, DisplayContent dc,
-            long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
-            int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
-            boolean launchTaskBehind, boolean alwaysFocusable,
-            AppWindowContainerController controller) {
-        return new AppWindowToken(service, token, component, voiceInteraction, dc,
-                inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
-                rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
-                controller);
-    }
-
-    public void removeContainer(int displayId) {
-        synchronized (mGlobalLock) {
-            final DisplayContent dc = mRoot.getDisplayContent(displayId);
-            if (dc == null) {
-                Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: "
-                        + mToken + " from non-existing displayId=" + displayId);
-                return;
-            }
-            dc.removeAppToken(mToken.asBinder());
-            super.removeContainer();
-        }
-    }
-
-    @Override
-    public void removeContainer() {
-        throw new UnsupportedOperationException("Use removeContainer(displayId) instead.");
-    }
-
-    public void reparent(TaskWindowContainerController taskController, int position) {
-        synchronized (mGlobalLock) {
-            if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken
-                    + " to task=" + taskController + " at " + position);
-            if (mContainer == null) {
-                if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM,
-                        "reparent: could not find app token=" + mToken);
-                return;
-            }
-            final Task task = taskController.mContainer;
-            if (task == null) {
-                throw new IllegalArgumentException("reparent: could not find task="
-                        + taskController);
-            }
-            mContainer.reparent(task, position);
-            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-        }
-    }
-
-    public Configuration setOrientation(int requestedOrientation, int displayId,
-            Configuration displayConfig, boolean freezeScreenIfNeeded) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM,
-                        "Attempted to set orientation of non-existing app token: " + mToken);
-                return null;
-            }
-
-            mContainer.setOrientation(requestedOrientation);
-
-            final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null;
-            return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId);
-
-        }
-    }
-
-    public int getOrientation() {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                return SCREEN_ORIENTATION_UNSPECIFIED;
-            }
-
-            return mContainer.getOrientationIgnoreVisibility();
-        }
-    }
-
-    public void setDisablePreviewScreenshots(boolean disable) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
-                        + " token: " + mToken);
-                return;
-            }
-            mContainer.setDisablePreviewScreenshots(disable);
-        }
-    }
-
-    public void setVisibility(boolean visible, boolean deferHidingClient) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
-                        + mToken);
-                return;
-            }
-
-            final AppWindowToken wtoken = mContainer;
-            final AppTransition appTransition = mContainer.getDisplayContent().mAppTransition;
-
-            // Don't set visibility to false if we were already not visible. This prevents WM from
-            // adding the app to the closing app list which doesn't make sense for something that is
-            // already not visible. However, set visibility to true even if we are already visible.
-            // This makes sure the app is added to the opening apps list so that the right
-            // transition can be selected.
-            // TODO: Probably a good idea to separate the concept of opening/closing apps from the
-            // concept of setting visibility...
-            if (!visible && wtoken.hiddenRequested) {
-
-                if (!deferHidingClient && wtoken.mDeferHidingClient) {
-                    // We previously deferred telling the client to hide itself when visibility was
-                    // initially set to false. Now we would like it to hide, so go ahead and set it.
-                    wtoken.mDeferHidingClient = deferHidingClient;
-                    wtoken.setClientHidden(true);
-                }
-                return;
-            }
-
-            if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility("
-                    + mToken + ", visible=" + visible + "): " + appTransition
-                    + " hidden=" + wtoken.isHidden() + " hiddenRequested="
-                    + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
-
-            final DisplayContent displayContent = mContainer.getDisplayContent();
-            displayContent.mOpeningApps.remove(wtoken);
-            displayContent.mClosingApps.remove(wtoken);
-            wtoken.waitingToShow = false;
-            wtoken.hiddenRequested = !visible;
-            wtoken.mDeferHidingClient = deferHidingClient;
-
-            if (!visible) {
-                // If the app is dead while it was visible, we kept its dead window on screen.
-                // Now that the app is going invisible, we can remove it. It will be restarted
-                // if made visible again.
-                wtoken.removeDeadWindows();
-            } else {
-                if (!appTransition.isTransitionSet()
-                        && appTransition.isReady()) {
-                    // Add the app mOpeningApps if transition is unset but ready. This means
-                    // we're doing a screen freeze, and the unfreeze will wait for all opening
-                    // apps to be ready.
-                    displayContent.mOpeningApps.add(wtoken);
-                }
-                wtoken.startingMoved = false;
-                // If the token is currently hidden (should be the common case), or has been
-                // stopped, then we need to set up to wait for its windows to be ready.
-                if (wtoken.isHidden() || wtoken.mAppStopped) {
-                    wtoken.clearAllDrawn();
-
-                    // If the app was already visible, don't reset the waitingToShow state.
-                    if (wtoken.isHidden()) {
-                        wtoken.waitingToShow = true;
-                    }
-                }
-
-                // In the case where we are making an app visible but holding off for a transition,
-                // we still need to tell the client to make its windows visible so they get drawn.
-                // Otherwise, we will wait on performing the transition until all windows have been
-                // drawn, they never will be, and we are sad.
-                wtoken.setClientHidden(false);
-
-                wtoken.requestUpdateWallpaperIfNeeded();
-
-                if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken);
-                wtoken.mAppStopped = false;
-
-                mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
-            }
-
-            // If we are preparing an app transition, then delay changing
-            // the visibility of this token until we execute that transition.
-            if (wtoken.okToAnimate() && appTransition.isTransitionSet()) {
-                wtoken.inPendingTransaction = true;
-                if (visible) {
-                    displayContent.mOpeningApps.add(wtoken);
-                    wtoken.mEnteringAnimation = true;
-                } else {
-                    displayContent.mClosingApps.add(wtoken);
-                    wtoken.mEnteringAnimation = false;
-                }
-                if (appTransition.getAppTransition()
-                        == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
-                    // We're launchingBehind, add the launching activity to mOpeningApps.
-                    final WindowState win = mContainer.getDisplayContent().findFocusedWindow();
-                    if (win != null) {
-                        final AppWindowToken focusedToken = win.mAppToken;
-                        if (focusedToken != null) {
-                            if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
-                                    + " adding " + focusedToken + " to mOpeningApps");
-                            // Force animation to be loaded.
-                            focusedToken.setHidden(true);
-                            displayContent.mOpeningApps.add(focusedToken);
-                        }
-                    }
-                }
-                return;
-            }
-
-            wtoken.setVisibility(null, visible, TRANSIT_UNSET, true, wtoken.mVoiceInteraction);
-            wtoken.updateReportedVisibilityLocked();
-        }
-    }
-
-    /**
-     * Notifies that we launched an app that might be visible or not visible depending on what kind
-     * of Keyguard flags it's going to set on its windows.
-     */
-    public void notifyUnknownVisibilityLaunched() {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.getDisplayContent().mUnknownAppVisibilityController.notifyLaunched(
-                        mContainer);
-            }
-        }
-    }
-
-    public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
-            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
-            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
-            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
-        synchronized (mGlobalLock) {
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken
-                    + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
-                    + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
-                    + " allowTaskSnapshot=" + allowTaskSnapshot);
-
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + mToken);
-                return false;
-            }
-
-            // If the display is frozen, we won't do anything until the actual window is
-            // displayed so there is no reason to put in the starting window.
-            if (!mContainer.okToDisplay()) {
-                return false;
-            }
-
-            if (mContainer.startingData != null) {
-                return false;
-            }
-
-            final WindowState mainWin = mContainer.findMainWindow();
-            if (mainWin != null && mainWin.mWinAnimator.getShown()) {
-                // App already has a visible window...why would you want a starting window?
-                return false;
-            }
-
-            final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
-                    mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
-                    false /* restoreFromDisk */, false /* reducedResolution */);
-            final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
-                    allowTaskSnapshot, activityCreated, fromRecents, snapshot);
-
-            if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
-                return createSnapshot(snapshot);
-            }
-
-            // If this is a translucent window, then don't show a starting window -- the current
-            // effect (a full-screen opaque starting window that fades away to the real contents
-            // when it is ready) does not work for this.
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Checking theme of starting window: 0x"
-                    + Integer.toHexString(theme));
-            if (theme != 0) {
-                AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
-                        com.android.internal.R.styleable.Window, mService.mCurrentUserId);
-                if (ent == null) {
-                    // Whoops!  App doesn't exist. Um. Okay. We'll just pretend like we didn't
-                    // see that.
-                    return false;
-                }
-                final boolean windowIsTranslucent = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowIsTranslucent, false);
-                final boolean windowIsFloating = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowIsFloating, false);
-                final boolean windowShowWallpaper = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowShowWallpaper, false);
-                final boolean windowDisableStarting = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowDisablePreview, false);
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Translucent=" + windowIsTranslucent
-                        + " Floating=" + windowIsFloating
-                        + " ShowWallpaper=" + windowShowWallpaper);
-                if (windowIsTranslucent) {
-                    return false;
-                }
-                if (windowIsFloating || windowDisableStarting) {
-                    return false;
-                }
-                if (windowShowWallpaper) {
-                    if (mContainer.getDisplayContent().mWallpaperController.getWallpaperTarget()
-                            == null) {
-                        // If this theme is requesting a wallpaper, and the wallpaper
-                        // is not currently visible, then this effectively serves as
-                        // an opaque window and our starting window transition animation
-                        // can still work.  We just need to make sure the starting window
-                        // is also showing the wallpaper.
-                        windowFlags |= FLAG_SHOW_WALLPAPER;
-                    } else {
-                        return false;
-                    }
-                }
-            }
-
-            if (mContainer.transferStartingWindow(transferFrom)) {
-                return true;
-            }
-
-            // There is no existing starting window, and we don't want to create a splash screen, so
-            // that's it!
-            if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
-                return false;
-            }
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
-            mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme,
-                    compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
-                    mContainer.getMergedOverrideConfiguration());
-            scheduleAddStartingWindow();
-        }
-        return true;
-    }
-
-    private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
-            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
-            TaskSnapshot snapshot) {
-        if (mContainer.getDisplayContent().mAppTransition.getAppTransition()
-                == TRANSIT_DOCK_TASK_FROM_RECENTS) {
-            // TODO(b/34099271): Remove this statement to add back the starting window and figure
-            // out why it causes flickering, the starting window appears over the thumbnail while
-            // the docked from recents transition occurs
-            return STARTING_WINDOW_TYPE_NONE;
-        } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
-            return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
-        } else if (taskSwitch && allowTaskSnapshot) {
-            return snapshot == null ? STARTING_WINDOW_TYPE_NONE
-                    : snapshotOrientationSameAsTask(snapshot) || fromRecents
-                            ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
-        } else {
-            return STARTING_WINDOW_TYPE_NONE;
-        }
-    }
-
-    void scheduleAddStartingWindow() {
-        // Note: we really want to do sendMessageAtFrontOfQueue() because we
-        // want to process the message ASAP, before any other queued
-        // messages.
-        if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
-            mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
-        }
-    }
-
-    private boolean createSnapshot(TaskSnapshot snapshot) {
-        if (snapshot == null) {
-            return false;
-        }
-
-        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
-        mContainer.startingData = new SnapshotStartingData(mService, snapshot);
-        scheduleAddStartingWindow();
-        return true;
-    }
-
-    private boolean snapshotOrientationSameAsTask(TaskSnapshot snapshot) {
-        if (snapshot == null) {
-            return false;
-        }
-        return mContainer.getTask().getConfiguration().orientation == snapshot.getOrientation();
-    }
-
-    public void removeStartingWindow() {
-        synchronized (mGlobalLock) {
-            if (mContainer.startingWindow == null) {
-                if (mContainer.startingData != null) {
-                    // Starting window has not been added yet, but it is scheduled to be added.
-                    // Go ahead and cancel the request.
-                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                            "Clearing startingData for token=" + mContainer);
-                    mContainer.startingData = null;
-                }
-                return;
-            }
-
-            final StartingSurface surface;
-            if (mContainer.startingData != null) {
-                surface = mContainer.startingSurface;
-                mContainer.startingData = null;
-                mContainer.startingSurface = null;
-                mContainer.startingWindow = null;
-                mContainer.startingDisplayed = false;
-                if (surface == null) {
-                    if (DEBUG_STARTING_WINDOW) {
-                        Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
-                                + "remove");
-                    }
-                    return;
-                }
-            } else {
-                if (DEBUG_STARTING_WINDOW) {
-                    Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
-                            + mContainer);
-                }
-                return;
-            }
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Schedule remove starting " + mContainer
-                    + " startingWindow=" + mContainer.startingWindow
-                    + " startingView=" + mContainer.startingSurface
-                    + " Callers=" + Debug.getCallers(5));
-
-            // Use the same thread to remove the window as we used to add it, as otherwise we end up
-            // with things in the view hierarchy being called from different threads.
-            mService.mAnimationHandler.post(() -> {
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface);
-                try {
-                    surface.remove();
-                } catch (Exception e) {
-                    Slog.w(TAG_WM, "Exception when removing starting window", e);
-                }
-            });
-        }
-    }
-
-    public void pauseKeyDispatching() {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.getDisplayContent().getInputMonitor().pauseDispatchingLw(mContainer);
-            }
-        }
-    }
-
-    public void resumeKeyDispatching() {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.getDisplayContent().getInputMonitor().resumeDispatchingLw(mContainer);
-            }
-        }
-    }
-
-    public void notifyAppResumed(boolean wasStopped) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + mToken);
-                return;
-            }
-            mContainer.notifyAppResumed(wasStopped);
-        }
-    }
-
-    public void notifyAppStopping() {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
-                        + mToken);
-                return;
-            }
-            mContainer.detachChildren();
-        }
-    }
-
-    public void notifyAppStopped() {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: "
-                        + mToken);
-                return;
-            }
-            mContainer.notifyAppStopped();
-        }
-    }
-
-    public void startFreezingScreen(int configChanges) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM,
-                        "Attempted to freeze screen with non-existing app token: " + mContainer);
-                return;
-            }
-
-            if (configChanges == 0 && mContainer.okToDisplay()) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
-                return;
-            }
-
-            mContainer.startFreezingScreen();
-        }
-    }
-
-    public void stopFreezingScreen(boolean force) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                return;
-            }
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden="
-                    + mContainer.isHidden() + " freezing=" + mContainer.isFreezingScreen());
-            mContainer.stopFreezingScreen(true, force);
-        }
-    }
-
-    public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
-                        + " token: " + mToken);
-                return;
-            }
-            mContainer.registerRemoteAnimations(definition);
-        }
-    }
-
-    void reportStartingWindowDrawn() {
-        mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN));
-    }
-
-    void reportWindowsDrawn() {
-        mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN));
-    }
-
-    void reportWindowsNotDrawn() {
-        mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_NOTDRAWN));
-    }
-
-    void reportWindowsVisible() {
-        mHandler.post(mOnWindowsVisible);
-    }
-
-    void reportWindowsGone() {
-        mHandler.post(mOnWindowsGone);
-    }
-
-    /** Calls directly into activity manager so window manager lock shouldn't held. */
-    boolean keyDispatchingTimedOut(String reason, int windowPid) {
-        return mListener != null && mListener.keyDispatchingTimedOut(reason, windowPid);
-    }
-
-    /**
-     * Apply override app transition base on options & animation type.
-     */
-    public void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
-        synchronized (mGlobalLock) {
-            final int animationType = pendingOptions.getAnimationType();
-            final DisplayContent displayContent = mContainer.getDisplayContent();
-            switch (animationType) {
-                case ANIM_CUSTOM:
-                    displayContent.mAppTransition.overridePendingAppTransition(
-                            pendingOptions.getPackageName(),
-                            pendingOptions.getCustomEnterResId(),
-                            pendingOptions.getCustomExitResId(),
-                            pendingOptions.getOnAnimationStartListener());
-                    break;
-                case ANIM_CLIP_REVEAL:
-                    displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getWidth(), pendingOptions.getHeight());
-                    if (intent.getSourceBounds() == null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX() + pendingOptions.getWidth(),
-                                pendingOptions.getStartY() + pendingOptions.getHeight()));
-                    }
-                    break;
-                case ANIM_SCALE_UP:
-                    displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getWidth(), pendingOptions.getHeight());
-                    if (intent.getSourceBounds() == null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX() + pendingOptions.getWidth(),
-                                pendingOptions.getStartY() + pendingOptions.getHeight()));
-                    }
-                    break;
-                case ANIM_THUMBNAIL_SCALE_UP:
-                case ANIM_THUMBNAIL_SCALE_DOWN:
-                    final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
-                    final GraphicBuffer buffer = pendingOptions.getThumbnail();
-                    displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getOnAnimationStartListener(),
-                            scaleUp);
-                    if (intent.getSourceBounds() == null && buffer != null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX() + buffer.getWidth(),
-                                pendingOptions.getStartY() + buffer.getHeight()));
-                    }
-                    break;
-                case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
-                case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
-                    final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
-                    final IAppTransitionAnimationSpecsFuture specsFuture =
-                            pendingOptions.getSpecsFuture();
-                    if (specsFuture != null) {
-                        // TODO(multidisplay): Shouldn't be really used anymore from next CL.
-                        displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
-                                specsFuture, pendingOptions.getOnAnimationStartListener(),
-                                animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
-                    } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
-                            && specs != null) {
-                        displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
-                                specs, pendingOptions.getOnAnimationStartListener(),
-                                pendingOptions.getAnimationFinishedListener(), false);
-                    } else {
-                        displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
-                                pendingOptions.getThumbnail(),
-                                pendingOptions.getStartX(), pendingOptions.getStartY(),
-                                pendingOptions.getWidth(), pendingOptions.getHeight(),
-                                pendingOptions.getOnAnimationStartListener(),
-                                (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
-                        if (intent.getSourceBounds() == null) {
-                            intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                    pendingOptions.getStartY(),
-                                    pendingOptions.getStartX() + pendingOptions.getWidth(),
-                                    pendingOptions.getStartY() + pendingOptions.getHeight()));
-                        }
-                    }
-                    break;
-                case ANIM_OPEN_CROSS_PROFILE_APPS:
-                    displayContent.mAppTransition
-                            .overridePendingAppTransitionStartCrossProfileApps();
-                    break;
-                case ANIM_REMOTE_ANIMATION:
-                    // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL.
-                    displayContent.mAppTransition.overridePendingAppTransitionRemote(
-                            pendingOptions.getRemoteAnimationAdapter());
-                    break;
-                case ANIM_NONE:
-                    break;
-                default:
-                    Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
-     * This information helps AWT know that the app is in the process of pausing before it gets the
-     * signal on the WM side.
-     */
-    public void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                return;
-            }
-
-            mContainer.setWillCloseOrEnterPip(willCloseOrEnterPip);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "AppWindowContainerController{"
-                + " token=" + mToken
-                + " mContainer=" + mContainer
-                + " mListener=" + mListener
-                + "}";
-    }
-}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
deleted file mode 100644
index ad27669..0000000
--- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-/** Interface used by the creator of the controller to listen to changes with the container. */
-public interface AppWindowContainerListener extends WindowContainerListener {
-    /** Called when the windows associated app window container drawn state changes. */
-    void onWindowsDrawn(boolean drawn, long timestamp);
-    /** Called when the windows associated app window container are visible. */
-    void onWindowsVisible();
-    /** Called when the windows associated app window container are no longer visible. */
-    void onWindowsGone();
-
-    /**
-     * Called when the starting window for this container is drawn.
-     */
-    void onStartingWindowDrawn(long timestamp);
-
-    /**
-     * Called when the key dispatching to a window associated with the app window container
-     * timed-out.
-     *
-     * @param reason The reason for the key dispatching time out.
-     * @param windowPid The pid of the window key dispatching timed out on.
-     * @return True if input dispatching should be aborted.
-     */
-    boolean keyDispatchingTimedOut(String reason, int windowPid);
-}
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 729f89b..b9b9d31 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -16,13 +16,13 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT;
+import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR;
+import static com.android.server.wm.AppWindowThumbnailProto.WIDTH;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
-import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT;
-import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR;
-import static com.android.server.wm.AppWindowThumbnailProto.WIDTH;
 
 import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
@@ -65,7 +65,7 @@
         // this to the task.
         mSurfaceControl = appToken.makeSurface()
                 .setName("thumbnail anim: " + appToken.toString())
-                .setSize(mWidth, mHeight)
+                .setBufferSize(mWidth, mHeight)
                 .setFormat(PixelFormat.TRANSLUCENT)
                 .setMetadata(appToken.windowType,
                         window != null ? window.mOwnerUid : Binder.getCallingUid())
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 92944a0..df81c07 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -28,10 +28,12 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 import static android.view.WindowManager.TRANSIT_UNSET;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
 
@@ -81,7 +83,9 @@
 
 import android.annotation.CallSuper;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.content.ComponentName;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.GraphicBuffer;
 import android.graphics.Point;
@@ -90,6 +94,7 @@
 import android.os.Debug;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -106,6 +111,8 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
+import com.android.server.AttributeCache;
+import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.policy.WindowManagerPolicy.StartingSurface;
 import com.android.server.wm.WindowManagerService.H;
 
@@ -121,7 +128,8 @@
  * Version of WindowToken that is specifically for a particular application (or
  * really activity) that is displaying windows.
  */
-class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener {
+class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener,
+        ConfigurationContainerListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowToken" : TAG_WM;
 
     /**
@@ -226,6 +234,9 @@
 
     private Task mLastParent;
 
+    // TODO: Remove after unification
+    ActivityRecord mActivityRecord;
+
     /**
      * See {@link #canTurnScreenOn()}
      */
@@ -273,14 +284,20 @@
     /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */
     boolean mNeedsAnimationBoundsLayer;
 
+    private static final int STARTING_WINDOW_TYPE_NONE = 0;
+    private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
+    private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
+
     AppWindowToken(WindowManagerService service, IApplicationToken token,
             ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
             long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
             int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
             boolean launchTaskBehind, boolean alwaysFocusable,
-            AppWindowContainerController controller) {
+            ActivityRecord activityRecord) {
         this(service, token, activityComponent, voiceInteraction, dc, fullscreen);
-        setController(controller);
+        // TODO: remove after unification
+        mActivityRecord = activityRecord;
+        mActivityRecord.registerConfigurationChangeListener(this);
         mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
         mShowForAllUsers = showForAllUsers;
         mTargetSdk = targetSdk;
@@ -304,7 +321,7 @@
         mActivityComponent = activityComponent;
         mVoiceInteraction = voiceInteraction;
         mFillsParent = fillsParent;
-        mInputApplicationHandle = new InputApplicationHandle(this);
+        mInputApplicationHandle = new InputApplicationHandle(appToken.asBinder());
     }
 
     void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
@@ -320,9 +337,7 @@
             // it from behind the starting window, so there is no need for it to also be doing its
             // own stuff.
             win.cancelAnimation();
-            if (getController() != null) {
-                getController().removeStartingWindow();
-            }
+            removeStartingWindow();
         }
         updateReportedVisibilityLocked();
     }
@@ -360,16 +375,9 @@
         }
         if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
                 + numInteresting + " visible=" + numVisible);
-        final AppWindowContainerController controller = getController();
         if (nowDrawn != reportedDrawn) {
-            if (nowDrawn) {
-                if (controller != null) {
-                    controller.reportWindowsDrawn();
-                }
-            } else {
-                if (controller != null) {
-                    controller.reportWindowsNotDrawn();
-                }
+            if (mActivityRecord != null) {
+                mActivityRecord.onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis());
             }
             reportedDrawn = nowDrawn;
         }
@@ -377,16 +385,36 @@
             if (DEBUG_VISIBILITY) Slog.v(TAG,
                     "Visibility changed in " + this + ": vis=" + nowVisible);
             reportedVisible = nowVisible;
-            if (controller != null) {
+            if (mActivityRecord != null) {
                 if (nowVisible) {
-                    controller.reportWindowsVisible();
+                    onWindowsVisible();
                 } else {
-                    controller.reportWindowsGone();
+                    onWindowsGone();
                 }
             }
         }
     }
 
+    private void onWindowsGone() {
+        if (mActivityRecord == null) {
+            return;
+        }
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG_WM, "Reporting gone in " + mActivityRecord.appToken);
+        }
+        mActivityRecord.onWindowsGone();
+    }
+
+    private void onWindowsVisible() {
+        if (mActivityRecord == null) {
+            return;
+        }
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG_WM, "Reporting visible in " + mActivityRecord.appToken);
+        }
+        mActivityRecord.onWindowsVisible();
+    }
+
     boolean isClientHidden() {
         return mClientHidden;
     }
@@ -401,7 +429,116 @@
         sendAppVisibilityToClients();
     }
 
-    boolean setVisibility(WindowManager.LayoutParams lp,
+    void setVisibility(boolean visible, boolean deferHidingClient) {
+        final AppTransition appTransition = getDisplayContent().mAppTransition;
+
+        // Don't set visibility to false if we were already not visible. This prevents WM from
+        // adding the app to the closing app list which doesn't make sense for something that is
+        // already not visible. However, set visibility to true even if we are already visible.
+        // This makes sure the app is added to the opening apps list so that the right
+        // transition can be selected.
+        // TODO: Probably a good idea to separate the concept of opening/closing apps from the
+        // concept of setting visibility...
+        if (!visible && hiddenRequested) {
+
+            if (!deferHidingClient && mDeferHidingClient) {
+                // We previously deferred telling the client to hide itself when visibility was
+                // initially set to false. Now we would like it to hide, so go ahead and set it.
+                mDeferHidingClient = deferHidingClient;
+                setClientHidden(true);
+            }
+            return;
+        }
+
+        if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
+            Slog.v(TAG_WM, "setAppVisibility("
+                    + appToken + ", visible=" + visible + "): " + appTransition
+                    + " hidden=" + isHidden() + " hiddenRequested="
+                    + hiddenRequested + " Callers=" + Debug.getCallers(6));
+        }
+
+        final DisplayContent displayContent = getDisplayContent();
+        displayContent.mOpeningApps.remove(this);
+        displayContent.mClosingApps.remove(this);
+        waitingToShow = false;
+        hiddenRequested = !visible;
+        mDeferHidingClient = deferHidingClient;
+
+        if (!visible) {
+            // If the app is dead while it was visible, we kept its dead window on screen.
+            // Now that the app is going invisible, we can remove it. It will be restarted
+            // if made visible again.
+            removeDeadWindows();
+        } else {
+            if (!appTransition.isTransitionSet()
+                    && appTransition.isReady()) {
+                // Add the app mOpeningApps if transition is unset but ready. This means
+                // we're doing a screen freeze, and the unfreeze will wait for all opening
+                // apps to be ready.
+                displayContent.mOpeningApps.add(this);
+            }
+            startingMoved = false;
+            // If the token is currently hidden (should be the common case), or has been
+            // stopped, then we need to set up to wait for its windows to be ready.
+            if (isHidden() || mAppStopped) {
+                clearAllDrawn();
+
+                // If the app was already visible, don't reset the waitingToShow state.
+                if (isHidden()) {
+                    waitingToShow = true;
+                }
+            }
+
+            // In the case where we are making an app visible but holding off for a transition,
+            // we still need to tell the client to make its windows visible so they get drawn.
+            // Otherwise, we will wait on performing the transition until all windows have been
+            // drawn, they never will be, and we are sad.
+            setClientHidden(false);
+
+            requestUpdateWallpaperIfNeeded();
+
+            if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + this);
+            mAppStopped = false;
+
+            transferStartingWindowFromHiddenAboveTokenIfNeeded();
+        }
+
+        // If we are preparing an app transition, then delay changing
+        // the visibility of this token until we execute that transition.
+        if (okToAnimate() && appTransition.isTransitionSet()) {
+            inPendingTransaction = true;
+            if (visible) {
+                displayContent.mOpeningApps.add(this);
+                mEnteringAnimation = true;
+            } else {
+                displayContent.mClosingApps.add(this);
+                mEnteringAnimation = false;
+            }
+            if (appTransition.getAppTransition()
+                    == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
+                // We're launchingBehind, add the launching activity to mOpeningApps.
+                final WindowState win = getDisplayContent().findFocusedWindow();
+                if (win != null) {
+                    final AppWindowToken focusedToken = win.mAppToken;
+                    if (focusedToken != null) {
+                        if (DEBUG_APP_TRANSITIONS) {
+                            Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
+                                    + " adding " + focusedToken + " to mOpeningApps");
+                        }
+                        // Force animation to be loaded.
+                        focusedToken.setHidden(true);
+                        displayContent.mOpeningApps.add(focusedToken);
+                    }
+                }
+            }
+            return;
+        }
+
+        commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction);
+        updateReportedVisibilityLocked();
+    }
+
+    boolean commitVisibility(WindowManager.LayoutParams lp,
             boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
 
         boolean delayed = false;
@@ -461,8 +598,10 @@
                 forAllWindows(mService::makeWindowFreezingScreenIfNeededLocked, true);
             }
 
-            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setVisibility: " + this
-                    + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
+            if (DEBUG_APP_TRANSITIONS) {
+                Slog.v(TAG_WM, "commitVisibility: " + this
+                        + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
+            }
 
             if (changed) {
                 getDisplayContent().getInputMonitor().setUpdateInputWindowsNeededLw();
@@ -499,10 +638,9 @@
                 mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
             }
 
-            // If we're becoming visible, immediately change client visibility as well although it
-            // usually gets changed in AppWindowContainerController.setVisibility already. However,
-            // there seem to be some edge cases where we change our visibility but client visibility
-            // never gets updated.
+            // If we're becoming visible, immediately change client visibility as well. there seem
+            // to be some edge cases where we change our visibility but client visibility never gets
+            // updated.
             // If we're becoming invisible, update the client visibility if we are not running an
             // animation. Otherwise, we'll update client visibility in onAnimationFinished.
             if (visible || !isReallyAnimating()) {
@@ -596,11 +734,6 @@
         return getWindowConfiguration().canReceiveKeys() || mAlwaysFocusable;
     }
 
-    AppWindowContainerController getController() {
-        final WindowContainerController controller = super.getController();
-        return controller != null ? (AppWindowContainerController) controller : null;
-    }
-
     @Override
     boolean isVisible() {
         // If the app token isn't hidden then it is considered visible and there is no need to check
@@ -637,7 +770,7 @@
 
         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this);
 
-        boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
+        boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
 
         getDisplayContent().mOpeningApps.remove(this);
         getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
@@ -656,8 +789,8 @@
         if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
                 + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
 
-        if (startingData != null && getController() != null) {
-            getController().removeStartingWindow();
+        if (startingData != null) {
+            removeStartingWindow();
         }
 
         // If this window was animating, then we need to ensure that the app transition notifies
@@ -768,9 +901,7 @@
         mAppStopped = true;
         destroySurfaces();
         // Remove any starting window that was added for this app if they are still around.
-        if (getController() != null) {
-            getController().removeStartingWindow();
-        }
+        removeStartingWindow();
     }
 
     void clearAllDrawn() {
@@ -826,9 +957,7 @@
         // TODO: Something smells about the code below...Is there a better way?
         if (startingWindow == win) {
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
-            if (getController() != null) {
-                getController().removeStartingWindow();
-            }
+            removeStartingWindow();
         } else if (mChildren.size() == 0) {
             // If this is the last window and we had requested a starting transition window,
             // well there is no point now.
@@ -845,9 +974,7 @@
             // we need to get rid of the starting transition.
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Last window, removing starting window "
                     + win);
-            if (getController() != null) {
-                getController().removeStartingWindow();
-            }
+            removeStartingWindow();
         }
     }
 
@@ -1021,6 +1148,10 @@
 
     @Override
     void removeChild(WindowState child) {
+        if (!mChildren.contains(child)) {
+            // This can be true when testing.
+            return;
+        }
         super.removeChild(child);
         checkKeyguardFlagsChanged();
         updateLetterboxSurface(child);
@@ -1042,6 +1173,20 @@
         }
     }
 
+    void reparent(TaskWindowContainerController taskController, int position) {
+        if (DEBUG_ADD_REMOVE) {
+            Slog.i(TAG_WM, "reparent: moving app token=" + this
+                    + " to task=" + taskController + " at " + position);
+        }
+        final Task task = taskController.mContainer;
+        if (task == null) {
+            throw new IllegalArgumentException("reparent: could not find task="
+                    + taskController);
+        }
+        reparent(task, position);
+        getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+    }
+
     void reparent(Task task, int position) {
         final Task currentTask = getTask();
         if (task == currentTask) {
@@ -1081,8 +1226,12 @@
         super.onDisplayChanged(dc);
         if (prevDc != null && prevDc.mFocusedApp == this) {
             prevDc.setFocusedApp(null);
-            if (dc.getTopStack().getTopChild().getTopChild() == this) {
-                dc.setFocusedApp(this);
+            final TaskStack stack = dc.getTopStack();
+            if (stack != null) {
+                final Task task = stack.getTopChild();
+                if (task != null && task.getTopChild() == this) {
+                    dc.setFocusedApp(this);
+                }
             }
         }
     }
@@ -1296,9 +1445,7 @@
             startingData = fromToken.startingData;
             fromToken.startingData = null;
             fromToken.startingMoved = true;
-            if (getController() != null) {
-                getController().scheduleAddStartingWindow();
-            }
+            scheduleAddStartingWindow();
             return true;
         }
 
@@ -1467,6 +1614,10 @@
         }
     }
 
+    boolean keyDispatchingTimedOut(String reason, int windowPid) {
+        return mActivityRecord != null && mActivityRecord.keyDispatchingTimedOut(reason, windowPid);
+    }
+
     /**
      * Updated this app token tracking states for interesting and drawn windows based on the window.
      *
@@ -1529,8 +1680,8 @@
                     }
                 }
             } else if (w.isDrawnLw()) {
-                if (getController() != null) {
-                    getController().reportStartingWindowDrawn();
+                if (mActivityRecord != null) {
+                    mActivityRecord.onStartingWindowDrawn(SystemClock.uptimeMillis());
                 }
                 startingDisplayed = true;
             }
@@ -1597,6 +1748,266 @@
         return this;
     }
 
+    boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
+            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+        // If the display is frozen, we won't do anything until the actual window is
+        // displayed so there is no reason to put in the starting window.
+        if (!okToDisplay()) {
+            return false;
+        }
+
+        if (startingData != null) {
+            return false;
+        }
+
+        final WindowState mainWin = findMainWindow();
+        if (mainWin != null && mainWin.mWinAnimator.getShown()) {
+            // App already has a visible window...why would you want a starting window?
+            return false;
+        }
+
+        final ActivityManager.TaskSnapshot snapshot =
+                mService.mTaskSnapshotController.getSnapshot(
+                        getTask().mTaskId, getTask().mUserId,
+                        false /* restoreFromDisk */, false /* reducedResolution */);
+        final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
+                allowTaskSnapshot, activityCreated, fromRecents, snapshot);
+
+        if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
+            return createSnapshot(snapshot);
+        }
+
+        // If this is a translucent window, then don't show a starting window -- the current
+        // effect (a full-screen opaque starting window that fades away to the real contents
+        // when it is ready) does not work for this.
+        if (DEBUG_STARTING_WINDOW) {
+            Slog.v(TAG, "Checking theme of starting window: 0x" + Integer.toHexString(theme));
+        }
+        if (theme != 0) {
+            AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
+                    com.android.internal.R.styleable.Window,
+                    mService.mCurrentUserId);
+            if (ent == null) {
+                // Whoops!  App doesn't exist. Um. Okay. We'll just pretend like we didn't
+                // see that.
+                return false;
+            }
+            final boolean windowIsTranslucent = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowIsTranslucent, false);
+            final boolean windowIsFloating = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowIsFloating, false);
+            final boolean windowShowWallpaper = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowShowWallpaper, false);
+            final boolean windowDisableStarting = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowDisablePreview, false);
+            if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG, "Translucent=" + windowIsTranslucent
+                        + " Floating=" + windowIsFloating
+                        + " ShowWallpaper=" + windowShowWallpaper);
+            }
+            if (windowIsTranslucent) {
+                return false;
+            }
+            if (windowIsFloating || windowDisableStarting) {
+                return false;
+            }
+            if (windowShowWallpaper) {
+                if (getDisplayContent().mWallpaperController
+                        .getWallpaperTarget() == null) {
+                    // If this theme is requesting a wallpaper, and the wallpaper
+                    // is not currently visible, then this effectively serves as
+                    // an opaque window and our starting window transition animation
+                    // can still work.  We just need to make sure the starting window
+                    // is also showing the wallpaper.
+                    windowFlags |= FLAG_SHOW_WALLPAPER;
+                } else {
+                    return false;
+                }
+            }
+
+            if (transferStartingWindow(transferFrom)) {
+                return true;
+            }
+
+            // There is no existing starting window, and we don't want to create a splash screen, so
+            // that's it!
+            if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+                return false;
+            }
+
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
+            startingData = new SplashScreenStartingData(mService, pkg,
+                    theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+                    getMergedOverrideConfiguration());
+            scheduleAddStartingWindow();
+        }
+        return true;
+    }
+
+
+    private boolean createSnapshot(ActivityManager.TaskSnapshot snapshot) {
+        if (snapshot == null) {
+            return false;
+        }
+
+        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
+        startingData = new SnapshotStartingData(mService, snapshot);
+        scheduleAddStartingWindow();
+        return true;
+    }
+
+    void scheduleAddStartingWindow() {
+        // Note: we really want to do sendMessageAtFrontOfQueue() because we
+        // want to process the message ASAP, before any other queued
+        // messages.
+        if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING");
+            mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+        }
+    }
+
+    private final Runnable mAddStartingWindow = new Runnable() {
+
+        @Override
+        public void run() {
+            synchronized (mService.mGlobalLock) {
+                // There can only be one adding request, silly caller!
+                mService.mAnimationHandler.removeCallbacks(this);
+            }
+
+            if (startingData == null) {
+                // Animation has been canceled... do nothing.
+                if (DEBUG_STARTING_WINDOW) {
+                    Slog.v(TAG, "startingData was nulled out before handling"
+                            + " mAddStartingWindow: " + AppWindowToken.this);
+                }
+                return;
+            }
+
+            if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG, "Add starting " + this + ": startingData=" + startingData);
+            }
+
+            WindowManagerPolicy.StartingSurface surface = null;
+            try {
+                surface = startingData.createStartingSurface(AppWindowToken.this);
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception when adding starting window", e);
+            }
+            if (surface != null) {
+                boolean abort = false;
+                synchronized (mService.mGlobalLock) {
+                    // If the window was successfully added, then
+                    // we need to remove it.
+                    if (removed || startingData == null) {
+                        if (DEBUG_STARTING_WINDOW) {
+                            Slog.v(TAG, "Aborted starting " + AppWindowToken.this
+                                    + ": removed=" + removed + " startingData=" + startingData);
+                        }
+                        startingWindow = null;
+                        startingData = null;
+                        abort = true;
+                    } else {
+                        startingSurface = surface;
+                    }
+                    if (DEBUG_STARTING_WINDOW && !abort) {
+                        Slog.v(TAG, "Added starting " + AppWindowToken.this + ": startingWindow="
+                                + startingWindow + " startingView=" + startingSurface);
+                    }
+                }
+                if (abort) {
+                    surface.remove();
+                }
+            } else if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG, "Surface returned was null: " + AppWindowToken.this);
+            }
+        }
+    };
+
+    private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
+            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
+            ActivityManager.TaskSnapshot snapshot) {
+        if (getDisplayContent().mAppTransition.getAppTransition()
+                == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+            // TODO(b/34099271): Remove this statement to add back the starting window and figure
+            // out why it causes flickering, the starting window appears over the thumbnail while
+            // the docked from recents transition occurs
+            return STARTING_WINDOW_TYPE_NONE;
+        } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+            return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+        } else if (taskSwitch && allowTaskSnapshot) {
+            return snapshot == null ? STARTING_WINDOW_TYPE_NONE
+                    : snapshotOrientationSameAsTask(snapshot) || fromRecents
+                            ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+        } else {
+            return STARTING_WINDOW_TYPE_NONE;
+        }
+    }
+
+
+    private boolean snapshotOrientationSameAsTask(ActivityManager.TaskSnapshot snapshot) {
+        if (snapshot == null) {
+            return false;
+        }
+        return getTask().getConfiguration().orientation == snapshot.getOrientation();
+    }
+
+    void removeStartingWindow() {
+        if (startingWindow == null) {
+            if (startingData != null) {
+                // Starting window has not been added yet, but it is scheduled to be added.
+                // Go ahead and cancel the request.
+                if (DEBUG_STARTING_WINDOW) {
+                    Slog.v(TAG_WM, "Clearing startingData for token=" + this);
+                }
+                startingData = null;
+            }
+            return;
+        }
+
+        final WindowManagerPolicy.StartingSurface surface;
+        if (startingData != null) {
+            surface = startingSurface;
+            startingData = null;
+            startingSurface = null;
+            startingWindow = null;
+            startingDisplayed = false;
+            if (surface == null) {
+                if (DEBUG_STARTING_WINDOW) {
+                    Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
+                            + "remove");
+                }
+                return;
+            }
+        } else {
+            if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
+                        + this);
+            }
+            return;
+        }
+
+        if (DEBUG_STARTING_WINDOW) {
+            Slog.v(TAG_WM, "Schedule remove starting " + this
+                    + " startingWindow=" + startingWindow
+                    + " startingView=" + startingSurface
+                    + " Callers=" + Debug.getCallers(5));
+        }
+
+        // Use the same thread to remove the window as we used to add it, as otherwise we end up
+        // with things in the view hierarchy being called from different threads.
+        mService.mAnimationHandler.post(() -> {
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface);
+            try {
+                surface.remove();
+            } catch (Exception e) {
+                Slog.w(TAG_WM, "Exception when removing starting window", e);
+            }
+        });
+    }
+
     @Override
     boolean fillsParent() {
         return mFillsParent;
@@ -1750,10 +2161,8 @@
         if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.i(TAG, "Creating animation bounds layer");
         final SurfaceControl.Builder builder = makeAnimationLeash()
                 .setParent(getAnimationLeashParent())
-                .setName(getSurfaceControl() + " - animation-bounds")
-                .setSize(getSurfaceWidth(), getSurfaceHeight());
+                .setName(getSurfaceControl() + " - animation-bounds");
         final SurfaceControl boundsLayer = builder.build();
-        t.setWindowCrop(boundsLayer, getSurfaceWidth(), getSurfaceHeight());
         t.show(boundsLayer);
         return boundsLayer;
     }
@@ -2211,9 +2620,6 @@
         if (mPendingRelaunchCount != 0) {
             pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
         }
-        if (getController() != null) {
-            pw.print(prefix); pw.print("controller="); pw.println(getController());
-        }
         if (mRemovingFromDisplay) {
             pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
         }
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 9633864..c90f5bf 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -48,7 +48,6 @@
 
             surface = dc.makeOverlay()
                     .setName("BlackSurface")
-                    .setSize(w, h)
                     .setColorLayer(true)
                     .setParent(null) // TODO: Work-around for b/69259549
                     .build();
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 2a216ab..c3d6211 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -69,7 +69,7 @@
         try {
             ctrl = dc.makeOverlay()
                     .setName("CircularDisplayMask")
-                    .setSize(mScreenSize.x, mScreenSize.y) // not a typo
+                    .setBufferSize(mScreenSize.x, mScreenSize.y) // not a typo
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
 
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index cc14afc..fa3c7ca 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -308,7 +308,6 @@
             return false;
         } else {
             // TODO: Once we use geometry from hierarchy this falls away.
-            t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
             t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
             t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
             if (!mDimState.isVisible) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 886b2ff..c0e9836 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -34,6 +34,7 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.View.GONE;
+import static android.view.InsetsState.TYPE_IME;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_TOP;
@@ -78,7 +79,6 @@
 import static com.android.server.wm.DisplayContentProto.ROTATION;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
 import static com.android.server.wm.DisplayContentProto.STACKS;
-import static com.android.server.wm.DisplayContentProto.SURFACE_SIZE;
 import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -124,6 +124,7 @@
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -151,6 +152,7 @@
 import android.view.Gravity;
 import android.view.InputChannel;
 import android.view.InputDevice;
+import android.view.InsetsState.InternalInsetType;
 import android.view.MagnificationSpec;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -162,6 +164,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
+import com.android.internal.util.function.TriConsumer;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.utils.DisplayRotationUtil;
 import com.android.server.wm.utils.RotationCache;
@@ -352,6 +355,14 @@
     int pendingLayoutChanges;
     int mDeferredRotationPauseCount;
 
+    /**
+     * Used to gate application window layout until we have sent the complete configuration.
+     * TODO: There are still scenarios where we may be out of sync with the client. Ideally
+     *       we want to replace this flag with a mechanism that will confirm the configuration
+     *       applied by the client is the one expected by the system server.
+     */
+    boolean mWaitingForConfig;
+
     // TODO(multi-display): remove some of the usages.
     @VisibleForTesting
     boolean isDefaultDisplay;
@@ -462,14 +473,6 @@
     private SurfaceControl mWindowingLayer;
 
     /**
-     * Specifies the size of the surfaces in {@link #mOverlayLayer} and {@link #mWindowingLayer}.
-     * <p>
-     * For these surfaces currently we use a surface based on the larger of width or height so we
-     * don't have to resize when rotating the display.
-     */
-    private int mSurfaceSize;
-
-    /**
      * Sequence number for the current layout pass.
      */
     int mLayoutSeq = 0;
@@ -507,6 +510,8 @@
 
     private final PointerEventDispatcher mPointerEventDispatcher;
 
+    private final InsetsStateController mInsetsStateController;
+
     // Last systemUiVisibility we received from status bar.
     private int mLastStatusBarVisibility = 0;
     // Last systemUiVisibility we dispatched to windows.
@@ -876,18 +881,7 @@
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
 
-        // We use this as our arbitrary surface size for buffer-less parents
-        // that don't impose cropping on their children. It may need to be larger
-        // than the display size because fullscreen windows can be shifted offscreen
-        // due to surfaceInsets. 2 times the largest display dimension feels like an
-        // appropriately arbitrary number. Eventually we would like to give SurfaceFlinger
-        // layers the ability to match their parent sizes and be able to skip
-        // such arbitrary size settings.
-        mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth) * 2;
-
-        final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
-                .setSize(mSurfaceSize, mSurfaceSize)
-                .setOpaque(true);
+        final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession).setOpaque(true);
         mWindowingLayer = b.setName("Display Root").build();
         mOverlayLayer = b.setName("Display Overlays").build();
 
@@ -914,6 +908,7 @@
 
         mService.mAnimator.addDisplayLocked(mDisplayId);
         mInputMonitor = new InputMonitor(service, mDisplayId);
+        mInsetsStateController = new InsetsStateController(this);
     }
 
     boolean isReady() {
@@ -1050,6 +1045,23 @@
         return mDisplayRotation;
     }
 
+    /**
+     * Marks a window as providing insets for the rest of the windows in the system.
+     *
+     * @param type The type of inset this window provides.
+     * @param win The window.
+     * @param frameProvider Function to compute the frame, or {@code null} if the just the frame of
+     *                      the window should be taken.
+     */
+    void setInsetProvider(@InternalInsetType int type, WindowState win,
+            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+        mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider);
+    }
+
+    InsetsStateController getInsetsStateController() {
+        return mInsetsStateController;
+    }
+
     @VisibleForTesting
     void setDisplayRotation(DisplayRotation displayRotation) {
         mDisplayRotation = displayRotation;
@@ -1284,7 +1296,7 @@
                 + (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
 
         if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
-            mService.mWaitingForConfig = true;
+            mWaitingForConfig = true;
         }
 
         mRotation = rotation;
@@ -2392,8 +2404,8 @@
             mDisplayReady = false;
             mRemovingDisplay = false;
         }
+
         mDisplayPolicy.onDisplayRemoved();
-        mInputMonitor.onRemoved();
         mService.mWindowPlacerLocked.requestTraversal();
     }
 
@@ -2604,7 +2616,6 @@
         }
         mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
         mAppTransition.writeToProto(proto, APP_TRANSITION);
-        proto.write(SURFACE_SIZE, mSurfaceSize);
         if (mFocusedApp != null) {
             mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
         }
@@ -2725,6 +2736,8 @@
         mDisplayRotation.dump(prefix, pw);
         pw.println();
         mInputMonitor.dump(pw, "  ");
+        pw.println();
+        mInsetsStateController.dump(prefix, pw);
     }
 
     @Override
@@ -3007,6 +3020,8 @@
                     mInputMethodWindow.getDisplayId());
         }
         computeImeTarget(true /* updateImeTarget */);
+        mInsetsStateController.getSourceProvider(TYPE_IME).setWindow(win,
+                null /* frameProvider */);
     }
 
     /**
@@ -3462,6 +3477,7 @@
             pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
             if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
                     "after finishPostLayoutPolicyLw", pendingLayoutChanges);
+                mInsetsStateController.onPostLayout();
         } while (pendingLayoutChanges != 0);
 
         mTmpApplySurfaceChangesTransactionState.reset();
@@ -3529,10 +3545,6 @@
         }
     }
 
-    int getSurfaceSize() {
-        return mSurfaceSize;
-    }
-
     void performLayout(boolean initial, boolean updateInputWindows) {
         if (!isLayoutNeeded()) {
             return;
@@ -3675,6 +3687,7 @@
             }
             mTmpWindow = w;
             w.setDisplayLayoutNeeded();
+            w.finishSeamlessRotation(true /* timeout */);
             mService.markForSeamlessRotation(w, false);
         }, true /* traverseTopToBottom */);
 
@@ -4474,8 +4487,6 @@
     SurfaceControl.Builder makeChildSurface(WindowContainer child) {
         SurfaceSession s = child != null ? child.getSession() : getSession();
         final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s);
-        b.setSize(mSurfaceSize, mSurfaceSize);
-
         if (child == null) {
             return b;
         }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c16f95e..0e5947a 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -128,6 +129,7 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
+import android.view.InsetsState;
 import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.view.Surface;
@@ -804,6 +806,11 @@
                 if (mDisplayContent.isDefaultDisplay) {
                     mService.mPolicy.setKeyguardCandidateLw(win);
                 }
+                mDisplayContent.setInsetProvider(TYPE_TOP_BAR, win,
+                        (displayFrames, windowState, rect) -> {
+                            rect.top = 0;
+                            rect.bottom = getStatusBarHeight(displayFrames);
+                        });
                 break;
             case TYPE_NAVIGATION_BAR:
                 mContext.enforceCallingOrSelfPermission(
@@ -818,6 +825,8 @@
                 mNavigationBarController.setWindow(win);
                 mNavigationBarController.setOnBarVisibilityChangedListener(
                         mNavBarVisibilityListener, true);
+                mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR,
+                        win, null /* frameProvider */);
                 if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
                 break;
             case TYPE_NAVIGATION_BAR_PANEL:
@@ -845,9 +854,11 @@
             if (mDisplayContent.isDefaultDisplay) {
                 mService.mPolicy.setKeyguardCandidateLw(null);
             }
+            mDisplayContent.setInsetProvider(TYPE_TOP_BAR, null, null);
         } else if (mNavigationBar == win) {
             mNavigationBar = null;
             mNavigationBarController.setWindow(null);
+            mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR, null, null);
         }
         if (mLastFocusedWindow == win) {
             mLastFocusedWindow = null;
@@ -855,6 +866,11 @@
         mScreenDecorWindows.remove(win);
     }
 
+    private int getStatusBarHeight(DisplayFrames displayFrames) {
+        return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation],
+                displayFrames.mDisplayCutoutSafe.top);
+    }
+
     /**
      * Control the animation to run when a window's state changes.  Return a
      * non-0 number to force the animation to a specific resource ID, or 0
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 7ed078a..e3ddadc 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -74,8 +74,15 @@
         return mDragState != null;
     }
 
-    InputWindowHandle getInputWindowHandleLocked() {
-        return mDragState.getInputWindowHandle();
+    void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+        mDragState.showInputSurface(t, displayId);
+    }
+
+    void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+        if (mDragState != null) {
+            // TODO: Are we guaranteed to get here?
+            mDragState.hideInputSurface(t, displayId);
+        }
     }
 
     void registerCallback(IDragDropCallback callback) {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index a379266..8f6ed85 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -32,9 +32,11 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Context;
+import android.graphics.Rect;
 import android.graphics.Point;
 import android.hardware.input.InputManager;
 import android.os.Build;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
@@ -118,6 +120,11 @@
     private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
     private Point mDisplaySize = new Point();
 
+    // A surface used to catch input events for the drag-and-drop operation.
+    SurfaceControl mInputSurface;
+
+    private final Rect mTmpClipRect = new Rect();
+
     DragState(WindowManagerService service, DragDropController controller, IBinder token,
             SurfaceControl surface, int flags, IBinder localWin) {
         mService = service;
@@ -127,6 +134,42 @@
         mFlags = flags;
         mLocalWin = localWin;
         mNotifiedWindows = new ArrayList<WindowState>();
+
+    }
+
+    void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+        if (displayId != mDisplayContent.getDisplayId()) {
+            return;
+        }
+
+        if (mInputSurface != null) {
+            t.hide(mInputSurface);
+        }
+    }
+
+    void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+        if (displayId != mDisplayContent.getDisplayId()) {
+            return;
+        }
+
+        if (mInputSurface == null) {
+            mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId)
+                    .getSession()).setContainerLayer(true)
+                    .setName("Drag and Drop Input Consumer").build();
+        }
+        final InputWindowHandle h = getInputWindowHandle();
+        if (h == null) {
+            Slog.w(TAG_WM, "Drag is in progress but there is no "
+                    + "drag window handle.");
+            return;
+        }
+
+        t.show(mInputSurface);
+        t.setInputWindowInfo(mInputSurface, h);
+        t.setLayer(mInputSurface, Integer.MAX_VALUE);
+
+        mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
+        t.setWindowCrop(mInputSurface, mTmpClipRect);
     }
 
     /**
@@ -218,7 +261,7 @@
             mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
                     mService.mH.getLooper(), mDragDropController);
 
-            mDragApplicationHandle = new InputApplicationHandle(null);
+            mDragApplicationHandle = new InputApplicationHandle(new Binder());
             mDragApplicationHandle.name = "drag";
             mDragApplicationHandle.dispatchingTimeoutNanos =
                     WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
@@ -226,7 +269,7 @@
             mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
                     display.getDisplayId());
             mDragWindowHandle.name = "drag";
-            mDragWindowHandle.inputChannel = mServerChannel;
+            mDragWindowHandle.token = mServerChannel.getToken();
             mDragWindowHandle.layer = getDragLayerLocked();
             mDragWindowHandle.layoutParamsFlags = 0;
             mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index fddf6ca..7cb4a43 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -32,7 +31,6 @@
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 class EmulatorDisplayOverlay {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "EmulatorDisplayOverlay" : TAG_WM;
@@ -59,7 +57,7 @@
         try {
             ctrl = dc.makeOverlay()
                     .setName("EmulatorDisplayOverlay")
-                    .setSize(mScreenSize.x, mScreenSize.y)
+                    .setBufferSize(mScreenSize.x, mScreenSize.y)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
             ctrl.setLayer(zOrder);
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 49bedc9..4df5a0b 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import android.graphics.Rect;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
@@ -25,6 +27,8 @@
 
 import android.view.InputApplicationHandle;
 import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
+import android.util.Slog;
 
 import java.io.PrintWriter;
 
@@ -39,6 +43,9 @@
     final int mClientPid;
     final UserHandle mClientUser;
 
+    final SurfaceControl mInputSurface;
+    Rect mTmpClipRect = new Rect();
+
     InputConsumerImpl(WindowManagerService service, IBinder token, String name,
             InputChannel inputChannel, int clientPid, UserHandle clientUser, int displayId) {
         mService = service;
@@ -58,14 +65,14 @@
         }
         mService.mInputManager.registerInputChannel(mServerChannel, null);
 
-        mApplicationHandle = new InputApplicationHandle(null);
+        mApplicationHandle = new InputApplicationHandle(new Binder());
         mApplicationHandle.name = name;
         mApplicationHandle.dispatchingTimeoutNanos =
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
 
         mWindowHandle = new InputWindowHandle(mApplicationHandle, null, displayId);
         mWindowHandle.name = name;
-        mWindowHandle.inputChannel = mServerChannel;
+        mWindowHandle.token = mServerChannel.getToken();
         mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
         mWindowHandle.layer = getLayerLw(mWindowHandle.layoutParamsType);
         mWindowHandle.layoutParamsFlags = 0;
@@ -80,6 +87,10 @@
         mWindowHandle.ownerUid = Process.myUid();
         mWindowHandle.inputFeatures = 0;
         mWindowHandle.scaleFactor = 1.0f;
+
+        mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId)
+                .getSession()).setContainerLayer(true).setName("Input Consumer " + name)
+                .build();
     }
 
     void linkToDeathRecipient() {
@@ -102,12 +113,33 @@
         mToken.unlinkToDeath(this, 0);
     }
 
-    void layout(int dw, int dh) {
-        mWindowHandle.touchableRegion.set(0, 0, dw, dh);
-        mWindowHandle.frameLeft = 0;
-        mWindowHandle.frameTop = 0;
-        mWindowHandle.frameRight = dw;
-        mWindowHandle.frameBottom = dh;
+    void layout(SurfaceControl.Transaction t, int dw, int dh) {
+        t.setPosition(mInputSurface, 0, 0);
+
+        mTmpClipRect.set(0, 0, dw, dh);
+        t.setWindowCrop(mInputSurface, mTmpClipRect);
+    }
+
+    void layout(SurfaceControl.Transaction t, Rect r) {
+        t.setPosition(mInputSurface, r.left, r.top);
+        mTmpClipRect.set(0, 0, r.width(), r.height());
+        t.setWindowCrop(mInputSurface, mTmpClipRect);
+    }
+
+    void hide(SurfaceControl.Transaction t) {
+        t.hide(mInputSurface);
+    }
+
+    void show(SurfaceControl.Transaction t, WindowState w) {
+        t.show(mInputSurface);
+        t.setInputWindowInfo(mInputSurface, mWindowHandle);
+        t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
+    }
+
+    void show(SurfaceControl.Transaction t, int layer) {
+        t.show(mInputSurface);
+        t.setInputWindowInfo(mInputSurface, mWindowHandle);
+        t.setLayer(mInputSurface, layer);
     }
 
     private int getLayerLw(int windowType) {
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 92ea1a9..639ed02 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -6,21 +6,16 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-import android.app.ActivityManager;
 import android.os.Debug;
 import android.os.IBinder;
-import android.os.RemoteException;
 import android.util.Slog;
+import android.view.InputApplicationHandle;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
-import android.view.InputApplicationHandle;
 import com.android.server.input.InputManagerService;
-import android.view.InputWindowHandle;
-import android.view.InputChannel;
 
 import java.io.PrintWriter;
-import java.util.HashMap;
 
 final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
     private final WindowManagerService mService;
@@ -72,8 +67,7 @@
      * Called by the InputManager.
      */
     @Override
-    public long notifyANR(InputApplicationHandle inputApplicationHandle,
-            IBinder token, String reason) {
+    public long notifyANR(IBinder token, String reason) {
         AppWindowToken appWindowToken = null;
         WindowState windowState = null;
         boolean aboveSystem = false;
@@ -84,9 +78,6 @@
                     appWindowToken = windowState.mAppToken;
                 }
             }
-            if (appWindowToken == null && inputApplicationHandle != null) {
-                appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
-            }
 
             if (windowState != null) {
                 Slog.i(TAG_WM, "Input event dispatching timed out "
@@ -116,9 +107,7 @@
         if (appWindowToken != null && appWindowToken.appToken != null) {
             // Notify the activity manager about the timeout and let it decide whether
             // to abort dispatching or keep waiting.
-            final AppWindowContainerController controller = appWindowToken.getController();
-            final boolean abort = controller != null
-                    && controller.keyDispatchingTimedOut(reason,
+            final boolean abort = appWindowToken.keyDispatchingTimedOut(reason,
                     (windowState != null) ? windowState.mSession.mPid : -1);
             if (!abort) {
                 // The activity manager declined to abort dispatching.
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 83d32c8ad..88b22cb 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -44,6 +44,7 @@
 import android.view.InputChannel;
 import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
 
 import com.android.server.policy.WindowManagerPolicy;
 
@@ -61,9 +62,8 @@
     // When true, need to call updateInputWindowsLw().
     private boolean mUpdateInputWindowsNeeded = true;
 
-    // Array of window handles to provide to the input dispatcher.
-    private InputWindowHandle[] mInputWindowHandles;
-    private int mInputWindowHandleCount;
+    // Currently focused input window handle.
+    private InputWindowHandle mFocusedInputWindowHandle;
 
     private boolean mDisableWallpaperTouchEvents;
     private final Rect mTmpRect = new Rect();
@@ -72,6 +72,8 @@
 
     private int mDisplayId;
 
+    SurfaceControl.Transaction mInputTransaction = new SurfaceControl.Transaction();
+
     /**
      * The set of input consumer added to the window manager by name, which consumes input events
      * for the windows below it.
@@ -126,6 +128,7 @@
     private boolean disposeInputConsumer(InputConsumerImpl consumer) {
         if (consumer != null) {
             consumer.disposeChannelsLw();
+            consumer.hide(mInputTransaction);
             return true;
         }
         return false;
@@ -137,7 +140,16 @@
 
     void layoutInputConsumers(int dw, int dh) {
         for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
-            mInputConsumers.valueAt(i).layout(dw, dh);
+            mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh);
+        }
+    }
+
+    // The visibility of the input consumers is recomputed each time we
+    // update the input windows. We use a model where consumers begin invisible
+    // (set so by this function) and must meet some condition for visibility on each update.
+    void resetInputConsumers(SurfaceControl.Transaction t) {
+        for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
+            mInputConsumers.valueAt(i).hide(t);
         }
     }
 
@@ -177,18 +189,7 @@
     }
 
 
-    private void addInputWindowHandle(final InputWindowHandle windowHandle) {
-        if (mInputWindowHandles == null) {
-            mInputWindowHandles = new InputWindowHandle[16];
-        }
-        if (mInputWindowHandleCount >= mInputWindowHandles.length) {
-            mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,
-                    mInputWindowHandleCount * 2);
-        }
-        mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
-    }
-
-    void addInputWindowHandle(final InputWindowHandle inputWindowHandle,
+    void populateInputWindowHandle(final InputWindowHandle inputWindowHandle,
             final WindowState child, int flags, final int type, final boolean isVisible,
             final boolean hasFocus, final boolean hasWallpaper) {
         // Add a window to our list of input windows.
@@ -214,6 +215,11 @@
         inputWindowHandle.frameRight = frame.right;
         inputWindowHandle.frameBottom = frame.bottom;
 
+        // Surface insets are hardcoded to be the same in all directions
+        // and we could probably deprecate the "left/right/top/bottom" concept.
+        // we avoid reintroducing this concept by just choosing one of them here.
+        inputWindowHandle.surfaceInset = child.getAttrs().surfaceInsets.left;
+
         if (child.mGlobalScale != 1) {
             // If we are scaling the window, input coordinates need
             // to be inversely scaled to map from what is on screen
@@ -227,12 +233,9 @@
             Slog.d(TAG_WM, "addInputWindowHandle: "
                     + child + ", " + inputWindowHandle);
         }
-        addInputWindowHandle(inputWindowHandle);
-    }
 
-    private void clearInputWindowHandlesLw() {
-        while (mInputWindowHandleCount != 0) {
-            mInputWindowHandles[--mInputWindowHandleCount] = null;
+        if (hasFocus) {
+            mFocusedInputWindowHandle = inputWindowHandle;
         }
     }
 
@@ -261,14 +264,9 @@
             if (DEBUG_DRAG) {
                 Log.d(TAG_WM, "Inserting drag window");
             }
-            final InputWindowHandle dragWindowHandle =
-                    mService.mDragDropController.getInputWindowHandleLocked();
-            if (dragWindowHandle == null) {
-                Slog.w(TAG_WM, "Drag is in progress but there is no "
-                        + "drag window handle.");
-            } else if (dragWindowHandle.displayId == mDisplayId) {
-                addInputWindowHandle(dragWindowHandle);
-            }
+            mService.mDragDropController.showInputSurface(mInputTransaction, mDisplayId);
+        } else {
+            mService.mDragDropController.hideInputSurface(mInputTransaction, mDisplayId);
         }
 
         final boolean inPositioning = mService.mTaskPositioningController.isPositioningLocked();
@@ -276,14 +274,9 @@
             if (DEBUG_TASK_POSITIONING) {
                 Log.d(TAG_WM, "Inserting window handle for repositioning");
             }
-            final InputWindowHandle dragWindowHandle =
-                    mService.mTaskPositioningController.getDragWindowHandleLocked();
-            if (dragWindowHandle == null) {
-                Slog.e(TAG_WM,
-                        "Repositioning is in progress but there is no drag window handle.");
-            } else if (dragWindowHandle.displayId == mDisplayId) {
-                addInputWindowHandle(dragWindowHandle);
-            }
+            mService.mTaskPositioningController.showInputSurface(mInputTransaction, mDisplayId);
+        } else {
+            mService.mTaskPositioningController.hideInputSurface(mInputTransaction, mDisplayId);
         }
 
         // Add all windows on the default display.
@@ -362,12 +355,6 @@
         }
     }
 
-    void onRemoved() {
-        // If DisplayContent removed, we need find a way to remove window handles of this display
-        // from InputDispatcher, so pass an empty InputWindowHandles to remove them.
-        mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
-    }
-
     private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
         InputConsumerImpl navInputConsumer;
         InputConsumerImpl pipInputConsumer;
@@ -401,16 +388,16 @@
             final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
             wallpaperController = dc.mWallpaperController;
 
-            dc.forAllWindows(this, true /* traverseTopToBottom */);
+            resetInputConsumers(mInputTransaction);
+
+            dc.forAllWindows(this,
+                    true /* traverseTopToBottom */);
+
             if (mAddWallpaperInputConsumerHandle) {
-                // No visible wallpaper found, add the wallpaper input consumer at the end.
-                addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+                wallpaperInputConsumer.show(mInputTransaction, 0);
             }
 
-            // Send windows to native code.
-            mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
-
-            clearInputWindowHandlesLw();
+            mInputTransaction.apply();
 
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
@@ -420,7 +407,7 @@
             final InputChannel inputChannel = w.mInputChannel;
             final InputWindowHandle inputWindowHandle = w.mInputWindowHandle;
             if (inputChannel == null || inputWindowHandle == null || w.mRemoved
-                    || w.canReceiveTouchInput()) {
+                    || w.cantReceiveTouchInput()) {
                 // Skip this window because it cannot possibly receive input.
                 return;
             }
@@ -438,41 +425,36 @@
                         && recentsAnimationController.shouldApplyInputConsumer(w.mAppToken)) {
                     if (recentsAnimationController.updateInputConsumerForApp(
                             recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
-                        addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
+                        recentsAnimationInputConsumer.show(mInputTransaction, w);
                         mAddRecentsAnimationInputConsumerHandle = false;
                     }
-                    // If the target app window does not yet exist, then we don't add the input
-                    // consumer window, but also don't add the app window below.
-                    return;
                 }
             }
 
             if (w.inPinnedWindowingMode()) {
-                if (mAddPipInputConsumerHandle
-                        && (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) {
+                if (mAddPipInputConsumerHandle) {
                     // Update the bounds of the Pip input consumer to match the window bounds.
                     w.getBounds(mTmpRect);
+                    pipInputConsumer.layout(mInputTransaction, mTmpRect);
+
+                    // The touchable region is relative to the surface top-left
+                    mTmpRect.offsetTo(0, 0);
                     pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
-                    addInputWindowHandle(pipInputConsumer.mWindowHandle);
+                    pipInputConsumer.show(mInputTransaction, w);
                     mAddPipInputConsumerHandle = false;
                 }
-                // TODO: Fix w.canReceiveTouchInput() to handle this case
-                if (!hasFocus) {
-                    // Skip this pinned stack window if it does not have focus
-                    return;
-                }
             }
 
             if (mAddInputConsumerHandle
                     && inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) {
-                addInputWindowHandle(navInputConsumer.mWindowHandle);
+                navInputConsumer.show(mInputTransaction, w);
                 mAddInputConsumerHandle = false;
             }
 
             if (mAddWallpaperInputConsumerHandle) {
                 if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisibleLw()) {
                     // Add the wallpaper input consumer above the first visible wallpaper.
-                    addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+                    wallpaperInputConsumer.show(mInputTransaction, w);
                     mAddWallpaperInputConsumerHandle = false;
                 }
             }
@@ -490,8 +472,13 @@
                 mService.mDragDropController.sendDragStartedIfNeededLocked(w);
             }
 
-            addInputWindowHandle(
+            populateInputWindowHandle(
                     inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper);
+
+            if (w.mWinAnimator.hasSurface()) {
+                mInputTransaction.setInputWindowInfo(
+                        w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
new file mode 100644
index 0000000..e96f0b1
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.view.InsetsSource;
+
+import com.android.internal.util.function.TriConsumer;
+import com.android.server.policy.WindowManagerPolicy;
+
+/**
+ * Controller for a specific inset source on the server. It's called provider as it provides the
+ * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
+ */
+class InsetsSourceProvider {
+
+    private final Rect mTmpRect = new Rect();
+    private final @NonNull InsetsSource mSource;
+    private WindowState mWin;
+    private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
+
+    InsetsSourceProvider(InsetsSource source) {
+        mSource = source;
+    }
+
+    InsetsSource getSource() {
+        return mSource;
+    }
+
+    /**
+     * Updates the window that currently backs this source.
+     *
+     * @param win The window that links to this source.
+     * @param frameProvider Based on display frame state and the window, calculates the resulting
+     *                      frame that should be reported to clients.
+     */
+    void setWindow(@Nullable WindowState win,
+            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+        if (mWin != null) {
+            mWin.setInsetProvider(null);
+        }
+        mWin = win;
+        mFrameProvider = frameProvider;
+        if (win == null) {
+            mSource.setVisible(false);
+            mSource.setFrame(new Rect());
+        } else {
+            mSource.setVisible(true);
+            mWin.setInsetProvider(this);
+        }
+    }
+
+    /**
+     * Called when a layout pass has occurred.
+     */
+    void onPostLayout() {
+        if (mWin == null) {
+            return;
+        }
+
+        mTmpRect.set(mWin.getFrameLw());
+        if (mFrameProvider != null) {
+            mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
+        } else {
+            mTmpRect.inset(mWin.mGivenContentInsets);
+        }
+        mSource.setFrame(mTmpRect);
+        mSource.setVisible(mWin.isVisible() && !mWin.mGivenInsetsPending);
+
+    }
+}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
new file mode 100644
index 0000000..1189ee6
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+
+import android.util.ArrayMap;
+import android.view.InsetsState;
+
+import java.io.PrintWriter;
+import java.util.function.Consumer;
+
+/**
+ * Manages global window inset state in the system represented by {@link InsetsState}.
+ */
+class InsetsStateController {
+
+    private final InsetsState mLastState = new InsetsState();
+    private final InsetsState mState = new InsetsState();
+    private final DisplayContent mDisplayContent;
+    private ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>();
+
+    private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
+        if (w.isVisible()) {
+            w.notifyInsetsChanged();
+        }
+    };
+
+    InsetsStateController(DisplayContent displayContent) {
+        mDisplayContent = displayContent;
+    }
+
+    /**
+     * When dispatching window state to the client, we'll need to exclude the source that represents
+     * the window that is being dispatched.
+     *
+     * @param target The client we dispatch the state to.
+     * @return The state stripped of the necessary information.
+     */
+    InsetsState getInsetsForDispatch(WindowState target) {
+        final InsetsSourceProvider provider = target.getInsetProvider();
+        if (provider == null) {
+            return mState;
+        }
+
+        final InsetsState state = new InsetsState();
+        state.set(mState);
+        final int type = provider.getSource().getType();
+        state.removeSource(type);
+
+        // Navigation bar doesn't get influenced by anything else
+        if (type == TYPE_NAVIGATION_BAR) {
+            state.removeSource(TYPE_IME);
+            state.removeSource(TYPE_TOP_BAR);
+        }
+        return state;
+    }
+
+    /**
+     * @return The provider of a specific type.
+     */
+    InsetsSourceProvider getSourceProvider(int type) {
+        return mControllers.computeIfAbsent(type,
+                key -> new InsetsSourceProvider(mState.getSource(key)));
+    }
+
+    /**
+     * Called when a layout pass has occurred.
+     */
+    void onPostLayout() {
+        for (int i = mControllers.size() - 1; i>= 0; i--) {
+            mControllers.valueAt(i).onPostLayout();
+        }
+        if (!mLastState.equals(mState)) {
+            mLastState.set(mState, true /* copySources */);
+            notifyInsetsChanged();
+        }
+    }
+
+    private void notifyInsetsChanged() {
+        mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "WindowInsetsStateController");
+        mState.dump(prefix + "  ", pw);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index c91af73..4ef3513 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -72,6 +72,7 @@
     private int mVisibilityTransactionDepth;
     private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
     private final ActivityTaskManagerService mService;
+    private RootActivityContainer mRootActivityContainer;
 
     KeyguardController(ActivityTaskManagerService service,
             ActivityStackSupervisor stackSupervisor) {
@@ -81,6 +82,7 @@
 
     void setWindowManager(WindowManagerService windowManager) {
         mWindowManager = windowManager;
+        mRootActivityContainer = mService.mRootActivityContainer;
     }
 
     /**
@@ -146,7 +148,7 @@
                 mDismissalRequested = false;
             }
         }
-        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
         updateKeyguardSleepToken();
     }
 
@@ -172,16 +174,17 @@
         mWindowManager.deferSurfaceLayout();
         try {
             setKeyguardGoingAway(true);
-            mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+            mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
                     .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
                             false /* alwaysKeepCurrent */, convertTransitFlags(flags),
                             false /* forceOverride */);
             updateKeyguardSleepToken();
 
             // Some stack visibility might change (e.g. docked stack)
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-            mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+            mRootActivityContainer.addStartingWindowsForVisibleActivities(
+                    true /* taskSwitch */);
             mWindowManager.executeAppTransition();
         } finally {
             Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
@@ -277,8 +280,9 @@
 
     private void visibilitiesUpdated() {
         boolean requestDismissKeyguard = false;
-        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
-            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+        for (int displayNdx = mRootActivityContainer.getChildCount() - 1;
+             displayNdx >= 0; displayNdx--) {
+            final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx);
             final KeyguardDisplayState state = getDisplay(display.mDisplayId);
             state.visibilitiesUpdated(this, display);
             requestDismissKeyguard |= state.mRequestDismissKeyguard;
@@ -298,12 +302,12 @@
         if (isKeyguardLocked()) {
             mWindowManager.deferSurfaceLayout();
             try {
-                mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+                mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
                         .prepareAppTransition(resolveOccludeTransit(),
                                 false /* alwaysKeepCurrent */, 0 /* flags */,
                                 true /* forceOverride */);
                 updateKeyguardSleepToken();
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                 mWindowManager.executeAppTransition();
             } finally {
                 mWindowManager.continueSurfaceLayout();
@@ -319,21 +323,23 @@
         // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
         // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
         // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
-        if (mWindowManager.isKeyguardSecure()) {
-            mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
-            mDismissalRequested = true;
+        if (!mWindowManager.isKeyguardSecure()) {
+            return;
+        }
 
-            // 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 =
-                    mStackSupervisor.getDefaultDisplay().getWindowContainerController();
-            if (mKeyguardShowing && canDismissKeyguard()
-                    && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
-                dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
-                        0 /* flags */, true /* forceOverride */);
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                mWindowManager.executeAppTransition();
-            }
+        mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
+        mDismissalRequested = true;
+
+        // 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();
+        if (mKeyguardShowing && canDismissKeyguard()
+                && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
+            dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
+                    0 /* flags */, true /* forceOverride */);
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+            mWindowManager.executeAppTransition();
         }
     }
 
@@ -350,7 +356,7 @@
 
     private int resolveOccludeTransit() {
         final DisplayWindowController dwc =
-                mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+                mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
         if (mBeforeUnoccludeTransit != TRANSIT_UNSET
                 && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
                 // TODO(b/113840485): Handle app transition for individual display.
@@ -377,7 +383,8 @@
             // show on top of the lock screen. In this can we want to dismiss the docked
             // stack since it will be complicated/risky to try to put the activity on top
             // of the lock screen in the right fullscreen configuration.
-            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+            final ActivityStack stack =
+                    mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
             if (stack == null) {
                 return;
             }
@@ -387,8 +394,9 @@
     }
 
     private void updateKeyguardSleepToken() {
-        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
-            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+        for (int displayNdx = mRootActivityContainer.getChildCount() - 1;
+             displayNdx >= 0; displayNdx--) {
+            final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx);
             final KeyguardDisplayState state = getDisplay(display.mDisplayId);
             if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) {
                 state.acquiredSleepToken();
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 72d5143..da9a507 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -223,7 +223,8 @@
     private boolean saveTaskToLaunchParam(TaskRecord task, PersistableLaunchParams params) {
         final ActivityStack<?> stack = task.getStack();
         final int displayId = stack.mDisplayId;
-        final ActivityDisplay display = mSupervisor.getActivityDisplay(displayId);
+        final ActivityDisplay display =
+                mSupervisor.mRootActivityContainer.getActivityDisplay(displayId);
         final DisplayInfo info = new DisplayInfo();
         display.mDisplay.getDisplayInfo(info);
 
@@ -259,7 +260,7 @@
             return;
         }
 
-        final ActivityDisplay display = mSupervisor.getActivityDisplay(
+        final ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay(
                 persistableParams.mDisplayUniqueId);
         if (display != null) {
             outParams.mPreferredDisplayId =  display.mDisplayId;
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index b49d304..1a2aa2f 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -186,7 +186,6 @@
                     createSurface();
                 }
                 t.setPosition(mSurface, mSurfaceFrame.left, mSurfaceFrame.top);
-                t.setSize(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
                 t.setWindowCrop(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
                 t.show(mSurface);
             } else if (mSurface != null) {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 41d0777..80dc245 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -447,7 +447,7 @@
             return;
         }
         task.performClearTaskLocked();
-        mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
     }
 
     /**
@@ -579,7 +579,7 @@
         if (andResume) {
             mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
                     lockTaskModeState != LOCK_TASK_MODE_NONE);
-            mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
             final ActivityStack stack = task.getStack();
             if (stack != null) {
                 stack.getDisplay().getWindowContainerController().executeAppTransition();
@@ -641,11 +641,12 @@
             taskChanged = true;
         }
 
-        for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) {
-            mSupervisor.getChildAt(displayNdx).onLockTaskPackagesUpdated();
+        for (int displayNdx = mSupervisor.mRootActivityContainer.getChildCount() - 1;
+             displayNdx >= 0; --displayNdx) {
+            mSupervisor.mRootActivityContainer.getChildAt(displayNdx).onLockTaskPackagesUpdated();
         }
 
-        final ActivityRecord r = mSupervisor.topRunningActivityLocked();
+        final ActivityRecord r = mSupervisor.mRootActivityContainer.topRunningActivity();
         final TaskRecord task = (r != null) ? r.getTask() : null;
         if (mLockTaskModeTasks.isEmpty() && task!= null
                 && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
@@ -657,7 +658,7 @@
         }
 
         if (taskChanged) {
-            mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java
index 3ef42e7..1c7ebd6 100644
--- a/services/core/java/com/android/server/wm/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/wm/PinnedActivityStack.java
@@ -41,7 +41,7 @@
     PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
             Rect outBounds) {
         return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
-                mStackSupervisor.mWindowManager);
+                mRootActivityContainer.mWindowManager);
     }
 
     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 476c1f9..24c5228 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -79,7 +79,7 @@
             int callingPid) {
         mService = atm;
         mStackSupervisor = stackSupervisor;
-        mDefaultDisplay = stackSupervisor.getDefaultDisplay();
+        mDefaultDisplay = mService.mRootActivityContainer.getDefaultDisplay();
         mActivityStartController = activityStartController;
         mWindowManager = wm;
         mCallingPid = callingPid;
@@ -94,7 +94,7 @@
 
         // TODO(multi-display) currently only support recents animation in default display.
         final DisplayWindowController dwc =
-                mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+                mService.mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
         if (!mWindowManager.canStartRecentsAnimation()) {
             notifyAnimationCancelBeforeStart(recentsAnimationRunner);
             if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
@@ -124,8 +124,8 @@
         // Send launch hint if we are actually launching the target. If it's already visible
         // (shouldn't happen in general) we don't need to send it.
         if (targetActivity == null || !targetActivity.visible) {
-            mStackSupervisor.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */,
-                    targetActivity);
+            mService.mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+                    true /* forceSend */, targetActivity);
         }
 
         mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
@@ -192,7 +192,7 @@
 
             // If we updated the launch-behind state, update the visibility of the activities after
             // we fetch the visible tasks to be controlled by the animation
-            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+            mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
 
             mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
                     targetActivity);
@@ -215,7 +215,8 @@
             @Deprecated IAssistDataReceiver assistDataReceiver, int userId) {
         final AppOpsManager appOpsManager = (AppOpsManager)
                 mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
-        final List<IBinder> topActivities = mStackSupervisor.getTopVisibleActivities();
+        final List<IBinder> topActivities =
+                mService.mRootActivityContainer.getTopVisibleActivities();
         final AssistDataRequester.AssistDataRequesterCallbacks assistDataCallbacks;
         if (assistDataReceiver != null) {
             assistDataCallbacks = new AssistDataReceiverProxy(assistDataReceiver,
@@ -283,7 +284,7 @@
             // Just to be sure end the launch hint in case the target activity was never launched.
             // However, if we're keeping the activity and making it visible, we can leave it on.
             if (reorderMode != REORDER_KEEP_IN_PLACE) {
-                mStackSupervisor.sendPowerHintForLaunchEndIfNeeded();
+                mService.mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded();
             }
 
             mService.mH.post(
@@ -343,8 +344,8 @@
                     }
 
                     mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, false);
+                    mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
 
                     // No reason to wait for the pausing activity in this case, as the hiding of
                     // surfaces needs to be done immediately.
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
new file mode 100644
index 0000000..4dd48c4
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -0,0 +1,2297 @@
+/*
+ * 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.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.windowingModeToString;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
+import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
+import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
+import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
+import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
+import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
+import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
+
+import static java.lang.Integer.MAX_VALUE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AppGlobals;
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.Build;
+import android.os.FactoryTest;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.ArraySet;
+import android.util.DisplayMetrics;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.am.AppTimeTracker;
+import com.android.server.am.UserState;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Root node for activity containers.
+ * TODO: This class is mostly temporary to separate things out of ActivityStackSupervisor.java. The
+ * intention is to have this merged with RootWindowContainer.java as part of unifying the hierarchy.
+ */
+class RootActivityContainer extends ConfigurationContainer
+        implements DisplayManager.DisplayListener {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "RootActivityContainer" : TAG_ATM;
+    static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+    private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+    static final String TAG_STATES = TAG + POSTFIX_STATES;
+    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+
+    /**
+     * The modes which affect which tasks are returned when calling
+     * {@link RootActivityContainer#anyTaskForId(int)}.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            MATCH_TASK_IN_STACKS_ONLY,
+            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
+            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+    })
+    public @interface AnyTaskForIdMatchTaskMode {}
+    // Match only tasks in the current stacks
+    static final int MATCH_TASK_IN_STACKS_ONLY = 0;
+    // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
+    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
+    // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
+    // provided stack id
+    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
+
+    ActivityTaskManagerService mService;
+    ActivityStackSupervisor mStackSupervisor;
+    WindowManagerService mWindowManager;
+    DisplayManager mDisplayManager;
+    private DisplayManagerInternal mDisplayManagerInternal;
+    // TODO: Remove after object merge with RootWindowContainer.
+    private RootWindowContainer mRootWindowContainer;
+
+    /**
+     * List of displays which contain activities, sorted by z-order.
+     * The last entry in the list is the topmost.
+     */
+    private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>();
+
+    /** Reference to default display so we can quickly look it up. */
+    private ActivityDisplay mDefaultDisplay;
+    private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
+
+    /** The current user */
+    int mCurrentUser;
+    /** Stack id of the front stack when user switched, indexed by userId. */
+    SparseIntArray mUserStackInFront = new SparseIntArray(2);
+
+    /**
+     * A list of tokens that cause the top activity to be put to sleep.
+     * They are used by components that may hide and block interaction with underlying
+     * activities.
+     */
+    final ArrayList<ActivityTaskManagerInternal.SleepToken> mSleepTokens = new ArrayList<>();
+
+    /** Is dock currently minimized. */
+    boolean mIsDockMinimized;
+
+    /** Set when a power hint has started, but not ended. */
+    private boolean mPowerHintSent;
+
+    // The default minimal size that will be used if the activity doesn't specify its minimal size.
+    // It will be calculated when the default display gets added.
+    int mDefaultMinSizeOfResizeableTaskDp = -1;
+
+    // Whether tasks have moved and we need to rank the tasks before next OOM scoring
+    private boolean mTaskLayersChanged = true;
+
+    private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+
+    private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
+    static class FindTaskResult {
+        ActivityRecord mRecord;
+        boolean mIdealMatch;
+
+        void clear() {
+            mRecord = null;
+            mIdealMatch = false;
+        }
+
+        void setTo(FindTaskResult result) {
+            mRecord = result.mRecord;
+            mIdealMatch = result.mIdealMatch;
+        }
+    }
+
+    RootActivityContainer(ActivityTaskManagerService service) {
+        mService = service;
+        mStackSupervisor = service.mStackSupervisor;
+        mStackSupervisor.mRootActivityContainer = this;
+    }
+
+    @VisibleForTesting
+    void setWindowContainer(RootWindowContainer container) {
+        mRootWindowContainer = container;
+        mRootWindowContainer.setRootActivityContainer(this);
+    }
+
+    void setWindowManager(WindowManagerService wm) {
+        mWindowManager = wm;
+        setWindowContainer(mWindowManager.mRoot);
+        mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
+        mDisplayManager.registerDisplayListener(this, mService.mH);
+        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+
+        final Display[] displays = mDisplayManager.getDisplays();
+        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
+            final Display display = displays[displayNdx];
+            final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
+            if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
+                mDefaultDisplay = activityDisplay;
+            }
+            addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
+        }
+        calculateDefaultMinimalSizeOfResizeableTasks();
+
+        final ActivityDisplay defaultDisplay = getDefaultDisplay();
+
+        defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
+    }
+
+    // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
+    ActivityDisplay getDefaultDisplay() {
+        return mDefaultDisplay;
+    }
+
+    /**
+     * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is
+     * defined in {@link DisplayInfo#uniqueId}.
+     *
+     * @param uniqueId the unique ID of the display
+     * @return the {@link ActivityDisplay} or {@code null} if nothing is found.
+     */
+    ActivityDisplay getActivityDisplay(String uniqueId) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            final boolean isValid = display.mDisplay.isValid();
+            if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) {
+                return display;
+            }
+        }
+
+        return null;
+    }
+
+    // TODO: Look into consolidating with getActivityDisplayOrCreate()
+    ActivityDisplay getActivityDisplay(int displayId) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
+            if (activityDisplay.mDisplayId == displayId) {
+                return activityDisplay;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Get an existing instance of {@link ActivityDisplay} or create new if there is a
+     * corresponding record in display manager.
+     */
+    // TODO: Look into consolidating with getActivityDisplay()
+    ActivityDisplay getActivityDisplayOrCreate(int displayId) {
+        ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+        if (activityDisplay != null) {
+            return activityDisplay;
+        }
+        if (mDisplayManager == null) {
+            // The system isn't fully initialized yet.
+            return null;
+        }
+        final Display display = mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            // The display is not registered in DisplayManager.
+            return null;
+        }
+        // The display hasn't been added to ActivityManager yet, create a new record now.
+        activityDisplay = new ActivityDisplay(this, display);
+        addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
+        return activityDisplay;
+    }
+
+    /** Check if display with specified id is added to the list. */
+    boolean isDisplayAdded(int displayId) {
+        return getActivityDisplayOrCreate(displayId) != null;
+    }
+
+    ActivityRecord getDefaultDisplayHomeActivity() {
+        return getDefaultDisplayHomeActivityForUser(mCurrentUser);
+    }
+
+    ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
+        return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
+    }
+
+    boolean startHomeOnAllDisplays(int userId, String reason) {
+        boolean homeStarted = false;
+        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+            final int displayId = mActivityDisplays.get(i).mDisplayId;
+            homeStarted |= startHomeOnDisplay(userId, reason, displayId);
+        }
+        return homeStarted;
+    }
+
+    /**
+     * This starts home activity on displays that can have system decorations and only if the
+     * home activity can have multiple instances.
+     */
+    boolean startHomeOnDisplay(int userId, String reason, int displayId) {
+        final Intent homeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+        if (aInfo == null) {
+            return false;
+        }
+
+        if (!canStartHomeOnDisplay(aInfo, displayId,
+                false /* allowInstrumenting */)) {
+            return false;
+        }
+
+        // Update the reason for ANR debugging to verify if the user activity is the one that
+        // actually launched.
+        final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
+                aInfo.applicationInfo.uid);
+        mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
+                displayId);
+        return true;
+    }
+
+    /**
+     * This resolves the home activity info and updates the home component of the given intent.
+     * @return the home activity info if any.
+     */
+    private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
+        final int flags = ActivityManagerService.STOCK_PM_FLAGS;
+        final ComponentName comp = homeIntent.getComponent();
+        ActivityInfo aInfo = null;
+        try {
+            if (comp != null) {
+                // Factory test.
+                aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
+            } else {
+                final String resolvedType =
+                        homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
+                final ResolveInfo info = AppGlobals.getPackageManager()
+                        .resolveIntent(homeIntent, resolvedType, flags, userId);
+                if (info != null) {
+                    aInfo = info.activityInfo;
+                }
+            }
+        } catch (RemoteException e) {
+            // ignore
+        }
+
+        if (aInfo == null) {
+            Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable());
+            return null;
+        }
+
+        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
+        aInfo = new ActivityInfo(aInfo);
+        aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
+        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
+        return aInfo;
+    }
+
+    boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
+        if (!mService.isBooting() && !mService.isBooted()) {
+            // Not ready yet!
+            return false;
+        }
+
+        if (displayId == INVALID_DISPLAY) {
+            displayId = DEFAULT_DISPLAY;
+        }
+
+        final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
+        final String myReason = reason + " resumeHomeActivity";
+
+        // Only resume home activity if isn't finishing.
+        if (r != null && !r.finishing) {
+            r.moveFocusableActivityToTop(myReason);
+            return resumeFocusedStacksTopActivities(r.getStack(), prev, null);
+        }
+        return startHomeOnDisplay(mCurrentUser, myReason, displayId);
+    }
+
+    /**
+     * Check if home activity start should be allowed on a display.
+     * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched.
+     * @param displayId The id of the target display.
+     * @param allowInstrumenting Whether launching home should be allowed if being instrumented.
+     * @return {@code true} if allow to launch, {@code false} otherwise.
+     */
+    boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId,
+            boolean allowInstrumenting) {
+        if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
+                && mService.mTopAction == null) {
+            // We are running in factory test mode, but unable to find the factory test app, so
+            // just sit around displaying the error message and don't try to start anything.
+            return false;
+        }
+
+        final WindowProcessController app =
+                mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid);
+        if (!allowInstrumenting && app != null && app.isInstrumenting()) {
+            // Don't do this if the home app is currently being instrumented.
+            return false;
+        }
+
+        if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
+                && displayId == mService.mVr2dDisplayId)) {
+            // No restrictions to default display or vr 2d display.
+            return true;
+        }
+
+        final ActivityDisplay display = getActivityDisplay(displayId);
+        if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
+            // Can't launch home on display that doesn't support system decorations.
+            return false;
+        }
+
+        final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
+                && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
+                && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+        if (!supportMultipleInstance) {
+            // Can't launch home on other displays if it requested to be single instance. Also we
+            // don't allow home applications that target before Q to have multiple home activity
+            // instances because they may not be expected to have multiple home scenario and
+            // haven't explicitly request for single instance.
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Ensure all activities visibility, update orientation and configuration.
+     *
+     * @param starting The currently starting activity or {@code null} if there is none.
+     * @param displayId The id of the display where operation is executed.
+     * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
+     *                                  {@code true} if config changed.
+     * @param deferResume Whether to defer resume while updating config.
+     * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
+     *         because of configuration update.
+     */
+    boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
+            boolean markFrozenIfConfigChanged, boolean deferResume) {
+        // First ensure visibility without updating the config just yet. We need this to know what
+        // activities are affecting configuration now.
+        // Passing null here for 'starting' param value, so that visibility of actual starting
+        // activity will be properly updated.
+        ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+                false /* preserveWindows */, false /* notifyClients */);
+
+        if (displayId == INVALID_DISPLAY) {
+            // The caller didn't provide a valid display id, skip updating config.
+            return true;
+        }
+
+        // Force-update the orientation from the WindowManager, since we need the true configuration
+        // to send to the client now.
+        final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+                getDisplayOverrideConfiguration(displayId),
+                starting != null && starting.mayFreezeScreenLocked(starting.app)
+                        ? starting.appToken : null,
+                displayId, true /* forceUpdate */);
+        if (starting != null && markFrozenIfConfigChanged && config != null) {
+            starting.frozenBeforeDestroy = true;
+        }
+
+        // Update the configuration of the activities on the display.
+        return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
+                displayId);
+    }
+
+    /**
+     * @return a list of activities which are the top ones in each visible stack. The first
+     * entry will be the focused activity.
+     */
+    List<IBinder> getTopVisibleActivities() {
+        final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
+        final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+        // Traverse all displays.
+        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            // Traverse all stacks on a display.
+            for (int j = display.getChildCount() - 1; j >= 0; --j) {
+                final ActivityStack stack = display.getChildAt(j);
+                // Get top activity from a visible stack and add it to the list.
+                if (stack.shouldBeVisible(null /* starting */)) {
+                    final ActivityRecord top = stack.getTopActivity();
+                    if (top != null) {
+                        if (stack == topFocusedStack) {
+                            topActivityTokens.add(0, top.appToken);
+                        } else {
+                            topActivityTokens.add(top.appToken);
+                        }
+                    }
+                }
+            }
+        }
+        return topActivityTokens;
+    }
+
+    ActivityStack getTopDisplayFocusedStack() {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack();
+            if (focusedStack != null) {
+                return focusedStack;
+            }
+        }
+        return null;
+    }
+
+    ActivityRecord getTopResumedActivity() {
+        final ActivityStack focusedStack = getTopDisplayFocusedStack();
+        if (focusedStack == null) {
+            return null;
+        }
+        final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+        if (resumedActivity != null && resumedActivity.app != null) {
+            return resumedActivity;
+        }
+        // The top focused stack might not have a resumed activity yet - look on all displays in
+        // focus order.
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
+            if (resumedActivityOnDisplay != null) {
+                return resumedActivityOnDisplay;
+            }
+        }
+        return null;
+    }
+
+    boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
+        if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
+            return false;
+        }
+
+        return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
+    }
+
+    boolean isTopDisplayFocusedStack(ActivityStack stack) {
+        return stack != null && stack == getTopDisplayFocusedStack();
+    }
+
+    void updatePreviousProcess(ActivityRecord r) {
+        // Now that this process has stopped, we may want to consider it to be the previous app to
+        // try to keep around in case the user wants to return to it.
+
+        // First, found out what is currently the foreground app, so that we don't blow away the
+        // previous app if this activity is being hosted by the process that is actually still the
+        // foreground.
+        WindowProcessController fgApp = null;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (isTopDisplayFocusedStack(stack)) {
+                    final ActivityRecord resumedActivity = stack.getResumedActivity();
+                    if (resumedActivity != null) {
+                        fgApp = resumedActivity.app;
+                    } else if (stack.mPausingActivity != null) {
+                        fgApp = stack.mPausingActivity.app;
+                    }
+                    break;
+                }
+            }
+        }
+
+        // Now set this one as the previous process, only if that really makes sense to.
+        if (r.hasProcess() && fgApp != null && r.app != fgApp
+                && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
+                && r.app != mService.mHomeProcess) {
+            mService.mPreviousProcess = r.app;
+            mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
+        }
+    }
+
+    boolean attachApplication(WindowProcessController app) throws RemoteException {
+        final String processName = app.mName;
+        boolean didSomething = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final ActivityStack stack = display.getFocusedStack();
+            if (stack != null) {
+                stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
+                final ActivityRecord top = stack.topRunningActivityLocked();
+                final int size = mTmpActivityList.size();
+                for (int i = 0; i < size; i++) {
+                    final ActivityRecord activity = mTmpActivityList.get(i);
+                    if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
+                            && processName.equals(activity.processName)) {
+                        try {
+                            if (mStackSupervisor.realStartActivityLocked(activity, app,
+                                    top == activity /* andResume */, true /* checkConfig */)) {
+                                didSomething = true;
+                            }
+                        } catch (RemoteException e) {
+                            Slog.w(TAG, "Exception in new application when starting activity "
+                                    + top.intent.getComponent().flattenToShortString(), e);
+                            throw e;
+                        }
+                    }
+                }
+            }
+        }
+        if (!didSomething) {
+            ensureActivitiesVisible(null, 0, false /* preserve_windows */);
+        }
+        return didSomething;
+    }
+
+    /**
+     * Make sure that all activities that need to be visible in the system actually are and update
+     * their configuration.
+     */
+    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
+            boolean preserveWindows) {
+        ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
+    }
+
+    /**
+     * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
+     */
+    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
+            boolean preserveWindows, boolean notifyClients) {
+        mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
+        try {
+            // First the front stacks. In case any are not fullscreen and are in front of home.
+            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getChildAt(stackNdx);
+                    stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
+                            notifyClients);
+                }
+            }
+        } finally {
+            mStackSupervisor.getKeyguardController().endActivityVisibilityUpdate();
+        }
+    }
+
+    boolean switchUser(int userId, UserState uss) {
+        final int focusStackId = getTopDisplayFocusedStack().getStackId();
+        // We dismiss the docked stack whenever we switch users.
+        final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
+        if (dockedStack != null) {
+            mStackSupervisor.moveTasksToFullscreenStackLocked(
+                    dockedStack, dockedStack.isFocusedStackOnDisplay());
+        }
+        // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
+        // also cause all tasks to be moved to the fullscreen stack at a position that is
+        // appropriate.
+        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+
+        mUserStackInFront.put(mCurrentUser, focusStackId);
+        final int restoreStackId =
+                mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
+        mCurrentUser = userId;
+
+        mStackSupervisor.mStartingUsers.add(uss);
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.switchUserLocked(userId);
+                TaskRecord task = stack.topTask();
+                if (task != null) {
+                    stack.positionChildWindowContainerAtTop(task);
+                }
+            }
+        }
+
+        ActivityStack stack = getStack(restoreStackId);
+        if (stack == null) {
+            stack = getDefaultDisplay().getHomeStack();
+        }
+        final boolean homeInFront = stack.isActivityTypeHome();
+        if (stack.isOnHomeDisplay()) {
+            stack.moveToFront("switchUserOnHomeDisplay");
+        } else {
+            // Stack was moved to another display while user was swapped out.
+            resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
+        }
+        return homeInFront;
+    }
+
+    void removeUser(int userId) {
+        mUserStackInFront.delete(userId);
+    }
+
+    /**
+     * Update the last used stack id for non-current user (current user's last
+     * used stack is the focused stack)
+     */
+    void updateUserStack(int userId, ActivityStack stack) {
+        if (userId != mCurrentUser) {
+            mUserStackInFront.put(userId, stack != null ? stack.getStackId()
+                    : getDefaultDisplay().getHomeStack().mStackId);
+        }
+    }
+
+    void resizeStack(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
+            Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
+            boolean deferResume) {
+
+        if (stack.inSplitScreenPrimaryWindowingMode()) {
+            mStackSupervisor.resizeDockedStackLocked(bounds, tempTaskBounds,
+                    tempTaskInsetBounds, null, null, preserveWindows, deferResume);
+            return;
+        }
+
+        final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
+        if (!allowResizeInDockedMode
+                && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
+            // If the docked stack exists, don't resize non-floating stacks independently of the
+            // size computed from the docked stack size (otherwise they will be out of sync)
+            return;
+        }
+
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
+        mWindowManager.deferSurfaceLayout();
+        try {
+            if (stack.affectedBySplitScreenResize()) {
+                if (bounds == null && stack.inSplitScreenWindowingMode()) {
+                    // null bounds = fullscreen windowing mode...at least for now.
+                    stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                } else if (splitScreenActive) {
+                    // If we are in split-screen mode and this stack support split-screen, then
+                    // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
+                    stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                }
+            }
+            stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
+            if (!deferResume) {
+                stack.ensureVisibleActivitiesConfigurationLocked(
+                        stack.topRunningActivityLocked(), preserveWindows);
+            }
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    /**
+     * Move stack with all its existing content to specified display.
+     * @param stackId Id of stack to move.
+     * @param displayId Id of display to move stack to.
+     * @param onTop Indicates whether container should be place on top or on bottom.
+     */
+    void moveStackToDisplay(int stackId, int displayId, boolean onTop) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException("moveStackToDisplay: Unknown displayId="
+                    + displayId);
+        }
+        final ActivityStack stack = getStack(stackId);
+        if (stack == null) {
+            throw new IllegalArgumentException("moveStackToDisplay: Unknown stackId="
+                    + stackId);
+        }
+
+        final ActivityDisplay currentDisplay = stack.getDisplay();
+        if (currentDisplay == null) {
+            throw new IllegalStateException("moveStackToDisplay: Stack with stack=" + stack
+                    + " is not attached to any display.");
+        }
+
+        if (currentDisplay.mDisplayId == displayId) {
+            throw new IllegalArgumentException("Trying to move stack=" + stack
+                    + " to its current displayId=" + displayId);
+        }
+
+        stack.reparent(activityDisplay, onTop, false /* displayRemoved */);
+        // TODO(multi-display): resize stacks properly if moved from split-screen.
+    }
+
+    boolean moveTopStackActivityToPinnedStack(int stackId) {
+        final ActivityStack stack = getStack(stackId);
+        if (stack == null) {
+            throw new IllegalArgumentException(
+                    "moveTopStackActivityToPinnedStack: Unknown stackId=" + stackId);
+        }
+
+        final ActivityRecord r = stack.topRunningActivityLocked();
+        if (r == null) {
+            Slog.w(TAG, "moveTopStackActivityToPinnedStack: No top running activity"
+                    + " in stack=" + stack);
+            return false;
+        }
+
+        if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
+            Slog.w(TAG, "moveTopStackActivityToPinnedStack: Picture-In-Picture not supported for "
+                    + " r=" + r);
+            return false;
+        }
+
+        moveActivityToPinnedStack(r, null /* sourceBounds */, 0f /* aspectRatio */,
+                "moveTopActivityToPinnedStack");
+        return true;
+    }
+
+    void moveActivityToPinnedStack(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
+            String reason) {
+
+        mWindowManager.deferSurfaceLayout();
+
+        final ActivityDisplay display = r.getStack().getDisplay();
+        PinnedActivityStack stack = display.getPinnedStack();
+
+        // This will clear the pinned stack by moving an existing task to the full screen stack,
+        // ensuring only one task is present.
+        if (stack != null) {
+            mStackSupervisor.moveTasksToFullscreenStackLocked(stack, !ON_TOP);
+        }
+
+        // Need to make sure the pinned stack exist so we can resize it below...
+        stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
+
+        // Calculate the target bounds here before the task is reparented back into pinned windowing
+        // mode (which will reset the saved bounds)
+        final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
+
+        try {
+            final TaskRecord task = r.getTask();
+            // Resize the pinned stack to match the current size of the task the activity we are
+            // going to be moving is currently contained in. We do this to have the right starting
+            // animation bounds for the pinned stack to the desired bounds the caller wants.
+            resizeStack(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
+                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
+                    true /* allowResizeInDockedMode */, !DEFER_RESUME);
+
+            if (task.mActivities.size() == 1) {
+                // Defer resume until below, and do not schedule PiP changes until we animate below
+                task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
+                        false /* schedulePictureInPictureModeChange */, reason);
+            } else {
+                // There are multiple activities in the task and moving the top activity should
+                // reveal/leave the other activities in their original task.
+
+                // Currently, we don't support reparenting activities across tasks in two different
+                // stacks, so instead, just create a new task in the same stack, reparent the
+                // activity into that task, and then reparent the whole task to the new stack. This
+                // ensures that all the necessary work to migrate states in the old and new stacks
+                // is also done.
+                final TaskRecord newTask = task.getStack().createTaskRecord(
+                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId), r.info,
+                        r.intent, null, null, true);
+                r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
+
+                // Defer resume until below, and do not schedule PiP changes until we animate below
+                newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+                        DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
+            }
+
+            // Reset the state that indicates it can enter PiP while pausing after we've moved it
+            // to the pinned stack
+            r.supportsEnterPipOnTaskSwitch = false;
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+        }
+
+        stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
+                true /* fromFullscreen */);
+
+        // Update the visibility of all activities after the they have been reparented to the new
+        // stack.  This MUST run after the animation above is scheduled to ensure that the windows
+        // drawn signal is scheduled after the bounds animation start call on the bounds animator
+        // thread.
+        ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+        resumeFocusedStacksTopActivities();
+
+        mService.getTaskChangeNotificationController().notifyActivityPinned(r);
+    }
+
+    void executeAppTransitionForAllDisplay() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            display.getWindowContainerController().executeAppTransition();
+        }
+    }
+
+    void setDockedStackMinimized(boolean minimized) {
+        // Get currently focused stack before setting mIsDockMinimized. We do this because if
+        // split-screen is active, primary stack will not be focusable (see #isFocusable) while
+        // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
+        final ActivityStack current = getTopDisplayFocusedStack();
+        mIsDockMinimized = minimized;
+        if (mIsDockMinimized) {
+            if (current.inSplitScreenPrimaryWindowingMode()) {
+                // The primary split-screen stack can't be focused while it is minimize, so move
+                // focus to something else.
+                current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
+            }
+        }
+    }
+
+    ActivityRecord findTask(ActivityRecord r, int preferredDisplayId) {
+        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
+        mTmpFindTaskResult.clear();
+
+        // Looking up task on preferred display first
+        final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId);
+        if (preferredDisplay != null) {
+            preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
+            if (mTmpFindTaskResult.mIdealMatch) {
+                return mTmpFindTaskResult.mRecord;
+            }
+        }
+
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            if (display.mDisplayId == preferredDisplayId) {
+                continue;
+            }
+
+            display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
+            if (mTmpFindTaskResult.mIdealMatch) {
+                return mTmpFindTaskResult.mRecord;
+            }
+        }
+
+        if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
+        return mTmpFindTaskResult.mRecord;
+    }
+
+    /**
+     * Finish the topmost activities in all stacks that belong to the crashed app.
+     * @param app The app that crashed.
+     * @param reason Reason to perform this action.
+     * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
+     */
+    int finishTopCrashedActivities(WindowProcessController app, String reason) {
+        TaskRecord finishedTask = null;
+        ActivityStack focusedStack = getTopDisplayFocusedStack();
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            // It is possible that request to finish activity might also remove its task and stack,
+            // so we need to be careful with indexes in the loop and check child count every time.
+            for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason);
+                if (stack == focusedStack || finishedTask == null) {
+                    finishedTask = t;
+                }
+            }
+        }
+        return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
+    }
+
+    boolean resumeFocusedStacksTopActivities() {
+        return resumeFocusedStacksTopActivities(null, null, null);
+    }
+
+    boolean resumeFocusedStacksTopActivities(
+            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
+
+        if (!mStackSupervisor.readyToResume()) {
+            return false;
+        }
+
+        if (targetStack != null && (targetStack.isTopStackOnDisplay()
+                || getTopDisplayFocusedStack() == targetStack)) {
+            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
+        }
+
+        // Resume all top activities in focused stacks on all displays.
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final ActivityStack focusedStack = display.getFocusedStack();
+            if (focusedStack == null) {
+                continue;
+            }
+            final ActivityRecord r = focusedStack.topRunningActivityLocked();
+            if (r == null || !r.isState(RESUMED)) {
+                focusedStack.resumeTopActivityUncheckedLocked(null, null);
+            } else if (r.isState(RESUMED)) {
+                // Kick off any lingering app transitions form the MoveTaskToFront operation.
+                focusedStack.executeAppTransition(targetOptions);
+            }
+        }
+
+        return false;
+    }
+
+    void applySleepTokens(boolean applyToStacks) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            // Set the sleeping state of the display.
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final boolean displayShouldSleep = display.shouldSleep();
+            if (displayShouldSleep == display.isSleeping()) {
+                continue;
+            }
+            display.setIsSleeping(displayShouldSleep);
+
+            if (!applyToStacks) {
+                continue;
+            }
+
+            // Set the sleeping state of the stacks on the display.
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (displayShouldSleep) {
+                    stack.goToSleepIfPossible(false /* shuttingDown */);
+                } else {
+                    stack.awakeFromSleepingLocked();
+                    if (stack.isFocusedStackOnDisplay()
+                            && !mStackSupervisor.getKeyguardController()
+                            .isKeyguardOrAodShowing(display.mDisplayId)) {
+                        // If the keyguard is unlocked - resume immediately.
+                        // It is possible that the display will not be awake at the time we
+                        // process the keyguard going away, which can happen before the sleep token
+                        // is released. As a result, it is important we resume the activity here.
+                        resumeFocusedStacksTopActivities();
+                    }
+                }
+            }
+
+            if (displayShouldSleep || mStackSupervisor.mGoingToSleepActivities.isEmpty()) {
+                continue;
+            }
+            // The display is awake now, so clean up the going to sleep list.
+            for (Iterator<ActivityRecord> it =
+                 mStackSupervisor.mGoingToSleepActivities.iterator(); it.hasNext(); ) {
+                final ActivityRecord r = it.next();
+                if (r.getDisplayId() == display.mDisplayId) {
+                    it.remove();
+                }
+            }
+        }
+    }
+
+    protected <T extends ActivityStack> T getStack(int stackId) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final T stack = mActivityDisplays.get(i).getStack(stackId);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    /** @see ActivityDisplay#getStack(int, int) */
+    private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    private ActivityManager.StackInfo getStackInfo(ActivityStack stack) {
+        final int displayId = stack.mDisplayId;
+        final ActivityDisplay display = getActivityDisplay(displayId);
+        ActivityManager.StackInfo info = new ActivityManager.StackInfo();
+        stack.getWindowContainerBounds(info.bounds);
+        info.displayId = displayId;
+        info.stackId = stack.mStackId;
+        info.userId = stack.mCurrentUser;
+        info.visible = stack.shouldBeVisible(null);
+        // A stack might be not attached to a display.
+        info.position = display != null ? display.getIndexOf(stack) : 0;
+        info.configuration.setTo(stack.getConfiguration());
+
+        ArrayList<TaskRecord> tasks = stack.getAllTasks();
+        final int numTasks = tasks.size();
+        int[] taskIds = new int[numTasks];
+        String[] taskNames = new String[numTasks];
+        Rect[] taskBounds = new Rect[numTasks];
+        int[] taskUserIds = new int[numTasks];
+        for (int i = 0; i < numTasks; ++i) {
+            final TaskRecord task = tasks.get(i);
+            taskIds[i] = task.taskId;
+            taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
+                    : task.realActivity != null ? task.realActivity.flattenToString()
+                    : task.getTopActivity() != null ? task.getTopActivity().packageName
+                    : "unknown";
+            taskBounds[i] = new Rect();
+            task.getWindowContainerBounds(taskBounds[i]);
+            taskUserIds[i] = task.userId;
+        }
+        info.taskIds = taskIds;
+        info.taskNames = taskNames;
+        info.taskBounds = taskBounds;
+        info.taskUserIds = taskUserIds;
+
+        final ActivityRecord top = stack.topRunningActivityLocked();
+        info.topActivity = top != null ? top.intent.getComponent() : null;
+        return info;
+    }
+
+    ActivityManager.StackInfo getStackInfo(int stackId) {
+        ActivityStack stack = getStack(stackId);
+        if (stack != null) {
+            return getStackInfo(stack);
+        }
+        return null;
+    }
+
+    ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
+        final ActivityStack stack = getStack(windowingMode, activityType);
+        return (stack != null) ? getStackInfo(stack) : null;
+    }
+
+    ArrayList<ActivityManager.StackInfo> getAllStackInfos() {
+        ArrayList<ActivityManager.StackInfo> list = new ArrayList<>();
+        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                list.add(getStackInfo(stack));
+            }
+        }
+        return list;
+    }
+
+    void deferUpdateBounds(int activityType) {
+        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+        if (stack != null) {
+            stack.deferUpdateBounds();
+        }
+    }
+
+    void continueUpdateBounds(int activityType) {
+        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+        if (stack != null) {
+            stack.continueUpdateBounds();
+        }
+    }
+
+    @Override
+    public void onDisplayAdded(int displayId) {
+        if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
+        synchronized (mService.mGlobalLock) {
+            getActivityDisplayOrCreate(displayId);
+            // Do not start home before booting, or it may accidentally finish booting before it
+            // starts. Instead, we expect home activities to be launched when the system is ready
+            // (ActivityManagerService#systemReady).
+            if (mService.isBooted() || mService.isBooting()) {
+                startHomeOnDisplay(mCurrentUser, "displayAdded", displayId);
+            }
+        }
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
+        if (displayId == DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("Can't remove the primary display.");
+        }
+
+        synchronized (mService.mGlobalLock) {
+            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+            if (activityDisplay == null) {
+                return;
+            }
+
+            activityDisplay.remove();
+        }
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+        if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
+        synchronized (mService.mGlobalLock) {
+            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+            if (activityDisplay != null) {
+                activityDisplay.onDisplayChanged();
+            }
+        }
+    }
+
+    /** Update lists of UIDs that are present on displays and have access to them. */
+    void updateUIDsPresentOnDisplay() {
+        mDisplayAccessUIDs.clear();
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+            // Only bother calculating the whitelist for private displays
+            if (activityDisplay.isPrivate()) {
+                mDisplayAccessUIDs.append(
+                        activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
+            }
+        }
+        // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
+        mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
+    }
+
+    ActivityStack findStackBehind(ActivityStack stack) {
+        final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
+        if (display != null) {
+            for (int i = display.getChildCount() - 1; i >= 0; i--) {
+                if (display.getChildAt(i) == stack && i > 0) {
+                    return display.getChildAt(i - 1);
+                }
+            }
+        }
+        throw new IllegalStateException("Failed to find a stack behind stack=" + stack
+                + " in=" + display);
+    }
+
+    @Override
+    protected int getChildCount() {
+        return mActivityDisplays.size();
+    }
+
+    @Override
+    protected ActivityDisplay getChildAt(int index) {
+        return mActivityDisplays.get(index);
+    }
+
+    @Override
+    protected ConfigurationContainer getParent() {
+        return null;
+    }
+
+    // TODO: remove after object merge with RootWindowContainer
+    void onChildPositionChanged(DisplayWindowController childController, 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);
+        }
+    }
+
+    /** Change the z-order of the given display. */
+    private void positionChildAt(ActivityDisplay display, int position) {
+        if (position >= mActivityDisplays.size()) {
+            position = mActivityDisplays.size() - 1;
+        } else if (position < 0) {
+            position = 0;
+        }
+
+        if (mActivityDisplays.isEmpty()) {
+            mActivityDisplays.add(display);
+        } else if (mActivityDisplays.get(position) != display) {
+            mActivityDisplays.remove(display);
+            mActivityDisplays.add(position, display);
+        }
+    }
+
+    @VisibleForTesting
+    void addChild(ActivityDisplay activityDisplay, int position) {
+        positionChildAt(activityDisplay, position);
+        mRootWindowContainer.positionChildAt(position,
+                activityDisplay.getWindowContainerController().mContainer);
+    }
+
+    void removeChild(ActivityDisplay activityDisplay) {
+        // The caller must tell the controller of {@link ActivityDisplay} to release its container
+        // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}).
+        mActivityDisplays.remove(activityDisplay);
+    }
+
+    Configuration getDisplayOverrideConfiguration(int displayId) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException("No display found with id: " + displayId);
+        }
+
+        return activityDisplay.getOverrideConfiguration();
+    }
+
+    void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException("No display found with id: " + displayId);
+        }
+
+        activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
+    }
+
+    void prepareForShutdown() {
+        for (int i = 0; i < mActivityDisplays.size(); i++) {
+            createSleepToken("shutdown", mActivityDisplays.get(i).mDisplayId);
+        }
+    }
+
+    ActivityTaskManagerInternal.SleepToken createSleepToken(String tag, int displayId) {
+        final ActivityDisplay display = getActivityDisplay(displayId);
+        if (display == null) {
+            throw new IllegalArgumentException("Invalid display: " + displayId);
+        }
+
+        final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
+        mSleepTokens.add(token);
+        display.mAllSleepTokens.add(token);
+        return token;
+    }
+
+    private void removeSleepToken(SleepTokenImpl token) {
+        mSleepTokens.remove(token);
+
+        final ActivityDisplay display = getActivityDisplay(token.mDisplayId);
+        if (display != null) {
+            display.mAllSleepTokens.remove(token);
+            if (display.mAllSleepTokens.isEmpty()) {
+                mService.updateSleepIfNeededLocked();
+            }
+        }
+    }
+
+    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.addStartingWindowsForVisibleActivities(taskSwitch);
+            }
+        }
+    }
+
+    void invalidateTaskLayers() {
+        mTaskLayersChanged = true;
+    }
+
+    void rankTaskLayersIfNeeded() {
+        if (!mTaskLayersChanged) {
+            return;
+        }
+        mTaskLayersChanged = false;
+        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            int baseLayer = 0;
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                baseLayer += stack.rankTaskLayers(baseLayer);
+            }
+        }
+    }
+
+    void clearOtherAppTimeTrackers(AppTimeTracker except) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.clearOtherAppTimeTrackers(except);
+            }
+        }
+    }
+
+    void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.scheduleDestroyActivities(app, reason);
+            }
+        }
+    }
+
+    void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
+        // Tasks is non-null only if two or more tasks are found.
+        ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
+        if (tasks == null) {
+            if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
+            return;
+        }
+        // If we have activities in multiple tasks that are in a position to be destroyed,
+        // let's iterate through the tasks and release the oldest one.
+        final int numDisplays = mActivityDisplays.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final int stackCount = display.getChildCount();
+            // Step through all stacks starting from behind, to hit the oldest things first.
+            for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                // Try to release activities in this stack; if we manage to, we are done.
+                if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
+                    return;
+                }
+            }
+        }
+    }
+
+    // Tries to put all activity stacks to sleep. Returns true if all stacks were
+    // successfully put to sleep.
+    boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) {
+        boolean allSleep = true;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (allowDelay) {
+                    allSleep &= stack.goToSleepIfPossible(shuttingDown);
+                } else {
+                    stack.goToSleep();
+                }
+            }
+        }
+        return allSleep;
+    }
+
+    void handleAppCrash(WindowProcessController app) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.handleAppCrash(app);
+            }
+        }
+    }
+
+    ActivityRecord findActivity(Intent intent, ActivityInfo info, boolean compareIntentFilters) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord ar = stack.findActivityLocked(
+                        intent, info, compareIntentFilters);
+                if (ar != null) {
+                    return ar;
+                }
+            }
+        }
+        return null;
+    }
+
+    boolean hasAwakeDisplay() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            if (!display.shouldSleep()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
+        return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */);
+    }
+
+    /**
+     * Returns the right stack to use for launching factoring in all the input parameters.
+     *
+     * @param r The activity we are trying to launch. Can be null.
+     * @param options The activity options used to the launch. Can be null.
+     * @param candidateTask The possible task the activity might be launched in. Can be null.
+     * @params launchParams The resolved launch params to use.
+     *
+     * @return The stack to use for the launch or INVALID_STACK_ID.
+     */
+    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
+            @Nullable LaunchParamsController.LaunchParams launchParams) {
+        int taskId = INVALID_TASK_ID;
+        int displayId = INVALID_DISPLAY;
+        //Rect bounds = null;
+
+        // We give preference to the launch preference in activity options.
+        if (options != null) {
+            taskId = options.getLaunchTaskId();
+            displayId = options.getLaunchDisplayId();
+        }
+
+        // First preference for stack goes to the task Id set in the activity options. Use the stack
+        // associated with that if possible.
+        if (taskId != INVALID_TASK_ID) {
+            // Temporarily set the task id to invalid in case in re-entry.
+            options.setLaunchTaskId(INVALID_TASK_ID);
+            final TaskRecord task = anyTaskForId(taskId,
+                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
+            options.setLaunchTaskId(taskId);
+            if (task != null) {
+                return task.getStack();
+            }
+        }
+
+        final int activityType = resolveActivityType(r, options, candidateTask);
+        T stack;
+
+        // Next preference for stack goes to the display Id set the candidate display.
+        if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
+            displayId = launchParams.mPreferredDisplayId;
+        }
+        if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
+            if (r != null) {
+                stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options,
+                        launchParams);
+                if (stack != null) {
+                    return stack;
+                }
+            }
+            final ActivityDisplay display = getActivityDisplayOrCreate(displayId);
+            if (display != null) {
+                stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+                if (stack != null) {
+                    return stack;
+                }
+            }
+        }
+
+        // Give preference to the stack and display of the input task and activity if they match the
+        // mode we want to launch into.
+        stack = null;
+        ActivityDisplay display = null;
+        if (candidateTask != null) {
+            stack = candidateTask.getStack();
+        }
+        if (stack == null && r != null) {
+            stack = r.getStack();
+        }
+        if (stack != null) {
+            display = stack.getDisplay();
+            if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
+                int windowingMode = launchParams != null ? launchParams.mWindowingMode
+                        : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+                if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+                    windowingMode = display.resolveWindowingMode(r, options, candidateTask,
+                            activityType);
+                }
+                if (stack.isCompatible(windowingMode, activityType)) {
+                    return stack;
+                }
+                if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
+                        && display.getSplitScreenPrimaryStack() == stack
+                        && candidateTask == stack.topTask()) {
+                    // This is a special case when we try to launch an activity that is currently on
+                    // top of split-screen primary stack, but is targeting split-screen secondary.
+                    // In this case we don't want to move it to another stack.
+                    // TODO(b/78788972): Remove after differentiating between preferred and required
+                    // launch options.
+                    return stack;
+                }
+            }
+        }
+
+        if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
+            display = getDefaultDisplay();
+        }
+
+        return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+    }
+
+    /** @return true if activity record is null or can be launched on provided display. */
+    private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) {
+        if (r == null) {
+            return true;
+        }
+        return r.canBeLaunchedOnDisplay(displayId);
+    }
+
+    /**
+     * Get a topmost stack on the display, that is a valid launch stack for specified activity.
+     * If there is no such stack, new dynamic stack can be created.
+     * @param displayId Target display.
+     * @param r Activity that should be launched there.
+     * @param candidateTask The possible task the activity might be put in.
+     * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
+     */
+    private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+            @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options,
+            @Nullable LaunchParamsController.LaunchParams launchParams) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException(
+                    "Display with displayId=" + displayId + " not found.");
+        }
+
+        if (!r.canBeLaunchedOnDisplay(displayId)) {
+            return null;
+        }
+
+        // If {@code r} is already in target display and its task is the same as the candidate task,
+        // the intention should be getting a launch stack for the reusable activity, so we can use
+        // the existing stack.
+        if (r.getDisplayId() == displayId && r.getTask() == candidateTask) {
+            return candidateTask.getStack();
+        }
+
+        // Return the topmost valid stack on the display.
+        for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = activityDisplay.getChildAt(i);
+            if (isValidLaunchStack(stack, r)) {
+                return stack;
+            }
+        }
+
+        // If there is no valid stack on the external display - check if new dynamic stack will do.
+        if (displayId != DEFAULT_DISPLAY) {
+            final int windowingMode;
+            if (launchParams != null) {
+                // When launch params is not null, we always defer to its windowing mode. Sometimes
+                // it could be unspecified, which indicates it should inherit windowing mode from
+                // display.
+                windowingMode = launchParams.mWindowingMode;
+            } else {
+                windowingMode = options != null ? options.getLaunchWindowingMode()
+                        : r.getWindowingMode();
+            }
+            final int activityType =
+                    options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
+                            ? options.getLaunchActivityType() : r.getActivityType();
+            return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/);
+        }
+
+        Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
+        return null;
+    }
+
+    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+            @Nullable ActivityOptions options,
+            @Nullable LaunchParamsController.LaunchParams launchParams) {
+        return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options,
+                launchParams);
+    }
+
+    // TODO: Can probably be consolidated into getLaunchStack()...
+    private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r) {
+        switch (stack.getActivityType()) {
+            case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
+            case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
+            case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+        }
+        // There is a 1-to-1 relationship between stack and task when not in
+        // primary split-windowing mode.
+        if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            return false;
+        } else {
+            return r.supportsSplitScreenWindowingMode();
+        }
+    }
+
+    int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+            @Nullable TaskRecord task) {
+        // Preference is given to the activity type for the activity then the task since the type
+        // once set shouldn't change.
+        int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+        if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
+            activityType = task.getActivityType();
+        }
+        if (activityType != ACTIVITY_TYPE_UNDEFINED) {
+            return activityType;
+        }
+        if (options != null) {
+            activityType = options.getLaunchActivityType();
+        }
+        return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD;
+    }
+
+    /**
+     * Get next focusable stack in the system. This will search through the stack on the same
+     * display as the current focused stack, looking for a focusable and visible stack, different
+     * from the target stack. If no valid candidates will be found, it will then go through all
+     * displays and stacks in last-focused order.
+     *
+     * @param currentFocus The stack that previously had focus.
+     * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
+     *                     candidate.
+     * @return Next focusable {@link ActivityStack}, {@code null} if not found.
+     */
+    ActivityStack getNextFocusableStack(@NonNull ActivityStack currentFocus,
+            boolean ignoreCurrent) {
+        // First look for next focusable stack on the same display
+        final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
+        final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
+                currentFocus, ignoreCurrent);
+        if (preferredFocusableStack != null) {
+            return preferredFocusableStack;
+        }
+        if (preferredDisplay.supportsSystemDecorations()) {
+            // Stop looking for focusable stack on other displays because the preferred display
+            // supports system decorations. Home activity would be launched on the same display if
+            // no focusable stack found.
+            return null;
+        }
+
+        // Now look through all displays
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            if (display == preferredDisplay) {
+                // We've already checked this one
+                continue;
+            }
+            final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
+                    ignoreCurrent);
+            if (nextFocusableStack != null) {
+                return nextFocusableStack;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Get next valid stack for launching provided activity in the system. This will search across
+     * displays and stacks in last-focused order for a focusable and visible stack, except those
+     * that are on a currently focused display.
+     *
+     * @param r The activity that is being launched.
+     * @param currentFocus The display that previously had focus and thus needs to be ignored when
+     *                     searching for the next candidate.
+     * @return Next valid {@link ActivityStack}, null if not found.
+     */
+    ActivityStack getNextValidLaunchStack(@NonNull ActivityRecord r, int currentFocus) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            if (display.mDisplayId == currentFocus) {
+                continue;
+            }
+            final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r,
+                    null /* options */, null /* launchParams */);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    boolean handleAppDied(WindowProcessController app) {
+        boolean hasVisibleActivities = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                hasVisibleActivities |= stack.handleAppDiedLocked(app);
+            }
+        }
+        return hasVisibleActivities;
+    }
+
+    void closeSystemDialogs() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.closeSystemDialogsLocked();
+            }
+        }
+    }
+
+    /** @return true if some activity was finished (or would have finished if doit were true). */
+    boolean finishDisabledPackageActivities(String packageName, Set<String> filterByClasses,
+            boolean doit, boolean evenPersistent, int userId) {
+        boolean didSomething = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (stack.finishDisabledPackageActivitiesLocked(
+                        packageName, filterByClasses, doit, evenPersistent, userId)) {
+                    didSomething = true;
+                }
+            }
+        }
+        return didSomething;
+    }
+
+    void updateActivityApplicationInfo(ApplicationInfo aInfo) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.updateActivityApplicationInfoLocked(aInfo);
+            }
+        }
+    }
+
+    void finishVoiceTask(IVoiceInteractionSession session) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final int numStacks = display.getChildCount();
+            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.finishVoiceTask(session);
+            }
+        }
+    }
+
+    /**
+     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+     */
+    void removeStacksInWindowingModes(int... windowingModes) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes);
+        }
+    }
+
+    void removeStacksWithActivityTypes(int... activityTypes) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes);
+        }
+    }
+
+    ActivityRecord topRunningActivity() {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
+            if (topActivity != null) {
+                return topActivity;
+            }
+        }
+        return null;
+    }
+
+    boolean allResumedActivitiesIdle() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            // TODO(b/117135575): Check resumed activities on all visible stacks.
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            if (display.isSleeping()) {
+                // No resumed activities while display is sleeping.
+                continue;
+            }
+
+            // If the focused stack is not null or not empty, there should have some activities
+            // resuming or resumed. Make sure these activities are idle.
+            final ActivityStack stack = display.getFocusedStack();
+            if (stack == null || stack.numActivities() == 0) {
+                continue;
+            }
+            final ActivityRecord resumedActivity = stack.getResumedActivity();
+            if (resumedActivity == null || !resumedActivity.idle) {
+                if (DEBUG_STATES) {
+                    Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
+                            + stack.mStackId + " " + resumedActivity + " not idle");
+                }
+                return false;
+            }
+        }
+        // Send launch end powerhint when idle
+        sendPowerHintForLaunchEndIfNeeded();
+        return true;
+    }
+
+    boolean allResumedActivitiesVisible() {
+        boolean foundResumed = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord r = stack.getResumedActivity();
+                if (r != null) {
+                    if (!r.nowVisible
+                            || mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
+                        return false;
+                    }
+                    foundResumed = true;
+                }
+            }
+        }
+        return foundResumed;
+    }
+
+    boolean allPausedActivitiesComplete() {
+        boolean pausing = true;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord r = stack.mPausingActivity;
+                if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
+                    if (DEBUG_STATES) {
+                        Slog.d(TAG_STATES,
+                                "allPausedActivitiesComplete: r=" + r + " state=" + r.getState());
+                        pausing = false;
+                    } else {
+                        return false;
+                    }
+                }
+            }
+        }
+        return pausing;
+    }
+
+    /**
+     * Find all visible task stacks containing {@param userId} and intercept them with an activity
+     * to block out the contents and possibly start a credential-confirming intent.
+     *
+     * @param userId user handle for the locked managed profile.
+     */
+    void lockAllProfileTasks(@UserIdInt int userId) {
+        mWindowManager.deferSurfaceLayout();
+        try {
+            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getChildAt(stackNdx);
+                    final List<TaskRecord> tasks = stack.getAllTasks();
+                    for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
+                        final TaskRecord task = tasks.get(taskNdx);
+
+                        // Check the task for a top activity belonging to userId, or returning a
+                        // result to an activity belonging to userId. Example case: a document
+                        // picker for personal files, opened by a work app, should still get locked.
+                        if (taskTopActivityIsUser(task, userId)) {
+                            mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
+                                    task.taskId, userId);
+                        }
+                    }
+                }
+            }
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+        }
+    }
+
+    /**
+     * Detects whether we should show a lock screen in front of this task for a locked user.
+     * <p>
+     * We'll do this if either of the following holds:
+     * <ul>
+     *   <li>The top activity explicitly belongs to {@param userId}.</li>
+     *   <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
+     * </ul>
+     *
+     * @return {@code true} if the top activity looks like it belongs to {@param userId}.
+     */
+    private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
+        // To handle the case that work app is in the task but just is not the top one.
+        final ActivityRecord activityRecord = task.getTopActivity();
+        final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
+
+        return (activityRecord != null && activityRecord.userId == userId)
+                || (resultTo != null && resultTo.userId == userId);
+    }
+
+    void cancelInitializingActivities() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.cancelInitializingActivities();
+            }
+        }
+    }
+
+    TaskRecord anyTaskForId(int id) {
+        return anyTaskForId(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
+    }
+
+    TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode) {
+        return anyTaskForId(id, matchMode, null, !ON_TOP);
+    }
+
+    /**
+     * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
+     * @param id Id of the task we would like returned.
+     * @param matchMode The mode to match the given task id in.
+     * @param aOptions The activity options to use for restoration. Can be null.
+     * @param onTop If the stack for the task should be the topmost on the display.
+     */
+    TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode,
+            @Nullable ActivityOptions aOptions, boolean onTop) {
+        // If options are set, ensure that we are attempting to actually restore a task
+        if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
+            throw new IllegalArgumentException("Should not specify activity options for non-restore"
+                    + " lookup");
+        }
+
+        int numDisplays = mActivityDisplays.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final TaskRecord task = stack.taskForIdLocked(id);
+                if (task == null) {
+                    continue;
+                }
+                if (aOptions != null) {
+                    // Resolve the stack the task should be placed in now based on options
+                    // and reparent if needed.
+                    final ActivityStack launchStack =
+                            getLaunchStack(null, aOptions, task, onTop);
+                    if (launchStack != null && stack != launchStack) {
+                        final int reparentMode = onTop
+                                ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
+                        task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
+                                "anyTaskForId");
+                    }
+                }
+                return task;
+            }
+        }
+
+        // If we are matching stack tasks only, return now
+        if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
+            return null;
+        }
+
+        // Otherwise, check the recent tasks and return if we find it there and we are not restoring
+        // the task from recents
+        if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
+        final TaskRecord task = mStackSupervisor.mRecentTasks.getTask(id);
+
+        if (task == null) {
+            if (DEBUG_RECENTS) {
+                Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
+            }
+
+            return null;
+        }
+
+        if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
+            return task;
+        }
+
+        // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+        if (!mStackSupervisor.restoreRecentTaskLocked(task, aOptions, onTop)) {
+            if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
+                    "Couldn't restore task id=" + id + " found in recents");
+            return null;
+        }
+        if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
+        return task;
+    }
+
+    ActivityRecord isInAnyStack(IBinder token) {
+        int numDisplays = mActivityDisplays.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord r = stack.isInStackLocked(token);
+                if (r != null) {
+                    return r;
+                }
+            }
+        }
+        return null;
+    }
+
+    @VisibleForTesting
+    void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
+            @WindowConfiguration.ActivityType int ignoreActivityType,
+            @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
+            boolean allowed) {
+        mStackSupervisor.mRunningTasks.getTasks(maxNum, list, ignoreActivityType,
+                ignoreWindowingMode, mActivityDisplays, callingUid, allowed);
+    }
+
+    void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
+        boolean sendHint = forceSend;
+
+        if (!sendHint) {
+            // Send power hint if we don't know what we're launching yet
+            sendHint = targetActivity == null || targetActivity.app == null;
+        }
+
+        if (!sendHint) { // targetActivity != null
+            // Send power hint when the activity's process is different than the current resumed
+            // activity on all displays, or if there are no resumed activities in the system.
+            boolean noResumedActivities = true;
+            boolean allFocusedProcessesDiffer = true;
+            for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+                final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+                final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
+                final WindowProcessController resumedActivityProcess =
+                        resumedActivity == null ? null : resumedActivity.app;
+
+                noResumedActivities &= resumedActivityProcess == null;
+                if (resumedActivityProcess != null) {
+                    allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
+                }
+            }
+            sendHint = noResumedActivities || allFocusedProcessesDiffer;
+        }
+
+        if (sendHint && mService.mPowerManagerInternal != null) {
+            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
+            mPowerHintSent = true;
+        }
+    }
+
+    void sendPowerHintForLaunchEndIfNeeded() {
+        // Trigger launch power hint if activity is launched
+        if (mPowerHintSent && mService.mPowerManagerInternal != null) {
+            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
+            mPowerHintSent = false;
+        }
+    }
+
+    private void calculateDefaultMinimalSizeOfResizeableTasks() {
+        final Resources res = mService.mContext.getResources();
+        final float minimalSize = res.getDimension(
+                com.android.internal.R.dimen.default_minimal_size_resizable_task);
+        final DisplayMetrics dm = res.getDisplayMetrics();
+
+        mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
+    }
+
+    /**
+     * Dumps the activities matching the given {@param name} in the either the focused stack
+     * or all visible stacks if {@param dumpVisibleStacks} is true.
+     */
+    ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleStacksOnly,
+            boolean dumpFocusedStackOnly) {
+        if (dumpFocusedStackOnly) {
+            return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
+        } else {
+            ArrayList<ActivityRecord> activities = new ArrayList<>();
+            int numDisplays = mActivityDisplays.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getChildAt(stackNdx);
+                    if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
+                        activities.addAll(stack.getDumpActivitiesLocked(name));
+                    }
+                }
+            }
+            return activities;
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix);
+        pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            display.dump(pw, prefix);
+        }
+    }
+
+    /**
+     * Dump all connected displays' configurations.
+     * @param prefix Prefix to apply to each line of the dump.
+     */
+    void dumpDisplayConfigs(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.println("Display override configurations:");
+        final int displayCount = mActivityDisplays.size();
+        for (int i = 0; i < displayCount; i++) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
+            pw.print(prefix); pw.print("  "); pw.print(activityDisplay.mDisplayId); pw.print(": ");
+            pw.println(activityDisplay.getOverrideConfiguration());
+        }
+    }
+
+    public void dumpDisplays(PrintWriter pw) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            pw.print("[id:" + display.mDisplayId + " stacks:");
+            display.dumpStacks(pw);
+            pw.print("]");
+        }
+    }
+
+    boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
+            String dumpPackage) {
+        boolean printed = false;
+        boolean needSep = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+            pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
+            pw.println(" (activities from top to bottom):");
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                pw.println();
+                pw.println("  Stack #" + stack.mStackId
+                        + ": type=" + activityTypeToString(stack.getActivityType())
+                        + " mode=" + windowingModeToString(stack.getWindowingMode()));
+                pw.println("  isSleeping=" + stack.shouldSleepActivities());
+                pw.println("  mBounds=" + stack.getOverrideBounds());
+
+                printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
+                        needSep);
+
+                printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, "    ", "Run", false,
+                        !dumpAll, false, dumpPackage, true,
+                        "    Running activities (most recent first):", null);
+
+                needSep = printed;
+                boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
+                        "    mPausingActivity: ");
+                if (pr) {
+                    printed = true;
+                    needSep = false;
+                }
+                pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep,
+                        "    mResumedActivity: ");
+                if (pr) {
+                    printed = true;
+                    needSep = false;
+                }
+                if (dumpAll) {
+                    pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
+                            "    mLastPausedActivity: ");
+                    if (pr) {
+                        printed = true;
+                        needSep = true;
+                    }
+                    printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
+                            needSep, "    mLastNoHistoryActivity: ");
+                }
+                needSep = printed;
+            }
+            printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
+                    " ResumedActivity:");
+        }
+
+        printed |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, "  ",
+                "Fin", false, !dumpAll,
+                false, dumpPackage, true, "  Activities waiting to finish:", null);
+        printed |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, "  ",
+                "Stop", false, !dumpAll,
+                false, dumpPackage, true, "  Activities waiting to stop:", null);
+        printed |= dumpHistoryList(fd, pw,
+                mStackSupervisor.mActivitiesWaitingForVisibleActivity, "  ", "Wait",
+                false, !dumpAll, false, dumpPackage, true,
+                "  Activities waiting for another to become visible:", null);
+        printed |= dumpHistoryList(fd, pw, mStackSupervisor.mGoingToSleepActivities,
+                "  ", "Sleep", false, !dumpAll,
+                false, dumpPackage, true, "  Activities waiting to sleep:", null);
+
+        return printed;
+    }
+
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
+        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+            activityDisplay.writeToProto(proto, DISPLAYS);
+        }
+        mStackSupervisor.getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
+        // TODO(b/111541062): Update tests to look for resumed activities on all displays
+        final ActivityStack focusedStack = getTopDisplayFocusedStack();
+        if (focusedStack != null) {
+            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+            final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
+            if (focusedActivity != null) {
+                focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+            }
+        } else {
+            proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
+        }
+        proto.write(IS_HOME_RECENTS_COMPONENT,
+                mStackSupervisor.mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+        mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
+        proto.end(token);
+    }
+
+    private final class SleepTokenImpl extends ActivityTaskManagerInternal.SleepToken {
+        private final String mTag;
+        private final long mAcquireTime;
+        private final int mDisplayId;
+
+        public SleepTokenImpl(String tag, int displayId) {
+            mTag = tag;
+            mDisplayId = displayId;
+            mAcquireTime = SystemClock.uptimeMillis();
+        }
+
+        @Override
+        public void release() {
+            synchronized (mService.mGlobalLock) {
+                removeSleepToken(this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "{\"" + mTag + "\", display " + mDisplayId
+                    + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 17e2c54..80d1368 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -82,12 +82,16 @@
 import java.util.function.Consumer;
 
 /** Root {@link WindowContainer} for the device. */
-class RootWindowContainer extends WindowContainer<DisplayContent> {
+class RootWindowContainer extends WindowContainer<DisplayContent>
+        implements ConfigurationContainerListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "RootWindowContainer" : TAG_WM;
 
     private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1;
     private static final int SET_USER_ACTIVITY_TIMEOUT = 2;
 
+    // TODO: Remove after object merge with RootActivityContainer.
+    private RootActivityContainer mRootActivityContainer;
+
     private Object mLastWindowFreezeSource = null;
     private Session mHoldScreen = null;
     private float mScreenBrightness = -1;
@@ -145,6 +149,13 @@
         mHandler = new MyHandler(service.mH.getLooper());
     }
 
+    void setRootActivityContainer(RootActivityContainer container) {
+        mRootActivityContainer = container;
+        if (container != null) {
+            container.registerConfigurationChangeListener(this);
+        }
+    }
+
     boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
         boolean changed = false;
         int topFocusedDisplayId = INVALID_DISPLAY;
@@ -189,9 +200,7 @@
                 mService.mH.sendMessage(msg);
             }
         });
-        final WindowState topFocusedWindow = getTopFocusedDisplayContent().mCurrentFocus;
-        mService.mInputManager.setFocusedWindow(
-                topFocusedWindow != null ? topFocusedWindow.mInputWindowHandle : null);
+
         return changed;
     }
 
@@ -512,9 +521,8 @@
                     if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
                             "RECOVER DESTROY", false);
                     winAnimator.destroySurface();
-                    if (winAnimator.mWin.mAppToken != null
-                            && winAnimator.mWin.mAppToken.getController() != null) {
-                        winAnimator.mWin.mAppToken.getController().removeStartingWindow();
+                    if (winAnimator.mWin.mAppToken != null) {
+                        winAnimator.mWin.mAppToken.removeStartingWindow();
                     }
                 }
 
@@ -1030,9 +1038,8 @@
     @Override
     void positionChildAt(int position, DisplayContent child, boolean includingParents) {
         super.positionChildAt(position, child, includingParents);
-        final RootWindowContainerController controller = getController();
-        if (controller != null) {
-            controller.onChildPositionChanged(child, position);
+        if (mRootActivityContainer != null) {
+            mRootActivityContainer.onChildPositionChanged(child.getController(), position);
         }
     }
 
@@ -1042,11 +1049,6 @@
     }
 
     @Override
-    RootWindowContainerController getController() {
-        return (RootWindowContainerController) super.getController();
-    }
-
-    @Override
     void scheduleAnimation() {
         mService.scheduleAnimationLocked();
     }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainerController.java b/services/core/java/com/android/server/wm/RootWindowContainerController.java
deleted file mode 100644
index 1176220..0000000
--- a/services/core/java/com/android/server/wm/RootWindowContainerController.java
+++ /dev/null
@@ -1,46 +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;
-
-/**
- * Controller for the root container. This is created by activity manager to link activity
- * stack supervisor to the root window container they use in window manager.
- */
-public class RootWindowContainerController
-        extends WindowContainerController<RootWindowContainer, RootWindowContainerListener> {
-
-    public RootWindowContainerController(RootWindowContainerListener listener) {
-        super(listener, WindowManagerService.getInstance());
-        synchronized (mGlobalLock) {
-            mRoot.setController(this);
-        }
-    }
-
-    void onChildPositionChanged(DisplayContent child, int position) {
-        // This callback invokes to AM directly so here assumes AM lock is held. If there is another
-        // path called only with WM lock, it should change to use handler to post or move outside of
-        // WM lock with adding AM lock.
-        mListener.onChildPositionChanged(child.getController(), position);
-    }
-
-    /** Move the display to the given position. */
-    public void positionChildAt(DisplayWindowController child, int position) {
-        synchronized (mGlobalLock) {
-            mContainer.positionChildAt(position, child.mContainer);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index df97027..ee0e89b 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -261,7 +261,7 @@
         try {
             mSurfaceControl = displayContent.makeOverlay()
                     .setName("ScreenshotSurface")
-                    .setSize(mWidth, mHeight)
+                    .setBufferSize(mWidth, mHeight)
                     .setSecure(isSecure)
                     .build();
 
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 95ca0a6..05f556c 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -89,13 +89,16 @@
      * Removing the transform and the result of the {@link WindowState} layout are both tied to the
      * {@link WindowState} next frame, such that they apply at the same time the client draws the
      * window in the new orientation.
+     *
+     * In the case of a rotation timeout, we want to remove the transform immediately and not defer
+     * it.
      */
-    public void finish(WindowState win) {
+    public void finish(WindowState win, boolean timeout) {
         mTransform.reset();
         final Transaction t = win.getPendingTransaction();
         t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
         t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
-        if (win.mWinAnimator.mSurfaceController != null) {
+        if (win.mWinAnimator.mSurfaceController != null && !timeout) {
             t.deferTransactionUntil(win.mSurfaceControl,
                     win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
             t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 6838c55..37b5a7c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -50,6 +50,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.view.InsetsState;
 import android.view.WindowManager;
 
 import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -153,17 +154,21 @@
     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
             Rect outStableInsets, Rect outOutsets,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
+            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
+            InsetsState outInsetsState) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
-                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
+                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
+                outInsetsState);
     }
 
     @Override
     public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
+            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
+            InsetsState outInsetsState) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                 new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */,
-                new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */);
+                new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
+                outInsetsState);
     }
 
     @Override
@@ -182,7 +187,7 @@
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
             Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface) {
+            Surface outSurface, InsetsState outInsetsState) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
@@ -190,7 +195,7 @@
                 requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                 outStableInsets, outsets, outBackdropFrame, cutout,
-                mergedConfiguration, outSurface);
+                mergedConfiguration, outSurface, outInsetsState);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index e97b366..82f2ad8 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -24,12 +23,9 @@
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.Region;
-import android.view.Display;
-import android.view.Surface.OutOfResourcesException;
 import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 class StrictModeFlash {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "StrictModeFlash" : TAG_WM;
@@ -46,7 +42,7 @@
         try {
             ctrl = dc.makeOverlay()
                     .setName("StrictModeFlash")
-                    .setSize(1, 1)
+                    .setBufferSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
             ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);  // one more than Watermark? arbitrary.
@@ -122,7 +118,7 @@
         }
         mLastDW = dw;
         mLastDH = dh;
-        mSurfaceControl.setSize(dw, dh);
+        mSurfaceControl.setBufferSize(dw, dh);
         mDrawNeeded = true;
     }
 
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 31c0c7f..11068ce 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -302,8 +302,7 @@
         if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
         final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
                 .setParent(mAnimatable.getAnimationLeashParent())
-                .setName(surface + " - animation-leash")
-                .setSize(width, height);
+                .setName(surface + " - animation-leash");
         final SurfaceControl leash = builder.build();
         t.setWindowCrop(leash, width, height);
         if (!hidden) {
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
index 0c9a14b..bbe5424 100644
--- a/services/core/java/com/android/server/wm/TEST_MAPPING
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -1,17 +1,6 @@
 {
   "presubmit": [
     {
-      "name": "CtsWindowManagerDeviceTestCases",
-      "options": [
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
-        }
-      ]
-    },
-    {
       "name": "FrameworksServicesTests",
       "options": [
         {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c9800f8..6904ef5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -302,7 +302,6 @@
 
     @Override
     void onDisplayChanged(DisplayContent dc) {
-        updateSurfaceSize(dc);
         adjustBoundsForDisplayChangeIfNeeded(dc);
         super.onDisplayChanged(dc);
     }
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 117984a..4ae2a79 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -138,7 +138,7 @@
         // STEP 1: Determine the display to launch the activity/task.
         final int displayId = getPreferredLaunchDisplay(task, options, source, currentParams);
         outParams.mPreferredDisplayId = displayId;
-        ActivityDisplay display = mSupervisor.getActivityDisplay(displayId);
+        ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay(displayId);
         if (DEBUG) {
             appendLog("display-id=" + outParams.mPreferredDisplayId + " display-windowing-mode="
                     + display.getWindowingMode());
@@ -300,12 +300,14 @@
             displayId = stack.mDisplayId;
         }
 
-        if (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) == null) {
+        if (displayId != INVALID_DISPLAY
+                && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) == null) {
             displayId = currentParams.mPreferredDisplayId;
         }
         displayId = (displayId == INVALID_DISPLAY) ? currentParams.mPreferredDisplayId : displayId;
 
-        return (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) != null)
+        return (displayId != INVALID_DISPLAY
+                && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) != null)
                 ? displayId : DEFAULT_DISPLAY;
     }
 
@@ -606,7 +608,8 @@
                 || displayBounds.height() < inOutBounds.height()) {
             // There is no way for us to fit the bounds in the display without changing width
             // or height. Just move the start to align with the display.
-            final int layoutDirection = mSupervisor.getConfiguration().getLayoutDirection();
+            final int layoutDirection =
+                    mSupervisor.mRootActivityContainer.getConfiguration().getLayoutDirection();
             final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL
                     ? displayBounds.width() - inOutBounds.width()
                     : 0;
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 8120dec..d50af38 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
 
 import android.annotation.NonNull;
 import android.graphics.Bitmap;
@@ -330,7 +330,7 @@
                                 // mWriteQueue.add(new TaskWriteQueueItem(task));
 
                                 final int taskId = task.taskId;
-                                if (mStackSupervisor.anyTaskForIdLocked(taskId,
+                                if (mService.mRootActivityContainer.anyTaskForId(taskId,
                                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
                                     // Should not happen.
                                     Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 7182ad6..b88e581 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -33,6 +33,7 @@
 import android.app.IActivityTaskManager;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Binder;
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
@@ -261,7 +262,7 @@
                 mClientChannel, mService.mAnimationHandler.getLooper(),
                 mService.mAnimator.getChoreographer());
 
-        mDragApplicationHandle = new InputApplicationHandle(null);
+        mDragApplicationHandle = new InputApplicationHandle(new Binder());
         mDragApplicationHandle.name = TAG;
         mDragApplicationHandle.dispatchingTimeoutNanos =
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
@@ -269,7 +270,7 @@
         mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
                 display.getDisplayId());
         mDragWindowHandle.name = TAG;
-        mDragWindowHandle.inputChannel = mServerChannel;
+        mDragWindowHandle.token = mServerChannel.getToken();
         mDragWindowHandle.layer = mService.getDragLayerLocked();
         mDragWindowHandle.layoutParamsFlags = 0;
         mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 51567a0..5a70325 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -21,10 +21,14 @@
 
 import android.annotation.Nullable;
 import android.app.IActivityTaskManager;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.view.Display;
+import android.view.SurfaceControl;
 import android.view.IWindow;
 
 import com.android.internal.annotations.GuardedBy;
@@ -39,10 +43,14 @@
     private final InputManagerService mInputManager;
     private final IActivityTaskManager mActivityManager;
     private final Handler mHandler;
+    private SurfaceControl mInputSurface;
+    private DisplayContent mPositioningDisplay;
 
     @GuardedBy("WindowManagerSerivce.mWindowMap")
     private @Nullable TaskPositioner mTaskPositioner;
 
+    private final Rect mTmpClipRect = new Rect();
+
     boolean isPositioningLocked() {
         return mTaskPositioner != null;
     }
@@ -59,6 +67,43 @@
         mHandler = new Handler(looper);
     }
 
+    void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+        if (mPositioningDisplay != null && mPositioningDisplay.getDisplayId() == displayId
+                && mInputSurface != null) {
+            t.hide(mInputSurface);
+        }
+    }
+
+    void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+        if (mPositioningDisplay == null || mPositioningDisplay.getDisplayId() != displayId) {
+            return;
+        }
+        final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+        if (mInputSurface == null) {
+            mInputSurface = mService.makeSurfaceBuilder(dc.getSession())
+                    .setContainerLayer(true)
+                    .setName("Drag and Drop Input Consumer").build();
+        }
+
+        final InputWindowHandle h = getDragWindowHandleLocked();
+        if (h == null) {
+            Slog.w(TAG_WM, "Drag is in progress but there is no "
+                    + "drag window handle.");
+            return;
+        }
+
+        t.show(mInputSurface);
+        t.setInputWindowInfo(mInputSurface, h);
+        t.setLayer(mInputSurface, Integer.MAX_VALUE);
+
+        final Display display = dc.getDisplay();
+        final Point p = new Point();
+        display.getRealSize(p);
+
+        mTmpClipRect.set(0, 0, p.x, p.y);
+        t.setWindowCrop(mInputSurface, mTmpClipRect);
+    }
+
     boolean startMovingTask(IWindow window, float startX, float startY) {
         WindowState win = null;
         synchronized (mService.mGlobalLock) {
@@ -122,6 +167,7 @@
             Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
             return false;
         }
+        mPositioningDisplay = displayContent;
 
         mTaskPositioner = TaskPositioner.create(mService);
         mTaskPositioner.register(displayContent);
@@ -157,6 +203,7 @@
                     mTaskPositioner.unregister();
                     mTaskPositioner = null;
                 }
+                mPositioningDisplay = null;
             }
         });
     }
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index eec10ab..8a3dbad 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -472,8 +472,8 @@
         }
         mResizeMode = resizeMode;
         mWindowContainerController.setResizeable(resizeMode);
-        mService.mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+        mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
     }
 
     void setTaskDockedResizing(boolean resizing) {
@@ -544,10 +544,9 @@
                     // this won't cause tons of irrelevant windows being preserved because only
                     // activities in this task may experience a bounds change. Configs for other
                     // activities stay the same.
-                    mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
-                            preserveWindow);
+                    mService.mRootActivityContainer.ensureActivitiesVisible(r, 0, preserveWindow);
                     if (!kept) {
-                        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                        mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
                     }
                 }
             }
@@ -623,6 +622,7 @@
             @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
             boolean schedulePictureInPictureModeChange, String reason) {
         final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
+        final RootActivityContainer root = mService.mRootActivityContainer;
         final WindowManagerService windowManager = mService.mWindowManager;
         final ActivityStack sourceStack = getStack();
         final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack,
@@ -655,7 +655,7 @@
         boolean kept = true;
         try {
             final ActivityRecord r = topRunningActivityLocked();
-            final boolean wasFocused = r != null && supervisor.isTopDisplayFocusedStack(sourceStack)
+            final boolean wasFocused = r != null && root.isTopDisplayFocusedStack(sourceStack)
                     && (topRunningActivityLocked() == r);
             final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r;
             final boolean wasPaused = r != null && sourceStack.mPausingActivity == r;
@@ -748,8 +748,8 @@
         if (!deferResume) {
             // The task might have already been running and its visibility needs to be synchronized
             // with the visibility of the stack / windows.
-            supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
-            supervisor.resumeFocusedStacksTopActivitiesLocked();
+            root.ensureActivitiesVisible(null, 0, !mightReplaceWindow);
+            root.resumeFocusedStacksTopActivities();
         }
 
         // TODO: Handle incorrect request to move before the actual move, not after.
@@ -982,7 +982,7 @@
     @Override
     protected void onParentChanged() {
         super.onParentChanged();
-        mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+        mService.mRootActivityContainer.updateUIDsPresentOnDisplay();
     }
 
     // Close up recents linked list.
@@ -1143,7 +1143,7 @@
     }
 
     boolean okToShowLocked() {
-        // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
+        // NOTE: If {@link TaskRecord#topRunningActivity} return is not null then it is
         // okay to show the activity when locked.
         return mService.mStackSupervisor.isCurrentProfileLocked(userId)
                 || topRunningActivityLocked() != null;
@@ -1182,7 +1182,7 @@
         mActivities.add(newTop);
 
         // Make sure window manager is aware of the position change.
-        mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
+        mWindowContainerController.positionChildAtTop(newTop.mAppWindowToken);
         updateEffectiveIntent();
 
         setFrontOfTask();
@@ -1264,17 +1264,15 @@
             mService.notifyTaskPersisterLocked(this, false);
         }
 
-        // Sync. with window manager
-        final AppWindowContainerController appController = r.getWindowContainerController();
-        if (appController != null) {
+        if (r.mAppWindowToken != null) {
             // Only attempt to move in WM if the child has a controller. It is possible we haven't
             // created controller for the activity we are starting yet.
-            mWindowContainerController.positionChildAt(appController, index);
+            mWindowContainerController.positionChildAt(r.mAppWindowToken, index);
         }
 
         // Make sure the list of display UID whitelists is updated
         // now that this record is in a new task.
-        mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+        mService.mRootActivityContainer.updateUIDsPresentOnDisplay();
     }
 
     /**
@@ -1683,9 +1681,9 @@
         // to do this for the pinned stack as the bounds are controlled by the system.
         if (!inPinnedWindowingMode()) {
             final int defaultMinSizeDp =
-                    mService.mStackSupervisor.mDefaultMinSizeOfResizeableTaskDp;
+                    mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp;
             final ActivityDisplay display =
-                    mService.mStackSupervisor.getActivityDisplay(mStack.mDisplayId);
+                    mService.mRootActivityContainer.getActivityDisplay(mStack.mDisplayId);
             final float density =
                     (float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
             final int defaultMinSize = (int) (defaultMinSizeDp * density);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index a7b0272..9a56606 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -32,6 +32,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+
 import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.getColorViewLeftInset;
@@ -65,6 +66,7 @@
 import android.view.SurfaceSession;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
+import android.view.InsetsState;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -141,6 +143,7 @@
         final Rect taskBounds;
         final Rect tmpContentInsets = new Rect();
         final Rect tmpStableInsets = new Rect();
+        final InsetsState mTmpInsetsState = new InsetsState();
         final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
         int backgroundColor = WHITE;
         int statusBarColor = 0;
@@ -201,7 +204,7 @@
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
                     View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect,
-                    tmpRect, tmpCutout, null);
+                    tmpRect, tmpCutout, null, mTmpInsetsState);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                 return null;
@@ -217,7 +220,7 @@
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                     tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
-                    tmpCutout, tmpMergedConfiguration, surface);
+                    tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState);
         } catch (RemoteException e) {
             // Local call.
         }
@@ -312,7 +315,7 @@
         // Keep a reference to it such that it doesn't get destroyed when finalized.
         mChildSurfaceControl = new SurfaceControl.Builder(session)
                 .setName(mTitle + " - task-snapshot-surface")
-                .setSize(buffer.getWidth(), buffer.getHeight())
+                .setBufferSize(buffer.getWidth(), buffer.getHeight())
                 .setFormat(buffer.getFormat())
                 .build();
         Surface surface = new Surface();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 64f4ba5..5deb4f1 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -248,7 +248,6 @@
         getRawBounds(mTmpRect);
         final Rect stackBounds = getBounds();
         getPendingTransaction()
-                .setSize(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
                 .setWindowCrop(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
                 .setPosition(mAnimationBackgroundSurface, mTmpRect.left - stackBounds.left,
                         mTmpRect.top - stackBounds.top);
@@ -751,7 +750,6 @@
         if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
             return;
         }
-        transaction.setSize(mSurfaceControl, width, height);
         transaction.setWindowCrop(mSurfaceControl, width, height);
         mLastSurfaceSize.set(width, height);
     }
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 59b2055..ec64d2e 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -16,6 +16,14 @@
 
 package com.android.server.wm;
 
+import static com.android.server.EventLogTags.WM_TASK_CREATED;
+import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Rect;
@@ -24,18 +32,11 @@
 import android.os.Message;
 import android.util.EventLog;
 import android.util.Slog;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.ref.WeakReference;
 
-import static com.android.server.EventLogTags.WM_TASK_CREATED;
-import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
 /**
  * Controller for the task container. This is created by activity manager to link task records to
  * the task container they use in window manager.
@@ -103,16 +104,15 @@
         }
     }
 
-    public void positionChildAtTop(AppWindowContainerController childController) {
-        positionChildAt(childController, POSITION_TOP);
+    void positionChildAtTop(AppWindowToken aToken) {
+        positionChildAt(aToken, POSITION_TOP);
     }
 
-    public void positionChildAt(AppWindowContainerController childController, int position) {
+    void positionChildAt(AppWindowToken aToken, int position) {
         synchronized (mService.mGlobalLock) {
-            final AppWindowToken aToken = childController.mContainer;
             if (aToken == null) {
                 Slog.w(TAG_WM,
-                        "Attempted to position of non-existing app : " + childController);
+                        "Attempted to position of non-existing app");
                 return;
             }
 
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 9216b66..e6ac059 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -20,19 +20,18 @@
 
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Typeface;
-import android.graphics.Paint.FontMetricsInt;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.Display;
-import android.view.Surface.OutOfResourcesException;
 import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 /**
  * Displays a watermark on top of the window manager's windows.
@@ -116,7 +115,7 @@
         try {
             ctrl = dc.makeOverlay()
                     .setName("WatermarkSurface")
-                    .setSize(1, 1)
+                    .setBufferSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
             ctrl.setLayerStack(mDisplay.getLayerStack());
@@ -133,7 +132,7 @@
         if (mLastDW != dw || mLastDH != dh) {
             mLastDW = dw;
             mLastDH = dh;
-            mSurfaceControl.setSize(dw, dh);
+            mSurfaceControl.setBufferSize(dw, dh);
             mDrawNeeded = true;
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 449c409..b8a0739 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -260,8 +258,7 @@
             if (DEBUG_WINDOW_TRACE) {
                 Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
                         + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
-                        + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
-                        + Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY)));
+                        + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 266006d..7e4c629 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -515,24 +515,6 @@
         }
     }
 
-    /**
-     * Update the surface size when display changed in order to avoid children being bound by the
-     * old display size.
-     *
-     * Note that we don't want to apply this to all layers, but only limiting this to layers that
-     * don't set their own size ({@link Task}, {@link WindowState} and {@link WindowToken}).
-     */
-    void updateSurfaceSize(DisplayContent dc) {
-        if (mSurfaceControl == null) {
-            return;
-        }
-
-        final int newSurfaceSize = dc.getSurfaceSize();
-        if (mSurfaceControl.getWidth() != newSurfaceSize) {
-            getPendingTransaction().setSize(mSurfaceControl, newSurfaceSize, newSurfaceSize);
-        }
-    }
-
     void setWaitingForDrawnIfResizingChanged() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 39a8465..b91afcd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -218,6 +218,7 @@
 import android.view.SurfaceSession;
 import android.view.View;
 import android.view.WindowContentFrameStats;
+import android.view.InsetsState;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.RemoveContentMode;
@@ -599,7 +600,6 @@
     long mDisplayFreezeTime = 0;
     int mLastDisplayFreezeDuration = 0;
     Object mLastFinishedFreezeSource = null;
-    boolean mWaitingForConfig = false;
     boolean mSwitchingUser = false;
 
     final static int WINDOWS_FREEZING_SCREENS_NONE = 0;
@@ -1112,7 +1112,8 @@
     public int addWindow(Session session, IWindow client, int seq,
             LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
             Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
+            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
+            InsetsState outInsetsState) {
         int[] appOp = new int[1];
         int res = mPolicy.checkAddPermission(attrs, appOp);
         if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -1460,6 +1461,7 @@
                     outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
             }
+            outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
 
             if (mInTouchMode) {
                 res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
@@ -1857,7 +1859,7 @@
             long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface) {
+            Surface outSurface, InsetsState outInsetsState) {
         int result = 0;
         boolean configChanged;
         final boolean hasStatusBarPermission =
@@ -1870,7 +1872,7 @@
         long origId = Binder.clearCallingIdentity();
         final int displayId;
         synchronized (mGlobalLock) {
-            WindowState win = windowForClientLocked(session, client, false);
+            final WindowState win = windowForClientLocked(session, client, false);
             if (win == null) {
                 return 0;
             }
@@ -1885,8 +1887,9 @@
 
             win.setFrameNumber(frameNumber);
 
-            if (!mWaitingForConfig) {
-                win.finishSeamlessRotation();
+            final DisplayContent dc = win.getDisplayContent();
+            if (!dc.mWaitingForConfig) {
+                win.finishSeamlessRotation(false /* timeout */);
             }
 
             int attrChanges = 0;
@@ -2157,6 +2160,7 @@
                     outStableInsets, outOutsets);
             outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
             outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
+            outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
             if (localLOGV) Slog.v(
                 TAG_WM, "Relayout given client " + client.asBinder()
                 + ", requestedWidth=" + requestedWidth
@@ -2438,7 +2442,7 @@
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             displayContent.computeScreenConfiguration(mTempConfiguration);
             if (currentConfig.diff(mTempConfiguration) != 0) {
-                mWaitingForConfig = true;
+                displayContent.mWaitingForConfig = true;
                 displayContent.setLayoutNeeded();
                 int anim[] = new int[2];
                 displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
@@ -2451,9 +2455,10 @@
         return config;
     }
 
-    void setNewDisplayOverrideConfiguration(Configuration overrideConfig, DisplayContent dc) {
-        if (mWaitingForConfig) {
-            mWaitingForConfig = false;
+    void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
+            @NonNull DisplayContent dc) {
+        if (dc.mWaitingForConfig) {
+            dc.mWaitingForConfig = false;
             mLastFinishedFreezeSource = "new-config";
         }
 
@@ -2474,26 +2479,36 @@
     @Override
     public void overridePendingAppTransitionMultiThumbFuture(
             IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
-            boolean scaleUp) {
+            boolean scaleUp, int displayId) {
         synchronized (mGlobalLock) {
-            // TODO(multi-display): sysui using this api only support default display.
-            mRoot.getDisplayContent(DEFAULT_DISPLAY)
-                    .mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture,
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent == null) {
+                Slog.w(TAG, "Attempted to call overridePendingAppTransitionMultiThumbFuture"
+                        + " for the display " + displayId + " that does not exist.");
+                return;
+            }
+            displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture,
                     callback, scaleUp);
         }
     }
 
     @Override
-    public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
+    public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter,
+            int displayId) {
         if (!checkCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
                 "overridePendingAppTransitionRemote()")) {
             throw new SecurityException(
                     "Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission");
         }
         synchronized (mGlobalLock) {
-            // TODO(multi-display): sysui using this api only support default display.
-            mRoot.getDisplayContent(DEFAULT_DISPLAY)
-                    .mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter);
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent == null) {
+                Slog.w(TAG, "Attempted to call overridePendingAppTransitionRemote"
+                        + " for the display " + displayId + " that does not exist.");
+                return;
+            }
+            displayContent.mAppTransition.overridePendingAppTransitionRemote(
+                    remoteAnimationAdapter);
         }
     }
 
@@ -4181,13 +4196,11 @@
                 // placement to unfreeze the display since we froze it when the rotation was updated
                 // in DisplayContent#updateRotationUnchecked.
                 synchronized (mGlobalLock) {
-                    if (mWaitingForConfig) {
-                        mWaitingForConfig = false;
+                    final DisplayContent dc = mRoot.getDisplayContent(displayId);
+                    if (dc != null && dc.mWaitingForConfig) {
+                        dc.mWaitingForConfig = false;
                         mLastFinishedFreezeSource = "config-unchanged";
-                        final DisplayContent dc = mRoot.getDisplayContent(displayId);
-                        if (dc != null) {
-                            dc.setLayoutNeeded();
-                        }
+                        dc.setLayoutNeeded();
                         mWindowPlacerLocked.performSurfacePlacement();
                     }
                 }
@@ -5136,7 +5149,7 @@
         configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0;
 
         if (configChanged) {
-            mWaitingForConfig = true;
+            displayContent.mWaitingForConfig = true;
             startFreezingDisplayLocked(0 /* exitAnim */,
                     0 /* enterAnim */, displayContent);
             displayContent.sendNewConfiguration();
@@ -5396,23 +5409,24 @@
             return;
         }
 
-        final DisplayContent dc = mRoot.getDisplayContent(mFrozenDisplayId);
-        if (mWaitingForConfig || mAppsFreezingScreen > 0
+        final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
+        final boolean waitingForConfig = displayContent != null && displayContent.mWaitingForConfig;
+        final int numOpeningApps = displayContent != null ? displayContent.mOpeningApps.size() : 0;
+        if (waitingForConfig || mAppsFreezingScreen > 0
                 || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
-                || mClientFreezingScreen || (dc != null && !dc.mOpeningApps.isEmpty())) {
+                || mClientFreezingScreen || numOpeningApps > 0) {
             if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
-                "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig
+                "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + waitingForConfig
                 + ", mAppsFreezingScreen=" + mAppsFreezingScreen
                 + ", mWindowsFreezingScreen=" + mWindowsFreezingScreen
                 + ", mClientFreezingScreen=" + mClientFreezingScreen
-                + ", mOpeningApps.size()=" + (dc != null ? dc.mOpeningApps.size() : 0));
+                + ", mOpeningApps.size()=" + numOpeningApps);
             return;
         }
 
         if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
                 "stopFreezingDisplayLocked: Unfreezing now");
 
-        final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
 
         // We must make a local copy of the displayId as it can be potentially overwritten later on
         // in this method. For example, {@link startFreezingDisplayLocked} may be called as a result
@@ -5707,8 +5721,14 @@
     }
 
     @Override
-    public boolean hasNavigationBar() {
-        return mPolicy.hasNavigationBar();
+    public boolean hasNavigationBar(int displayId) {
+        synchronized (mGlobalLock) {
+            final DisplayContent dc = mRoot.getDisplayContent(displayId);
+            if (dc == null) {
+                return false;
+            }
+            return dc.getDisplayPolicy().hasNavigationBar();
+        }
     }
 
     @Override
@@ -6033,7 +6053,6 @@
                     pw.print(" windows="); pw.print(mWindowsFreezingScreen);
                     pw.print(" client="); pw.print(mClientFreezingScreen);
                     pw.print(" apps="); pw.print(mAppsFreezingScreen);
-                    pw.print(" waitingForConfig="); pw.println(mWaitingForConfig);
             final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
             pw.print("  mRotation="); pw.print(defaultDisplayContent.getRotation());
                     pw.print(" mAltOrientation=");
@@ -6042,6 +6061,9 @@
                     pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
                     pw.print(" mLastOrientation=");
                             pw.println(defaultDisplayContent.getLastOrientation());
+                    pw.print(" waitingForConfig=");
+                            pw.println(defaultDisplayContent.mWaitingForConfig);
+
             pw.print("  Animation settings: disabled="); pw.print(mAnimationsDisabled);
                     pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
                     pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 484bd8c..578af2e 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -755,9 +755,9 @@
             return;
         }
         final ActivityDisplay activityDisplay =
-                mAtm.mStackSupervisor.getActivityDisplay(mDisplayId);
+                mAtm.mRootActivityContainer.getActivityDisplay(mDisplayId);
         if (activityDisplay != null) {
-            mAtm.mStackSupervisor.getActivityDisplay(
+            mAtm.mRootActivityContainer.getActivityDisplay(
                     mDisplayId).unregisterConfigurationChangeListener(this);
         }
         mDisplayId = INVALID_DISPLAY;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 567b583..cfd1f86 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -143,6 +143,7 @@
 import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
 
 import android.annotation.CallSuper;
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -180,6 +181,7 @@
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
+import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
@@ -193,6 +195,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.policy.WindowManagerPolicy.DisplayContentInfo;
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 import com.android.server.wm.utils.InsetUtils;
 import com.android.server.wm.utils.WmDisplayCutout;
@@ -577,10 +580,12 @@
      */
     private boolean mIsDimming = false;
 
+    private @Nullable InsetsSourceProvider mInsetProvider;
+
     private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
 
-    void seamlesslyRotateIfAllowed(Transaction transaction, int oldRotation, int rotation,
-            boolean requested) {
+    void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
+            @Rotation int rotation, boolean requested) {
         // Invisible windows and the wallpaper do not participate in the seamless rotation animation
         if (!isVisibleNow() || mIsWallpaper) {
             return;
@@ -597,9 +602,9 @@
         }
     }
 
-    void finishSeamlessRotation() {
+    void finishSeamlessRotation(boolean timeout) {
         if (mPendingSeamlessRotate != null) {
-            mPendingSeamlessRotate.finish(this);
+            mPendingSeamlessRotate.finish(this, timeout);
             mFinishSeamlessRotateFrameNumber = getFrameNumber();
             mPendingSeamlessRotate = null;
             mService.markForSeamlessRotation(this, false);
@@ -1260,7 +1265,6 @@
 
     @Override
     void onDisplayChanged(DisplayContent dc) {
-        updateSurfaceSize(dc);
         super.onDisplayChanged(dc);
         // Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
         if (dc != null) {
@@ -2036,7 +2040,7 @@
         InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
         mInputChannel = inputChannels[0];
         mClientChannel = inputChannels[1];
-        mInputWindowHandle.inputChannel = inputChannels[0];
+        mInputWindowHandle.token = mClient.asBinder();
         if (outInputChannel != null) {
             mClientChannel.transferTo(outInputChannel);
             mClientChannel.dispose();
@@ -2067,7 +2071,7 @@
             mClientChannel.dispose();
             mClientChannel = null;
         }
-        mInputWindowHandle.inputChannel = null;
+        mInputWindowHandle.token = null;
     }
 
     /** Returns true if the replacement window was removed. */
@@ -2166,11 +2170,15 @@
                 mTmpRect.inset(-delta, -delta);
             }
             region.set(mTmpRect);
-            cropRegionToStackBoundsIfNeeded(region);
+            region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
         } else {
             // Not modal or full screen modal
             getTouchableRegion(region);
         }
+
+        // The area containing the shadows is not touchable.
+        region.translate(mAttrs.surfaceInsets.left, mAttrs.surfaceInsets.top);
+
         return flags;
     }
 
@@ -2391,11 +2399,11 @@
                 && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
                 && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
                 && (mAppToken == null || mAppToken.windowsAreFocusable())
-                && !canReceiveTouchInput();
+                && !cantReceiveTouchInput();
     }
 
-    /** @return true if this window desires touch events. */
-    boolean canReceiveTouchInput() {
+    /** @return false if this window desires touch events. */
+    boolean cantReceiveTouchInput() {
         return mAppToken != null && mAppToken.getTask() != null
                 && mAppToken.getTask().mStack.shouldIgnoreInput();
     }
@@ -2802,25 +2810,32 @@
     }
 
     void getTouchableRegion(Region outRegion) {
+        if (inPinnedWindowingMode() && !isFocused()) {
+            outRegion.setEmpty();
+            return;
+        }
+
         final Rect frame = mWindowFrames.mFrame;
         switch (mTouchableInsets) {
             default:
             case TOUCHABLE_INSETS_FRAME:
                 outRegion.set(frame);
+                outRegion.translate(-frame.left, -frame.top);
                 break;
             case TOUCHABLE_INSETS_CONTENT:
                 applyInsets(outRegion, frame, mGivenContentInsets);
+                outRegion.translate(-frame.left, -frame.top);
                 break;
             case TOUCHABLE_INSETS_VISIBLE:
                 applyInsets(outRegion, frame, mGivenVisibleInsets);
+                outRegion.translate(-frame.left, -frame.top);
                 break;
             case TOUCHABLE_INSETS_REGION: {
                 outRegion.set(mGivenTouchableRegion);
-                outRegion.translate(frame.left, frame.top);
                 break;
             }
         }
-        cropRegionToStackBoundsIfNeeded(outRegion);
+        outRegion.translate(mAttrs.surfaceInsets.left, mAttrs.surfaceInsets.top);
     }
 
     private void cropRegionToStackBoundsIfNeeded(Region region) {
@@ -2944,6 +2959,18 @@
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
+    /**
+     * Called when the insets state changed.
+     */
+    void notifyInsetsChanged() {
+        try {
+            mClient.insetsChanged(
+                    getDisplayContent().getInsetsStateController().getInsetsForDispatch(this));
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to deliver inset state change", e);
+        }
+    }
+
     Rect getBackdropFrame(Rect frame) {
         // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
         // start even if we haven't received the relayout window, so that the client requests
@@ -4765,6 +4792,14 @@
         mWindowFrames.setContentChanged(false);
     }
 
+    void setInsetProvider(InsetsSourceProvider insetProvider) {
+        mInsetProvider = insetProvider;
+    }
+
+    InsetsSourceProvider getInsetProvider() {
+        return mInsetProvider;
+    }
+
     private final class MoveAnimationSpec implements AnimationSpec {
 
         private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e090cc5..78a3fe5 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -861,7 +861,7 @@
         // to find the surface size changed underneath it.
         final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
         if (relayout) {
-            mSurfaceResized = mSurfaceController.setSizeInTransaction(
+            mSurfaceResized = mSurfaceController.setBufferSizeInTransaction(
                     mTmpSize.width(), mTmpSize.height(), recoveringMemory);
         } else {
             mSurfaceResized = false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 6821e94..ce627e2 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -19,34 +19,28 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW;
 
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowSurfaceControllerProto.LAYER;
 import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
 
-import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.IBinder;
 import android.os.Debug;
+import android.os.IBinder;
 import android.os.Trace;
+import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.WindowContentFrameStats;
-import android.view.Surface.OutOfResourcesException;
 
-import android.util.Slog;
-
-import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 
 class WindowSurfaceController {
     static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfaceController" : TAG_WM;
@@ -106,7 +100,7 @@
         final SurfaceControl.Builder b = win.makeSurface()
                 .setParent(win.getSurfaceControl())
                 .setName(name)
-                .setSize(w, h)
+                .setBufferSize(w, h)
                 .setFormat(format)
                 .setFlags(flags)
                 .setMetadata(windowType, ownerUid);
@@ -303,7 +297,7 @@
         }
     }
 
-    boolean setSizeInTransaction(int width, int height, boolean recoveringMemory) {
+    boolean setBufferSizeInTransaction(int width, int height, boolean recoveringMemory) {
         final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
         if (surfaceResized) {
             mSurfaceW = width;
@@ -312,7 +306,7 @@
             try {
                 if (SHOW_TRANSACTIONS) logSurface(
                         "SIZE " + width + "x" + height, null);
-                mSurfaceControl.setSize(width, height);
+                mSurfaceControl.setBufferSize(width, height);
             } catch (RuntimeException e) {
                 // If something goes wrong with the surface (such
                 // as running out of memory), don't take down the
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7193dd7..2ee58fe 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -113,7 +113,9 @@
             return;
         }
 
-        if (mService.mWaitingForConfig) {
+        // TODO(multi-display):
+        final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
+        if (defaultDisplay.mWaitingForConfig) {
             // Our configuration has changed (most likely rotation), but we
             // don't yet have the complete configuration to report to
             // applications.  Don't do any window layout until we have it.
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 0cf79b6..d8242f8 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -265,7 +265,6 @@
         // to another display before the window behind
         // it is ready.
 
-        updateSurfaceSize(dc);
         super.onDisplayChanged(dc);
     }
 
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index ee8a08b..fcd9335 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -86,6 +86,7 @@
     jmethodID notifySwitch;
     jmethodID notifyInputChannelBroken;
     jmethodID notifyANR;
+    jmethodID notifyFocusChanged;
     jmethodID filterInputEvent;
     jmethodID interceptKeyBeforeQueueing;
     jmethodID interceptMotionBeforeQueueingNonInteractive;
@@ -147,15 +148,6 @@
     return value ? "true" : "false";
 }
 
-static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
-        const sp<InputApplicationHandle>& inputApplicationHandle) {
-    if (inputApplicationHandle == nullptr) {
-        return nullptr;
-    }
-    return static_cast<NativeInputApplicationHandle*>(inputApplicationHandle.get())->
-            getInputApplicationHandleObjLocalRef(env);
-}
-
 static void loadSystemIconAsSpriteWithPointerIcon(JNIEnv* env, jobject contextObj, int32_t style,
         PointerIcon* outPointerIcon, SpriteIcon* outSpriteIcon) {
     status_t status = android_view_PointerIcon_loadSystemIcon(env,
@@ -249,6 +241,7 @@
             const sp<IBinder>& token,
             const std::string& reason);
     virtual void notifyInputChannelBroken(const sp<IBinder>& token);
+    virtual void notifyFocusChanged(const sp<IBinder>& token);
     virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
@@ -342,6 +335,8 @@
     mInteractive = true;
 
     mInputManager = new InputManager(this, this);
+    defaultServiceManager()->addService(String16("inputflinger"),
+            mInputManager, false);
 }
 
 NativeInputManager::~NativeInputManager() {
@@ -656,13 +651,11 @@
 
     JNIEnv* env = jniEnv();
 
-    jobject inputApplicationHandleObj =
-            getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
     jobject tokenObj = javaObjectForIBinder(env, token);
     jstring reasonObj = env->NewStringUTF(reason.c_str());
 
     jlong newTimeout = env->CallLongMethod(mServiceObj,
-                gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj,
+                gServiceClassInfo.notifyANR, tokenObj,
                 reasonObj);
     if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
         newTimeout = 0; // abort dispatch
@@ -671,7 +664,6 @@
     }
 
     env->DeleteLocalRef(reasonObj);
-    env->DeleteLocalRef(inputApplicationHandleObj);
     return newTimeout;
 }
 
@@ -691,6 +683,22 @@
     }
 }
 
+void NativeInputManager::notifyFocusChanged(const sp<IBinder>& token) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    ALOGD("notifyFocusChanged");
+#endif
+    ATRACE_CALL();
+
+    JNIEnv* env = jniEnv();
+
+    jobject tokenObj = javaObjectForIBinder(env, token);
+    if (tokenObj) {
+        env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyFocusChanged,
+                tokenObj);
+        checkAndClearExceptionFromCallback(env, "notifyFocusChanged");
+    }
+}
+
 void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
     ATRACE_CALL();
     JNIEnv* env = jniEnv();
@@ -1715,10 +1723,13 @@
 
     GET_METHOD_ID(gServiceClassInfo.notifyInputChannelBroken, clazz,
             "notifyInputChannelBroken", "(Landroid/os/IBinder;)V");
+    
+    GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz,
+            "notifyFocusChanged", "(Landroid/os/IBinder;)V");
 
     GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
             "notifyANR",
-            "(Landroid/view/InputApplicationHandle;Landroid/os/IBinder;Ljava/lang/String;)J");
+            "(Landroid/os/IBinder;Ljava/lang/String;)J");
 
     GET_METHOD_ID(gServiceClassInfo.filterInputEvent, clazz,
             "filterInputEvent", "(Landroid/view/InputEvent;I)Z");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
new file mode 100644
index 0000000..05912a5
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
@@ -0,0 +1,268 @@
+/*
+ * 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.devicepolicy;
+
+import android.app.admin.DevicePolicyManager.InstallUpdateCallback;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.UpdateEngine;
+import android.os.UpdateEngineCallback;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+
+/**
+ * Used for installing an update on <a href="https://source.android.com/devices/tech/ota/ab">AB
+ * devices.</a>
+ * <p>This logic is specific to GOTA and should be modified by OEMs using a different AB update
+ * system.</p>
+ */
+class AbUpdateInstaller extends UpdateInstaller {
+    private static final String PAYLOAD_BIN = "payload.bin";
+    private static final String PAYLOAD_PROPERTIES_TXT = "payload_properties.txt";
+    //https://en.wikipedia.org/wiki/Zip_(file_format)#Local_file_header
+    private static final int OFFSET_TO_FILE_NAME = 30;
+    // kDownloadStateInitializationError constant from system/update_engine/common/error_code.h.
+    private static final int DOWNLOAD_STATE_INITIALIZATION_ERROR = 20;
+    private long mSizeForUpdate;
+    private long mOffsetForUpdate;
+    private List<String> mProperties;
+    private Enumeration<? extends ZipEntry> mEntries;
+    private ZipFile mPackedUpdateFile;
+    private static final Map<Integer, Integer> errorCodesMap = buildErrorCodesMap();
+    private static final Map<Integer, String> errorStringsMap = buildErrorStringsMap();
+    public static final String UNKNOWN_ERROR = "Unknown error with error code = ";
+    private boolean mUpdateInstalled;
+
+    private static Map<Integer, Integer> buildErrorCodesMap() {
+        Map<Integer, Integer> map = new HashMap<>();
+        map.put(UpdateEngine.ErrorCodeConstants.ERROR, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        map.put(
+                DOWNLOAD_STATE_INITIALIZATION_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
+
+        // Error constants corresponding to errors related to bad update file.
+        map.put(
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+
+        // Error constants corresponding to errors related to devices bad state.
+        map.put(
+                UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+
+        return map;
+    }
+
+    private static Map<Integer, String> buildErrorStringsMap() {
+        Map<Integer, String> map = new HashMap<>();
+        map.put(UpdateEngine.ErrorCodeConstants.ERROR, UNKNOWN_ERROR);
+        map.put(
+                DOWNLOAD_STATE_INITIALIZATION_ERROR,
+                "The delta update payload was targeted for another version or the source partition"
+                        + "was modified after it was installed");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+                "Failed to finish the configured postinstall works.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+                "Failed to open one of the partitions it tried to write to or read data from.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+                "Payload mismatch error.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+                "Failed to read the payload data from the given URL.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR, "Payload hash error.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+                "Payload size mismatch error.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+                "Failed to verify the signature of the payload.");
+        map.put(
+                UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+                "The payload has been successfully installed,"
+                        + "but the active slot was not flipped.");
+        return map;
+    }
+
+    AbUpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor,
+            StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+            DevicePolicyConstants constants) {
+        super(context, updateFileDescriptor, callback, injector, constants);
+        mUpdateInstalled = false;
+    }
+
+    @Override
+    public void installUpdateInThread() {
+        if (mUpdateInstalled) {
+            throw new IllegalStateException("installUpdateInThread can be called only once.");
+        }
+        try {
+            setState();
+            applyPayload(Paths.get(mCopiedUpdateFile.getAbsolutePath()).toUri().toString());
+        } catch (ZipException e) {
+            Log.w(UpdateInstaller.TAG, e);
+            notifyCallbackOnError(
+                    InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                    Log.getStackTraceString(e));
+        } catch (IOException e) {
+            Log.w(UpdateInstaller.TAG, e);
+            notifyCallbackOnError(
+                    InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e));
+        }
+    }
+
+    private void setState() throws IOException {
+        mUpdateInstalled = true;
+        mPackedUpdateFile = new ZipFile(mCopiedUpdateFile);
+        mProperties = new ArrayList<>();
+        mSizeForUpdate = -1;
+        mOffsetForUpdate = 0;
+        mEntries = mPackedUpdateFile.entries();
+    }
+
+    private UpdateEngine buildBoundUpdateEngine() {
+        UpdateEngine updateEngine = new UpdateEngine();
+        updateEngine.bind(new DelegatingUpdateEngineCallback(this, updateEngine));
+        return updateEngine;
+    }
+
+    private void applyPayload(String updatePath) throws IOException {
+        if (!updateStateForPayload()) {
+            return;
+        }
+        String[] headerKeyValuePairs = mProperties.stream().toArray(String[]::new);
+        if (mSizeForUpdate == -1) {
+            Log.w(UpdateInstaller.TAG, "Failed to find payload entry in the given package.");
+            notifyCallbackOnError(
+                    InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                    "Failed to find payload entry in the given package.");
+            return;
+        }
+
+        UpdateEngine updateEngine = buildBoundUpdateEngine();
+        updateEngine.applyPayload(
+                updatePath, mOffsetForUpdate, mSizeForUpdate, headerKeyValuePairs);
+    }
+
+    private boolean updateStateForPayload() throws IOException {
+        long offset = 0;
+        while (mEntries.hasMoreElements()) {
+            ZipEntry entry = mEntries.nextElement();
+
+            String name = entry.getName();
+            offset += buildOffsetForEntry(entry, name);
+            if (entry.isDirectory()) {
+                offset -= entry.getCompressedSize();
+                continue;
+            }
+            if (PAYLOAD_BIN.equals(name)) {
+                if (entry.getMethod() != ZipEntry.STORED) {
+                    Log.w(UpdateInstaller.TAG, "Invalid compression method.");
+                    notifyCallbackOnError(
+                            InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                            "Invalid compression method.");
+                    return false;
+                }
+                mSizeForUpdate = entry.getCompressedSize();
+                mOffsetForUpdate = offset - entry.getCompressedSize();
+            } else if (PAYLOAD_PROPERTIES_TXT.equals(name)) {
+                updatePropertiesForEntry(entry);
+            }
+        }
+        return true;
+    }
+
+    private long buildOffsetForEntry(ZipEntry entry, String name) {
+        return OFFSET_TO_FILE_NAME + name.length() + entry.getCompressedSize()
+                + (entry.getExtra() == null ? 0 : entry.getExtra().length);
+    }
+
+    private void updatePropertiesForEntry(ZipEntry entry) throws IOException {
+        try (BufferedReader bufferedReader = new BufferedReader(
+                new InputStreamReader(mPackedUpdateFile.getInputStream(entry)))) {
+            String line;
+            /* Neither @line nor @mProperties are size constraint since there is a few properties
+            with limited size. */
+            while ((line = bufferedReader.readLine()) != null) {
+                mProperties.add(line);
+            }
+        }
+    }
+
+    private static class DelegatingUpdateEngineCallback extends UpdateEngineCallback {
+        private UpdateInstaller mUpdateInstaller;
+        private UpdateEngine mUpdateEngine;
+
+        DelegatingUpdateEngineCallback(
+                UpdateInstaller updateInstaller, UpdateEngine updateEngine) {
+            mUpdateInstaller = updateInstaller;
+            mUpdateEngine = updateEngine;
+        }
+
+        @Override
+        public void onStatusUpdate(int statusCode, float percentage) {
+            return;
+        }
+
+        @Override
+        public void onPayloadApplicationComplete(int errorCode) {
+            mUpdateEngine.unbind();
+            if (errorCode == UpdateEngine.ErrorCodeConstants.SUCCESS) {
+                mUpdateInstaller.notifyCallbackOnSuccess();
+            } else {
+                mUpdateInstaller.notifyCallbackOnError(
+                        errorCodesMap.getOrDefault(
+                                errorCode, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN),
+                        errorStringsMap.getOrDefault(errorCode, UNKNOWN_ERROR + errorCode));
+            }
+        }
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 5926bdd..b9dabb9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -17,10 +17,15 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.IDevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
 import android.content.ComponentName;
+import android.os.ParcelFileDescriptor;
 
 import com.android.server.SystemService;
 
+import java.util.Collections;
+import java.util.List;
+
 /**
  * Defines the required interface for IDevicePolicyManager implemenation.
  *
@@ -91,4 +96,28 @@
 
     @Override
     public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) { }
+
+    @Override
+    public void installUpdateFromFile(ComponentName admin,
+            ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback listener) {}
+
+    @Override
+    public void addCrossProfileCalendarPackage(ComponentName admin, String packageName) {
+    }
+
+    @Override
+    public boolean removeCrossProfileCalendarPackage(ComponentName admin, String packageName) {
+        return false;
+    }
+
+    @Override
+    public List<String> getCrossProfileCalendarPackages(ComponentName admin) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean isPackageAllowedToAccessCalendarForUser(String packageName,
+            int userHandle) {
+        return false;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 71fea02..fd59b43 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -42,6 +42,12 @@
     private static final String DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY =
             "das_died_service_stable_connection_threshold_sec";
 
+    private static final String BATTERY_THRESHOLD_NOT_CHARGING_KEY =
+            "battery_threshold_not_charging";
+
+    private static final String BATTERY_THRESHOLD_CHARGING_KEY =
+            "battery_threshold_charging";
+
     /**
      * The back-off before re-connecting, when a service binding died, due to the owner
      * crashing repeatedly.
@@ -63,6 +69,17 @@
      */
     public final long DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC;
 
+    /**
+     * Battery threshold for installing system update while the device is not charging.
+     */
+    public final int BATTERY_THRESHOLD_NOT_CHARGING;
+
+    /**
+     * Battery threshold for installing system update while the device is charging.
+     */
+    public final int BATTERY_THRESHOLD_CHARGING;
+
+
     private DevicePolicyConstants(String settings) {
 
         final KeyValueListParser parser = new KeyValueListParser(',');
@@ -87,6 +104,12 @@
                 DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY,
                 TimeUnit.MINUTES.toSeconds(2));
 
+        int batteryThresholdNotCharging = parser.getInt(
+                BATTERY_THRESHOLD_NOT_CHARGING_KEY, 40);
+
+        int batteryThresholdCharging = parser.getInt(
+                BATTERY_THRESHOLD_CHARGING_KEY, 20);
+
         // Set minimum: 5 seconds.
         dasDiedServiceReconnectBackoffSec = Math.max(5, dasDiedServiceReconnectBackoffSec);
 
@@ -103,6 +126,8 @@
         DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC = dasDiedServiceReconnectMaxBackoffSec;
         DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC =
                 dasDiedServiceStableConnectionThresholdSec;
+        BATTERY_THRESHOLD_NOT_CHARGING = batteryThresholdNotCharging;
+        BATTERY_THRESHOLD_CHARGING = batteryThresholdCharging;
     }
 
     public static DevicePolicyConstants loadFromString(String settings) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bbbc40c..6fbb850 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -74,8 +74,10 @@
         .PROVISIONING_ENTRY_POINT_ADB;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
         .STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+        .ADMIN_TYPE_DEVICE_OWNER;
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+        .ADMIN_TYPE_PROFILE_OWNER;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -113,6 +115,7 @@
 import android.app.admin.PasswordMetrics;
 import android.app.admin.SecurityLog;
 import android.app.admin.SecurityLog.SecurityEvent;
+import android.app.admin.StartInstallingUpdateCallback;
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.backup.IBackupManager;
@@ -379,6 +382,8 @@
     private static final Set<String> GLOBAL_SETTINGS_DEPRECATED;
     private static final Set<String> SYSTEM_SETTINGS_WHITELIST;
     private static final Set<Integer> DA_DISALLOWED_POLICIES;
+    private static final String AB_DEVICE_KEY = "ro.build.ab_update";
+
     static {
         SECURE_SETTINGS_WHITELIST = new ArraySet<>();
         SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
@@ -905,8 +910,12 @@
         private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
         private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
         private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";
-        private static final String TAG_METERED_DATA_DISABLED_PACKAGES
-                = "metered_data_disabled_packages";
+        private static final String TAG_METERED_DATA_DISABLED_PACKAGES =
+                "metered_data_disabled_packages";
+        private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES =
+                "cross-profile-calendar-packages";
+        private static final String TAG_PACKAGE = "package";
+
 
         DeviceAdminInfo info;
 
@@ -1027,6 +1036,9 @@
         String startUserSessionMessage = null;
         String endUserSessionMessage = null;
 
+        // The whitelist of packages that can access cross profile calendar APIs.
+        final Set<String> mCrossProfileCalendarPackages = new ArraySet<>();
+
         ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
             info = _info;
             isParent = parent;
@@ -1296,6 +1308,12 @@
                 out.text(endUserSessionMessage);
                 out.endTag(null, TAG_END_USER_SESSION_MESSAGE);
             }
+            if (!mCrossProfileCalendarPackages.isEmpty()) {
+                out.startTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES);
+                writeAttributeValuesToXml(
+                        out, TAG_PACKAGE, mCrossProfileCalendarPackages);
+                out.endTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES);
+            }
         }
 
         void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -1488,6 +1506,9 @@
                     } else {
                         Log.w(LOG_TAG, "Missing text when loading end session message");
                     }
+                } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
+                    readAttributeValues(
+                            parser, TAG_PACKAGE, mCrossProfileCalendarPackages);
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -1703,6 +1724,8 @@
                 pw.print(prefix);  pw.println("parentAdmin:");
                 parentAdmin.dump(prefix + "  ", pw);
             }
+            pw.print(prefix); pw.print("mCrossProfileCalendarPackages=");
+            pw.println(mCrossProfileCalendarPackages);
         }
     }
 
@@ -13315,4 +13338,98 @@
 
         return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER);
     }
+
+    @Override
+    public void installUpdateFromFile(ComponentName admin,
+            ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback callback) {
+        enforceDeviceOwner(admin);
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            UpdateInstaller updateInstaller;
+            if (isDeviceAB()) {
+                updateInstaller = new AbUpdateInstaller(
+                        mContext, updateFileDescriptor, callback, mInjector, mConstants);
+            } else {
+                updateInstaller = new NonAbUpdateInstaller(
+                        mContext, updateFileDescriptor, callback, mInjector, mConstants);
+            }
+            updateInstaller.startInstallUpdate();
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+    }
+
+    private boolean isDeviceAB() {
+        return "true".equalsIgnoreCase(android.os.SystemProperties
+                .get(AB_DEVICE_KEY, ""));
+    }
+
+    @Override
+    public void addCrossProfileCalendarPackage(ComponentName who, String packageName) {
+        if (!mHasFeature) {
+            return;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getActiveAdminForCallerLocked(
+                    who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (admin.mCrossProfileCalendarPackages.add(packageName)) {
+                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            }
+        }
+    }
+
+    @Override
+    public boolean removeCrossProfileCalendarPackage(ComponentName who, String packageName) {
+        if (!mHasFeature) {
+            return false;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+        boolean isRemoved = false;
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getActiveAdminForCallerLocked(
+                    who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            isRemoved = admin.mCrossProfileCalendarPackages.remove(packageName);
+            if (isRemoved) {
+                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            }
+        }
+        return isRemoved;
+    }
+
+    @Override
+    public List<String> getCrossProfileCalendarPackages(ComponentName who) {
+        if (!mHasFeature) {
+            return Collections.emptyList();
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getActiveAdminForCallerLocked(
+                    who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            return new ArrayList<String>(admin.mCrossProfileCalendarPackages);
+        }
+    }
+
+    @Override
+    public boolean isPackageAllowedToAccessCalendarForUser(String packageName,
+            int userHandle) {
+        if (!mHasFeature) {
+            return false;
+        }
+        Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+        enforceCrossUsersPermission(userHandle);
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
+            if (admin != null && admin.mCrossProfileCalendarPackages != null) {
+                return admin.mCrossProfileCalendarPackages.contains(packageName);
+            }
+        }
+        return false;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
new file mode 100644
index 0000000..5f1e926
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.RecoverySystem;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Used for installing an update for <a href="https://source.android.com/devices/tech/ota/nonab">non
+ * AB</a> devices.
+ */
+class NonAbUpdateInstaller extends UpdateInstaller {
+    NonAbUpdateInstaller(Context context,
+            ParcelFileDescriptor updateFileDescriptor,
+            StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+            DevicePolicyConstants constants) {
+        super(context, updateFileDescriptor, callback, injector, constants);
+    }
+
+    @Override
+    public void installUpdateInThread() {
+        try {
+            RecoverySystem.installPackage(mContext, mCopiedUpdateFile);
+            notifyCallbackOnSuccess();
+        } catch (IOException e) {
+            Log.w(TAG, "IO error while trying to install non AB update.", e);
+            notifyCallbackOnError(
+                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                    Log.getStackTraceString(e));
+        }
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
new file mode 100644
index 0000000..7910598
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.StartInstallingUpdateCallback;
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+abstract class UpdateInstaller {
+    private StartInstallingUpdateCallback mCallback;
+    private ParcelFileDescriptor mUpdateFileDescriptor;
+    private DevicePolicyConstants mConstants;
+    protected Context mContext;
+    protected File mCopiedUpdateFile;
+
+    static final String TAG = "UpdateInstaller";
+    private DevicePolicyManagerService.Injector mInjector;
+
+    protected UpdateInstaller(Context context, ParcelFileDescriptor updateFileDescriptor,
+            StartInstallingUpdateCallback callback, DevicePolicyManagerService.Injector injector,
+            DevicePolicyConstants constants) {
+        mContext = context;
+        mCallback = callback;
+        mUpdateFileDescriptor = updateFileDescriptor;
+        mInjector = injector;
+        mConstants = constants;
+    }
+
+    public abstract void installUpdateInThread();
+
+    public void startInstallUpdate() {
+        if (!checkIfBatteryIsSufficient()) {
+            notifyCallbackOnError(
+                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW,
+                    "The battery level must be above "
+                            + mConstants.BATTERY_THRESHOLD_NOT_CHARGING + " while not charging or"
+                            + "above " + mConstants.BATTERY_THRESHOLD_CHARGING + " while charging");
+            return;
+        }
+        Thread thread = new Thread(() -> {
+            mCopiedUpdateFile = copyUpdateFileToDataOtaPackageDir();
+            if (mCopiedUpdateFile == null) {
+                notifyCallbackOnError(
+                        DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                        "Error while copying file.");
+                return;
+            }
+            installUpdateInThread();
+        });
+        thread.setPriority(Process.THREAD_PRIORITY_BACKGROUND);
+        thread.start();
+    }
+
+    private boolean checkIfBatteryIsSufficient() {
+        BatteryManager batteryManager =
+                (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE);
+        if (batteryManager != null) {
+            int chargePercentage = batteryManager
+                    .getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+            return batteryManager.isCharging()
+                    ? chargePercentage >= mConstants.BATTERY_THRESHOLD_CHARGING
+                    : chargePercentage >= mConstants.BATTERY_THRESHOLD_NOT_CHARGING;
+        }
+        return false;
+    }
+
+    private File copyUpdateFileToDataOtaPackageDir() {
+        try {
+            File destination = createNewFileWithPermissions();
+            copyToFile(destination);
+            return destination;
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to copy update file to OTA directory", e);
+            notifyCallbackOnError(
+                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                    Log.getStackTraceString(e));
+            return null;
+        }
+    }
+
+    private File createNewFileWithPermissions() throws IOException {
+        File destination = File.createTempFile(
+                "update", ".zip", new File(Environment.getDataDirectory() + "/ota_package"));
+        FileUtils.setPermissions(
+                /* path= */ destination,
+                /* mode= */ FileUtils.S_IRWXU | FileUtils.S_IRGRP | FileUtils.S_IROTH,
+                /* uid= */ -1, /* gid= */ -1);
+        return destination;
+    }
+
+    private void copyToFile(File destination) throws IOException {
+        try (OutputStream out = new FileOutputStream(destination);
+             InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
+                     mUpdateFileDescriptor)) {
+            FileUtils.copy(in, out);
+        }
+    }
+
+    void cleanupUpdateFile() {
+        if (mCopiedUpdateFile.exists()) {
+            mCopiedUpdateFile.delete();
+        }
+    }
+
+    protected void notifyCallbackOnError(int errorCode, String errorMessage) {
+        cleanupUpdateFile();
+        try {
+            mCallback.onStartInstallingUpdateError(errorCode, errorMessage);
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error while calling callback", e);
+        }
+    }
+
+    protected void notifyCallbackOnSuccess() {
+        cleanupUpdateFile();
+        mInjector.powerManagerReboot(PowerManager.REBOOT_REQUESTED_BY_DEVICE_OWNER);
+    }
+}
diff --git a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
index 57e954f..08fbf55 100644
--- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -24,11 +24,14 @@
 import android.service.intelligence.InteractionSessionId;
 import android.service.intelligence.SnapshotData;
 import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 import com.android.server.AbstractRemoteService;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
 import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks;
 
 import java.io.PrintWriter;
@@ -39,12 +42,12 @@
     private static final String TAG = "ContentCaptureSession";
 
     private final Object mLock;
-    private final IBinder mActivityToken;
-
+    final IBinder mActivityToken;
     private final IntelligencePerUserService mService;
     private final RemoteIntelligenceService mRemoteService;
     private final InteractionContext mInterationContext;
     private final InteractionSessionId mId;
+    private AugmentedAutofillCallback mAutofillCallback;
 
     ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
             @NonNull IBinder activityToken, @NonNull IntelligencePerUserService service,
@@ -92,6 +95,18 @@
     }
 
     /**
+     * Requests the service to autofill the given field.
+     */
+    public AugmentedAutofillCallback requestAutofillLocked(@NonNull IAutoFillManagerClient client,
+            int autofillSessionId, @NonNull AutofillId focusedId) {
+        mRemoteService.onRequestAutofillLocked(mId, client, autofillSessionId, focusedId);
+        if (mAutofillCallback == null) {
+            mAutofillCallback = () -> mRemoteService.onDestroyAutofillWindowsRequest(mId);
+        }
+        return mAutofillCallback;
+    }
+
+    /**
      * Cleans up the session and removes it from the service.
      *
      * @param notifyRemoteService whether it should trigger a {@link
@@ -119,6 +134,11 @@
         if (mService.isVerbose()) {
             Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
         }
+        if (mAutofillCallback != null) {
+            mAutofillCallback.destroy();
+            mAutofillCallback = null;
+        }
+
         // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
         if (notifyRemoteService) {
             mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
@@ -152,6 +172,8 @@
         pw.print(prefix); pw.print("id: ");  mId.dump(pw); pw.println();
         pw.print(prefix); pw.print("context: ");  mInterationContext.dump(pw); pw.println();
         pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
+        pw.print(prefix); pw.print("has autofill callback: ");
+        pw.println(mAutofillCallback != null);
     }
 
     @Override
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index a7f45ee..9fd797d 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -19,6 +19,7 @@
 import static android.content.Context.INTELLIGENCE_MANAGER_SERVICE;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.content.ComponentName;
@@ -27,6 +28,8 @@
 import android.os.IBinder;
 import android.os.UserManager;
 import android.service.intelligence.InteractionSessionId;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 import android.view.intelligence.IIntelligenceManager;
 
@@ -86,20 +89,6 @@
         service.destroyLocked();
     }
 
-    /**
-     * Notifies the intelligence service of new assist data for the given activity.
-     *
-     * @return {@code false} if there was no service set for the given user
-     */
-    private boolean sendActivityAssistDataLocked(@UserIdInt int userId,
-            @NonNull IBinder activityToken, @NonNull Bundle data) {
-        final IntelligencePerUserService service = peekServiceForUserLocked(userId);
-        if (service != null) {
-            return service.sendActivityAssistDataLocked(activityToken, data);
-        }
-        return false;
-    }
-
     private ActivityManagerInternal getAmInternal() {
         synchronized (mLock) {
             if (mAm == null) {
@@ -112,7 +101,7 @@
     final class IntelligenceManagerServiceStub extends IIntelligenceManager.Stub {
 
         @Override
-        public void startSession(int userId, @NonNull IBinder activityToken,
+        public void startSession(@UserIdInt int userId, @NonNull IBinder activityToken,
                 @NonNull ComponentName componentName, @NonNull InteractionSessionId sessionId,
                 int flags, @NonNull IResultReceiver result) {
             Preconditions.checkNotNull(activityToken);
@@ -134,7 +123,7 @@
         }
 
         @Override
-        public void sendEvents(int userId, @NonNull InteractionSessionId sessionId,
+        public void sendEvents(@UserIdInt int userId, @NonNull InteractionSessionId sessionId,
                 @NonNull List<ContentCaptureEvent> events) {
             Preconditions.checkNotNull(sessionId);
             Preconditions.checkNotNull(events);
@@ -146,12 +135,13 @@
         }
 
         @Override
-        public void finishSession(int userId, @NonNull InteractionSessionId sessionId) {
+        public void finishSession(@UserIdInt int userId, @NonNull InteractionSessionId sessionId,
+                @Nullable List<ContentCaptureEvent> events) {
             Preconditions.checkNotNull(sessionId);
 
             synchronized (mLock) {
                 final IntelligencePerUserService service = getServiceForUserLocked(userId);
-                service.finishSessionLocked(sessionId);
+                service.finishSessionLocked(sessionId, events);
             }
         }
 
@@ -168,14 +158,13 @@
     private final class LocalService extends IntelligenceManagerInternal {
 
         @Override
-        public boolean isIntelligenceServiceForUser(int uid, int userId) {
+        public boolean isIntelligenceServiceForUser(int uid, @UserIdInt int userId) {
             synchronized (mLock) {
                 final IntelligencePerUserService service = peekServiceForUserLocked(userId);
                 if (service != null) {
                     return service.isIntelligenceServiceForUserLocked(uid);
                 }
             }
-
             return false;
         }
 
@@ -183,8 +172,26 @@
         public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken,
                 @NonNull Bundle data) {
             synchronized (mLock) {
-                return sendActivityAssistDataLocked(userId, activityToken, data);
+                final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    return service.sendActivityAssistDataLocked(activityToken, data);
+                }
             }
+            return false;
+        }
+
+        @Override
+        public AugmentedAutofillCallback requestAutofill(@UserIdInt int userId,
+                @NonNull IAutoFillManagerClient client, @NonNull IBinder activityToken,
+                int autofillSessionId, @NonNull AutofillId focusedId) {
+            synchronized (mLock) {
+                final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    return service.requestAutofill(client, activityToken, autofillSessionId,
+                            focusedId);
+                }
+            }
+            return null;
         }
     }
 }
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
index 9694ab9..9ab7e58 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -22,6 +22,8 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.AppGlobals;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
@@ -36,12 +38,15 @@
 import android.service.intelligence.SnapshotData;
 import android.util.ArrayMap;
 import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 import android.view.intelligence.IntelligenceManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
 import com.android.server.AbstractPerUserSystemService;
+import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -62,7 +67,7 @@
     // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
 
     protected IntelligencePerUserService(
-            IntelligenceManagerService master, Object lock, int userId) {
+            IntelligenceManagerService master, Object lock, @UserIdInt int userId) {
         super(master, lock, userId);
     }
 
@@ -142,7 +147,8 @@
 
     // TODO(b/111276913): log metrics
     @GuardedBy("mLock")
-    public void finishSessionLocked(@NonNull InteractionSessionId sessionId) {
+    public void finishSessionLocked(@NonNull InteractionSessionId sessionId,
+            @Nullable List<ContentCaptureEvent> events) {
         if (!isEnabledLocked()) {
             return;
         }
@@ -154,8 +160,18 @@
             }
             return;
         }
+        if (events != null && !events.isEmpty()) {
+            // TODO(b/111276913): for now we're sending the events and the onDestroy() in 2 separate
+            // calls because it's not clear yet whether we'll change the manager to send events
+            // to the service directly (i.e., without passing through system server). Once we
+            // decide, we might need to split IIntelligenceService.onSessionLifecycle() in 2
+            // methods, one for start and another for finish (and passing the events to finish),
+            // otherwise the service might receive the 2 calls out of order.
+            session.sendEventsLocked(events);
+        }
         if (mMaster.verbose) {
-            Slog.v(TAG, "finishSession(): " + session);
+            Slog.v(TAG, "finishSession(" + (events == null ? 0 : events.size()) + " events): "
+                    + session);
         }
         session.removeSelfLocked(true);
     }
@@ -210,6 +226,17 @@
         return uid == getServiceUidLocked();
     }
 
+    @GuardedBy("mLock")
+    private ContentCaptureSession getSession(@NonNull IBinder activityToken) {
+        for (int i = 0; i < mSessions.size(); i++) {
+            final ContentCaptureSession session = mSessions.valueAt(i);
+            if (session.mActivityToken.equals(activityToken)) {
+                return session;
+            }
+        }
+        return null;
+    }
+
     /**
      * Destroys the service and all state associated with it.
      *
@@ -226,6 +253,22 @@
         mSessions.clear();
     }
 
+    public AugmentedAutofillCallback requestAutofill(@NonNull IAutoFillManagerClient client,
+            @NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId) {
+        synchronized (mLock) {
+            final ContentCaptureSession session = getSession(activityToken);
+            if (session != null) {
+                // TODO(b/111330312): log metrics
+                if (mMaster.verbose) Slog.v(TAG, "requestAugmentedAutofill()");
+                return session.requestAutofillLocked(client, autofillSessionId, focusedId);
+            }
+            if (mMaster.debug) {
+                Slog.d(TAG, "requestAutofill(): no session for " + activityToken);
+            }
+            return null;
+        }
+    }
+
     @Override
     protected void dumpLocked(String prefix, PrintWriter pw) {
         super.dumpLocked(prefix, pw);
diff --git a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
index a27c1cf..00c5b6a 100644
--- a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
+++ b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.RemoteException;
@@ -28,8 +29,12 @@
 import android.service.intelligence.SnapshotData;
 import android.text.format.DateUtils;
 import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
 
+import com.android.internal.os.IResultReceiver;
 import com.android.server.AbstractRemoteService;
 
 import java.util.List;
@@ -39,7 +44,7 @@
     private static final String TAG = "RemoteIntelligenceService";
 
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
-    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
 
     private final RemoteIntelligenceServiceCallbacks mCallbacks;
     private IIntelligenceService mService;
@@ -101,6 +106,25 @@
         scheduleRequest(new PendingOnActivitySnapshotRequest(this, sessionId, snapshotData));
     }
 
+    /**
+     * Called by {@link ContentCaptureSession} to request augmented autofill.
+     */
+    public void onRequestAutofillLocked(@NonNull InteractionSessionId sessionId,
+            @NonNull IAutoFillManagerClient client, int autofillSessionId,
+            @NonNull AutofillId focusedId) {
+        cancelScheduledUnbind();
+        scheduleRequest(new PendingAutofillRequest(this, sessionId, client, autofillSessionId,
+                focusedId));
+    }
+
+    /**
+     * Called by {@link ContentCaptureSession} when it's time to destroy all augmented autofill
+     * requests.
+     */
+    public void onDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) {
+        cancelScheduledUnbind();
+        scheduleRequest(new PendingDestroyAutofillWindowsRequest(this, sessionId));
+    }
 
     private abstract static class MyPendingRequest
             extends PendingRequest<RemoteIntelligenceService> {
@@ -124,8 +148,9 @@
             final RemoteIntelligenceService remoteService = getService();
             if (remoteService != null) {
                 try {
-                    myRun(remoteService);
                     // We don't expect the service to call us back, so we finish right away.
+                    myRun(remoteService);
+                    // TODO(b/111330312): not true anymore!!
                     finish();
                 } catch (RemoteException e) {
                     Slog.w(TAG, "exception handling " + getClass().getSimpleName() + " for "
@@ -191,6 +216,53 @@
         }
     }
 
+    private static final class PendingAutofillRequest extends MyPendingRequest {
+        private final @NonNull AutofillId mFocusedId;
+        private final @NonNull IAutoFillManagerClient mClient;
+        private final int mAutofillSessionId;
+
+        protected PendingAutofillRequest(@NonNull RemoteIntelligenceService service,
+                @NonNull InteractionSessionId sessionId, @NonNull IAutoFillManagerClient client,
+                int autofillSessionId, @NonNull AutofillId focusedId) {
+            super(service, sessionId);
+            mClient = client;
+            mAutofillSessionId = autofillSessionId;
+            mFocusedId = focusedId;
+        }
+
+        @Override // from MyPendingRequest
+        public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
+            final IResultReceiver receiver = new IResultReceiver.Stub() {
+
+                @Override
+                public void send(int resultCode, Bundle resultData) throws RemoteException {
+                    final IBinder realClient = resultData
+                            .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT);
+                    remoteService.mService.onAutofillRequest(mSessionId, realClient,
+                            mAutofillSessionId, mFocusedId);
+                }
+            };
+
+            // TODO(b/111330312): set cancellation signal, timeout (from  both mClient and service),
+            // cache IAugmentedAutofillManagerClient reference, etc...
+            mClient.getAugmentedAutofillClient(receiver);
+        }
+    }
+
+    private static final class PendingDestroyAutofillWindowsRequest extends MyPendingRequest {
+
+        protected PendingDestroyAutofillWindowsRequest(@NonNull RemoteIntelligenceService service,
+                @NonNull InteractionSessionId sessionId) {
+            super(service, sessionId);
+        }
+
+        @Override
+        protected void myRun(@NonNull RemoteIntelligenceService service) throws RemoteException {
+            service.mService.onDestroyAutofillWindowsRequest(mSessionId);
+            // TODO(b/111330312): implement timeout
+        }
+    }
+
     public interface RemoteIntelligenceServiceCallbacks extends VultureCallback {
         // To keep it simple, we use the same callback for all failures / timeouts.
         void onFailureOrTimeout(boolean timedOut);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f8ac41f..56f7cff 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,7 +36,6 @@
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
 import android.database.sqlite.SQLiteGlobal;
 import android.hardware.display.ColorDisplayManager;
-import android.hardware.display.DisplayManagerInternal;
 import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Build;
@@ -717,17 +716,9 @@
 
         // Manages Overlay packages
         traceBeginAndSlog("StartOverlayManagerService");
-        OverlayManagerService overlayManagerService = new OverlayManagerService(
-                mSystemContext, installer);
-        mSystemServiceManager.startService(overlayManagerService);
+        mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
         traceEnd();
 
-        if (SystemProperties.getInt("persist.sys.displayinset.top", 0) > 0) {
-            // DisplayManager needs the overlay immediately.
-            overlayManagerService.updateSystemUiContext();
-            LocalServices.getService(DisplayManagerInternal.class).onOverlayChanged();
-        }
-
         // The sensor service needs access to package manager service, app ops
         // service, and permissions service, therefore we start it after them.
         // Start sensor service in a separate thread. Completion should be checked
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index dc55179b..c9b9f3e 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -146,13 +146,14 @@
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     disabledMessage = dpmi.getPrintingDisabledReasonForUser(callingUserId);
+
+                    if (disabledMessage != null) {
+                        Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage,
+                                Toast.LENGTH_LONG).show();
+                    }
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
-                if (disabledMessage != null) {
-                    Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage,
-                            Toast.LENGTH_LONG).show();
-                }
                 try {
                     adapter.start();
                 } catch (RemoteException re) {
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 565152c..6f10ed5 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -26,6 +26,7 @@
 LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    bmgrlib \
     services.backup \
     services.core \
     services.net
diff --git a/services/robotests/src/com/android/commands/bmgr/BmgrTest.java b/services/robotests/src/com/android/commands/bmgr/BmgrTest.java
new file mode 100644
index 0000000..1705f5b
--- /dev/null
+++ b/services/robotests/src/com/android/commands/bmgr/BmgrTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.bmgr;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.backup.IBackupManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+/** Unit tests for {@link com.android.commands.bmgr.Bmgr}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class BmgrTest {
+    @Mock private IBackupManager mBackupManager;
+    private Bmgr mBmgr;
+
+    /** Common setup run before each test method. */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mBmgr = new Bmgr(mBackupManager);
+    }
+
+    /**
+     * Test that bmgr uses the default user {@link UserHandle.USER_SYSTEM} if no user is specified.
+     */
+    @Test
+    public void testRun_whenUserNotSpecified_callsBackupManagerAsSystemUser() throws Exception {
+        mBmgr.run(new String[] {"run"});
+
+        verify(mBackupManager).isBackupServiceActive(UserHandle.USER_SYSTEM);
+    }
+
+    /** Test that bmgr uses the specified user if an user is specified. */
+    @Test
+    public void testRun_whenUserSpecified_callsBackupManagerAsSpecifiedUser() throws Exception {
+        mBmgr.run(new String[] {"--user", "10"});
+
+        verify(mBackupManager).isBackupServiceActive(10);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index f7bb68c..ba4caf44 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -16,49 +16,27 @@
 
 package com.android.server.backup;
 
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startSilentBackupThread;
 import static com.android.server.backup.testing.TransportData.backupTransport;
-import static com.android.server.backup.testing.TransportData.d2dTransport;
-import static com.android.server.backup.testing.TransportData.localTransport;
-import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
-import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.expectThrows;
 
-import android.app.backup.BackupManager;
+import android.app.Application;
+import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
 import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
-import android.os.Binder;
-import android.os.HandlerThread;
-import android.os.PowerManager;
-import android.os.PowerSaveState;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
 
 import com.android.server.backup.testing.BackupManagerServiceTestUtils;
 import com.android.server.backup.testing.TransportData;
-import com.android.server.backup.testing.TransportTestUtils.TransportMock;
-import com.android.server.backup.transport.TransportNotRegisteredException;
-import com.android.server.testing.shadows.ShadowAppBackupUtils;
-import com.android.server.testing.shadows.ShadowBinder;
-import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
-import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -66,977 +44,482 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowContextWrapper;
-import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.shadows.ShadowSettings;
 
 import java.io.File;
-import java.util.List;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
-/** Tests for the system service {@link BackupManagerService} that performs backup/restore. */
+/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowAppBackupUtils.class})
 @Presubmit
 public class BackupManagerServiceTest {
-    private static final String TAG = "BMSTest";
-    private static final String PACKAGE_1 = "some.package.1";
-    private static final String PACKAGE_2 = "some.package.2";
+    private static final String TEST_PACKAGE = "package";
+    private static final String TEST_TRANSPORT = "transport";
 
+    @Mock private UserBackupManagerService mUserBackupManagerService;
     @Mock private TransportManager mTransportManager;
-    private HandlerThread mBackupThread;
-    private ShadowLooper mShadowBackupLooper;
-    private File mBaseStateDir;
-    private File mDataDir;
-    private ShadowContextWrapper mShadowContext;
+    private BackupManagerService mBackupManagerService;
     private Context mContext;
-    private TransportData mTransport;
-    private String mTransportName;
-    private ShadowPackageManager mShadowPackageManager;
 
-    /**
-     * Initialize state that {@link BackupManagerService} operations interact with. This includes
-     * setting up the transport, starting the backup thread, and creating backup data directories.
-     */
+    /** Initialize {@link BackupManagerService}. */
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        mTransport = backupTransport();
-        mTransportName = mTransport.transportName;
-
-        // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
-        // we should not fail tests because of this. This is not flakiness, the exceptions thrown
-        // don't interfere with the tests.
-        mBackupThread = startSilentBackupThread(TAG);
-        mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
-
-        ContextWrapper context = RuntimeEnvironment.application;
-        mShadowPackageManager = shadowOf(context.getPackageManager());
-        mContext = context;
-        mShadowContext = shadowOf(context);
-
-        File cacheDir = mContext.getCacheDir();
-        // Corresponds to /data/backup
-        mBaseStateDir = new File(cacheDir, "base_state");
-        // Corresponds to /cache/backup_stage
-        mDataDir = new File(cacheDir, "data");
+        Application application = RuntimeEnvironment.application;
+        mContext = application;
+        mBackupManagerService =
+                new BackupManagerService(
+                        application,
+                        new Trampoline(application),
+                        BackupManagerServiceTestUtils.startBackupThread(null),
+                        new File(application.getCacheDir(), "base_state"),
+                        new File(application.getCacheDir(), "data"),
+                        mTransportManager);
+        mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService);
     }
 
     /**
-     * Clean up and reset state that was created for testing {@link BackupManagerService}
-     * operations.
-     */
-    @After
-    public void tearDown() throws Exception {
-        mBackupThread.quit();
-        ShadowAppBackupUtils.reset();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
-     * specifically to prevent overloading the logs in production.
+     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}.
+     * This is specifically to prevent overloading the logs in production.
      */
     @Test
-    public void testMoreDebug_isFalse() {
+    public void testMoreDebug_isFalse() throws Exception {
         boolean moreDebug = BackupManagerService.MORE_DEBUG;
 
         assertThat(moreDebug).isFalse();
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#getDestinationString(String)} returns the
-     * current destination string of inputted transport if the transport is registered.
-     */
+    // TODO(b/118520567): Change the following tests to use the per-user instance of
+    // UserBackupManagerService once it's implemented. Currently these tests only test the straight
+    // forward redirection.
+
+    // ---------------------------------------------
+    // Backup agent tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testDestinationString() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
-                .thenReturn("destinationString");
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testDataChanged_callsDataChangedForUser() throws Exception {
+        mBackupManagerService.dataChanged(TEST_PACKAGE);
 
-        String destination = backupManagerService.getDestinationString(mTransportName);
-
-        assertThat(destination).isEqualTo("destinationString");
+        verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#getDestinationString(String)} returns {@code
-     * null} if the inputted transport is not registered.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testDestinationString_whenTransportNotRegistered() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
-                .thenThrow(TransportNotRegisteredException.class);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testAgentConnected_callsAgentConnectedForUser() throws Exception {
+        IBinder agentBinder = mock(IBinder.class);
 
-        String destination = backupManagerService.getDestinationString(mTransportName);
+        mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
 
-        assertThat(destination).isNull();
+        verify(mUserBackupManagerService).agentConnected(TEST_PACKAGE, agentBinder);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#getDestinationString(String)} throws a {@link
-     * SecurityException} if the caller does not have backup permission.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testDestinationString_withoutPermission() throws Exception {
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
-                .thenThrow(TransportNotRegisteredException.class);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testAgentDisconnected_callsAgentDisconnectedForUser() throws Exception {
+        mBackupManagerService.agentDisconnected(TEST_PACKAGE);
 
-        expectThrows(
-                SecurityException.class,
-                () -> backupManagerService.getDestinationString(mTransportName));
+        verify(mUserBackupManagerService).agentDisconnected(TEST_PACKAGE);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} returns
-     * {@code false} when the given app is not eligible for backup.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        setUpCurrentTransport(mTransportManager, mTransport);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testOpComplete_callsOpCompleteForUser() throws Exception {
+        mBackupManagerService.opComplete(/* token */ 0, /* result */ 0L);
 
-        boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
-
-        assertThat(result).isFalse();
+        verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} returns
-     * {@code true} when the given app is eligible for backup.
-     */
+    // ---------------------------------------------
+    // Transport tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
-        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception {
+        String[] transports = {TEST_TRANSPORT};
 
-        boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+        mBackupManagerService.initializeTransports(transports, /* observer */ null);
 
-        assertThat(result).isTrue();
-        verify(mTransportManager)
-                .disposeOfTransportClient(eq(transportMock.transportClient), any());
+        verify(mUserBackupManagerService).initializeTransports(transports, /* observer */ null);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} throws a
-     * {@link SecurityException} if the caller does not have backup permission.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        setUpCurrentTransport(mTransportManager, mTransport);
-        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testClearBackupData_callsClearBackupDataForUser() throws Exception {
+        mBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
 
-        expectThrows(
-                SecurityException.class,
-                () -> backupManagerService.isAppEligibleForBackup(PACKAGE_1));
+        verify(mUserBackupManagerService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])}
-     * returns an {@code array} of only apps that are eligible for backup from an {@array} of
-     * inputted apps.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testFilterAppsEligibleForBackup() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
-        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testGetCurrentTransport_callsGetCurrentTransportForUser() throws Exception {
+        mBackupManagerService.getCurrentTransport();
 
-        String[] filtered =
-                backupManagerService.filterAppsEligibleForBackup(
-                        new String[] {PACKAGE_1, PACKAGE_2});
-
-        assertThat(filtered).asList().containsExactly(PACKAGE_1);
-        verify(mTransportManager)
-                .disposeOfTransportClient(eq(transportMock.transportClient), any());
+        verify(mUserBackupManagerService).getCurrentTransport();
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])}
-     * returns an empty {@code array} if no inputted apps are eligible for backup.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser()
+            throws Exception {
+        mBackupManagerService.getCurrentTransportComponent();
 
-        String[] filtered =
-                backupManagerService.filterAppsEligibleForBackup(
-                        new String[] {PACKAGE_1, PACKAGE_2});
-
-        assertThat(filtered).isEmpty();
+        verify(mUserBackupManagerService).getCurrentTransportComponent();
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])} throws
-     * a {@link SecurityException} if the caller does not have backup permission.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        setUpCurrentTransport(mTransportManager, mTransport);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testListAllTransports_callsListAllTransportsForUser() throws Exception {
+        mBackupManagerService.listAllTransports();
 
-        expectThrows(
-                SecurityException.class,
-                () ->
-                        backupManagerService.filterAppsEligibleForBackup(
-                                new String[] {PACKAGE_1, PACKAGE_2}));
+        verify(mUserBackupManagerService).listAllTransports();
     }
 
-    /* Tests for select transport */
-
-    private ComponentName mNewTransportComponent;
-    private TransportData mNewTransport;
-    private TransportMock mNewTransportMock;
-    private TransportData mOldTransport;
-    private TransportMock mOldTransportMock;
-
-    private void setUpForSelectTransport() throws Exception {
-        mNewTransport = backupTransport();
-        mNewTransportComponent = mNewTransport.getTransportComponent();
-        mOldTransport = d2dTransport();
-        List<TransportMock> transportMocks =
-                setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
-        mNewTransportMock = transportMocks.get(0);
-        mOldTransportMock = transportMocks.get(1);
-        when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
-                .thenReturn(mOldTransport.transportName);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransport(String)} successfully
-     * switches the current transport to the inputted transport, returns the name of the old
-     * transport, and disposes of the transport client after the operation.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testSelectBackupTransport() throws Exception {
-        setUpForSelectTransport();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testListAllTransportComponents_callsListAllTransportComponentsForUser()
+            throws Exception {
+        mBackupManagerService.listAllTransportComponents();
 
-        String oldTransport =
-                backupManagerService.selectBackupTransport(mNewTransport.transportName);
-
-        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
-        assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
-        verify(mTransportManager)
-                .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+        verify(mUserBackupManagerService).listAllTransportComponents();
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransport(String)} throws a
-     * {@link SecurityException} if the caller does not have backup permission.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testSelectBackupTransport_withoutPermission() throws Exception {
-        setUpForSelectTransport();
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception {
+        mBackupManagerService.getTransportWhitelist();
 
-        expectThrows(
-                SecurityException.class,
-                () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
+        verify(mUserBackupManagerService).getTransportWhitelist();
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
-     * ISelectBackupTransportCallback)} successfully switches the current transport to the inputted
-     * transport and disposes of the transport client after the operation.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testSelectBackupTransportAsync() throws Exception {
-        setUpForSelectTransport();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
-                .thenReturn(BackupManager.SUCCESS);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
-        backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
-        mShadowBackupLooper.runToEndOfTasks();
-        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
-        verify(callback).onSuccess(eq(mNewTransport.transportName));
-        verify(mTransportManager)
-                .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
-     * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
-     * transport and notifies the inputted callback of failure when it fails to register the
-     * transport.
-     */
-    @Test
-    public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
-        setUpForSelectTransport();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
-                .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
-        backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
-        mShadowBackupLooper.runToEndOfTasks();
-        assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
-        verify(callback).onFailure(anyInt());
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
-     * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
-     * transport and notifies the inputted callback of failure when the transport gets unregistered.
-     */
-    @Test
-    public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
-        setUpTransports(mTransportManager, mTransport.unregistered());
-        ComponentName newTransportComponent = mTransport.getTransportComponent();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
-                .thenReturn(BackupManager.SUCCESS);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
-        backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
-
-        mShadowBackupLooper.runToEndOfTasks();
-        assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
-        verify(callback).onFailure(anyInt());
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
-     * ISelectBackupTransportCallback)} throws a {@link SecurityException} if the caller does not
-     * have backup permission.
-     */
-    @Test
-    public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
-        setUpForSelectTransport();
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        ComponentName newTransportComponent = mNewTransport.getTransportComponent();
-
-        expectThrows(
-                SecurityException.class,
-                () ->
-                        backupManagerService.selectBackupTransportAsync(
-                                newTransportComponent, mock(ISelectBackupTransportCallback.class)));
-    }
-
-    private String getSettingsTransport() {
-        return ShadowSettings.ShadowSecure.getString(
-                mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns the
-     * {@link ComponentName} of the currently selected transport.
-     */
-    @Test
-    public void testGetCurrentTransportComponent() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getCurrentTransportComponent())
-                .thenReturn(mTransport.getTransportComponent());
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
-        assertThat(transportComponent).isEqualTo(mTransport.getTransportComponent());
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns
-     * {@code null} if there is no currently selected transport.
-     */
-    @Test
-    public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
-        assertThat(transportComponent).isNull();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns
-     * {@code null} if the currently selected transport is not registered.
-     */
-    @Test
-    public void testGetCurrentTransportComponent_whenTransportNotRegistered() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getCurrentTransportComponent())
-                .thenThrow(TransportNotRegisteredException.class);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
-        assertThat(transportComponent).isNull();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} throws a
-     * {@link SecurityException} if the caller does not have backup permission.
-     */
-    @Test
-    public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
-    }
-
-    /* Tests for updating transport attributes */
-
-    private static final int PACKAGE_UID = 10;
-    private ComponentName mTransportComponent;
-    private int mTransportUid;
-
-    private void setUpForUpdateTransportAttributes() throws Exception {
-        mTransportComponent = mTransport.getTransportComponent();
-        String transportPackage = mTransportComponent.getPackageName();
-
-        ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
-        shadowPackageManager.addPackage(transportPackage);
-        shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
-
-        mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} succeeds if the uid of the transport is same as the
-     * uid of the caller.
-     */
-    @Test
-    public void
-            testUpdateTransportAttributes_whenTransportUidEqualsCallingUid_callsTransportManager()
-                    throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+    public void testUpdateTransportAttributes_callsUpdateTransportAttributesForUser()
+            throws Exception {
+        TransportData transport = backupTransport();
         Intent configurationIntent = new Intent();
         Intent dataManagementIntent = new Intent();
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
 
-        backupManagerService.updateTransportAttributes(
-                mTransportUid,
-                mTransportComponent,
-                mTransportName,
+        mBackupManagerService.updateTransportAttributes(
+                transport.getTransportComponent(),
+                transport.transportName,
                 configurationIntent,
                 "currentDestinationString",
                 dataManagementIntent,
                 "dataManagementLabel");
 
-        verify(mTransportManager)
+        verify(mUserBackupManagerService)
                 .updateTransportAttributes(
-                        eq(mTransportComponent),
-                        eq(mTransportName),
-                        eq(configurationIntent),
-                        eq("currentDestinationString"),
-                        eq(dataManagementIntent),
-                        eq("dataManagementLabel"));
+                        transport.getTransportComponent(),
+                        transport.transportName,
+                        configurationIntent,
+                        "currentDestinationString",
+                        dataManagementIntent,
+                        "dataManagementLabel");
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link SecurityException} if the uid of the
-     * transport is not equal to the uid of the caller.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
+    public void testSelectBackupTransport_callsSelectBackupTransportForUser() throws Exception {
+        mBackupManagerService.selectBackupTransport(TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSelectTransportAsync_callsSelectTransportAsyncForUser() throws Exception {
+        TransportData transport = backupTransport();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        mBackupManagerService.selectBackupTransportAsync(
+                transport.getTransportComponent(), callback);
+
+        verify(mUserBackupManagerService)
+                .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetConfigurationIntent_callsGetConfigurationIntentForUser() throws Exception {
+        mBackupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).getConfigurationIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDestinationString_callsGetDestinationStringForUser() throws Exception {
+        mBackupManagerService.getDestinationString(TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).getDestinationString(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDataManagementIntent_callsGetDataManagementIntentForUser() throws Exception {
+        mBackupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).getDataManagementIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDataManagementLabel_callsGetDataManagementLabelForUser() throws Exception {
+        mBackupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).getDataManagementLabel(TEST_TRANSPORT);
+    }
+
+    // ---------------------------------------------
+    // Settings tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception {
+        mBackupManagerService.setBackupEnabled(true);
+
+        verify(mUserBackupManagerService).setBackupEnabled(true);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception {
+        mBackupManagerService.setAutoRestore(true);
+
+        verify(mUserBackupManagerService).setAutoRestore(true);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSetBackupProvisioned_callsSetBackupProvisionedForUser() throws Exception {
+        mBackupManagerService.setBackupProvisioned(true);
+
+        verify(mUserBackupManagerService).setBackupProvisioned(true);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception {
+        mBackupManagerService.isBackupEnabled();
+
+        verify(mUserBackupManagerService).isBackupEnabled();
+    }
+
+    // ---------------------------------------------
+    // Backup tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testIsAppEligibleForBackup_callsIsAppEligibleForBackupForUser() throws Exception {
+        mBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+
+        verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testFilterAppsEligibleForBackup_callsFilterAppsEligibleForBackupForUser()
             throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+        String[] packages = {TEST_PACKAGE};
 
-        expectThrows(
-                SecurityException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid + 1,
-                                mTransportComponent,
-                                mTransportName,
-                                new Intent(),
-                                "currentDestinationString",
-                                new Intent(),
-                                "dataManagementLabel"));
+        mBackupManagerService.filterAppsEligibleForBackup(packages);
+
+        verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
-     * null} transport component.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
+    public void testBackupNow_callsBackupNowForUser() throws Exception {
+        mBackupManagerService.backupNow();
+
+        verify(mUserBackupManagerService).backupNow();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testRequestBackup_callsRequestBackupForUser() throws Exception {
+        String[] packages = {TEST_PACKAGE};
+        IBackupObserver observer = mock(IBackupObserver.class);
+        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+
+        mBackupManagerService.requestBackup(packages, observer, monitor, /* flags */ 0);
+
+        verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testCancelBackups_callsCancelBackupsForUser() throws Exception {
+        mBackupManagerService.cancelBackups();
+
+        verify(mUserBackupManagerService).cancelBackups();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception {
+        FullBackupJob job = new FullBackupJob();
+
+        mBackupManagerService.beginFullBackup(job);
+
+        verify(mUserBackupManagerService).beginFullBackup(job);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testEndFullBackup_callsEndFullBackupForUser() throws Exception {
+        mBackupManagerService.endFullBackup();
+
+        verify(mUserBackupManagerService).endFullBackup();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception {
+        String[] packages = {TEST_PACKAGE};
+
+        mBackupManagerService.fullTransportBackup(packages);
+
+        verify(mUserBackupManagerService).fullTransportBackup(packages);
+    }
+
+    // ---------------------------------------------
+    // Restore tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testRestoreAtInstall_callsRestoreAtInstallForUser() throws Exception {
+        mBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+
+        verify(mUserBackupManagerService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testBeginRestoreSession_callsBeginRestoreSessionForUser() throws Exception {
+        mBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser()
             throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+        mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
 
-        expectThrows(
-                RuntimeException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                null,
-                                mTransportName,
-                                new Intent(),
-                                "currentDestinationString",
-                                new Intent(),
-                                "dataManagementLabel"));
+        verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
-     * null} transport name.
-     */
-    @Test
-    public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    // ---------------------------------------------
+    // Adb backup/restore tests
+    // ---------------------------------------------
 
-        expectThrows(
-                RuntimeException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                mTransportComponent,
-                                null,
-                                new Intent(),
-                                "currentDestinationString",
-                                new Intent(),
-                                "dataManagementLabel"));
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSetBackupPassword_callsSetBackupPasswordForUser() throws Exception {
+        mBackupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+        verify(mUserBackupManagerService).setBackupPassword("currentPassword", "newPassword");
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
-     * null} destination string.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
+    public void testHasBackupPassword_callsHasBackupPasswordForUser() throws Exception {
+        mBackupManagerService.hasBackupPassword();
+
+        verify(mUserBackupManagerService).hasBackupPassword();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAdbBackup_callsAdbBackupForUser() throws Exception {
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        ParcelFileDescriptor parcelFileDescriptor =
+                ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+        String[] packages = {TEST_PACKAGE};
+
+        mBackupManagerService.adbBackup(
+                parcelFileDescriptor,
+                /* includeApks */ true,
+                /* includeObbs */ true,
+                /* includeShared */ true,
+                /* doWidgets */ true,
+                /* doAllApps */ true,
+                /* includeSystem */ true,
+                /* doCompress */ true,
+                /* doKeyValue */ true,
+                packages);
+
+        verify(mUserBackupManagerService)
+                .adbBackup(
+                        parcelFileDescriptor,
+                        /* includeApks */ true,
+                        /* includeObbs */ true,
+                        /* includeShared */ true,
+                        /* doWidgets */ true,
+                        /* doAllApps */ true,
+                        /* includeSystem */ true,
+                        /* doCompress */ true,
+                        /* doKeyValue */ true,
+                        packages);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAdbRestore_callsAdbRestoreForUser() throws Exception {
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        ParcelFileDescriptor parcelFileDescriptor =
+                ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+
+        mBackupManagerService.adbRestore(parcelFileDescriptor);
+
+        verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAcknowledgeAdbBackupOrRestore_callsAcknowledgeAdbBackupOrRestoreForUser()
             throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+        IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
 
-        expectThrows(
-                RuntimeException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                mTransportComponent,
-                                mTransportName,
-                                new Intent(),
-                                null,
-                                new Intent(),
-                                "dataManagementLabel"));
+        mBackupManagerService.acknowledgeAdbBackupOrRestore(
+                /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+
+        verify(mUserBackupManagerService)
+                .acknowledgeAdbBackupOrRestore(
+                        /* token */ 0,
+                        /* allow */ true,
+                        "currentPassword",
+                        "encryptionPassword",
+                        observer);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given either a
-     * {@code null} data management label or {@code null} data management intent, but not both.
-     */
+    // ---------------------------------------------
+    //  Service tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void
-            testUpdateTransportAttributes_whenDataManagementArgsNullityDontMatch_throwsException()
-                    throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testDump_callsDumpForUser() throws Exception {
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        FileDescriptor fileDescriptor = new FileDescriptor();
+        PrintWriter printWriter = new PrintWriter(testFile);
+        String[] args = {"1", "2"};
 
-        expectThrows(
-                RuntimeException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                mTransportComponent,
-                                mTransportName,
-                                new Intent(),
-                                "currentDestinationString",
-                                null,
-                                "dataManagementLabel"));
+        mBackupManagerService.dump(fileDescriptor, printWriter, args);
 
-        expectThrows(
-                RuntimeException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                mTransportComponent,
-                                mTransportName,
-                                new Intent(),
-                                "currentDestinationString",
-                                new Intent(),
-                                null));
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} succeeds if the caller has backup permission.
-     */
-    @Test
-    public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
-            throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        Intent configurationIntent = new Intent();
-        Intent dataManagementIntent = new Intent();
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        backupManagerService.updateTransportAttributes(
-                mTransportUid,
-                mTransportComponent,
-                mTransportName,
-                configurationIntent,
-                "currentDestinationString",
-                dataManagementIntent,
-                "dataManagementLabel");
-
-        verify(mTransportManager)
-                .updateTransportAttributes(
-                        eq(mTransportComponent),
-                        eq(mTransportName),
-                        eq(configurationIntent),
-                        eq("currentDestinationString"),
-                        eq(dataManagementIntent),
-                        eq("dataManagementLabel"));
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link SecurityException} if the caller
-     * does not have backup permission.
-     */
-    @Test
-    public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
-            throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        expectThrows(
-                SecurityException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                mTransportComponent,
-                                mTransportName,
-                                new Intent(),
-                                "currentDestinationString",
-                                new Intent(),
-                                "dataManagementLabel"));
-    }
-
-    /* Tests for request backup */
-
-    @Mock private IBackupObserver mObserver;
-
-    private void setUpForRequestBackup(String... packages) throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        for (String packageName : packages) {
-            mShadowPackageManager.addPackage(packageName);
-            ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
-        }
-        setUpCurrentTransport(mTransportManager, mTransport);
-    }
-
-    private void tearDownForRequestBackup() {
-        ShadowKeyValueBackupTask.reset();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} throws a {@link SecurityException} if the caller does not have backup permission.
-     */
-    @Test
-    public void testRequestBackup_whenPermissionDenied() throws Exception {
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        expectThrows(
-                SecurityException.class,
-                () -> backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0));
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} throws an {@link IllegalArgumentException} if passed {@null} for packages.
-     */
-    @Test
-    public void testRequestBackup_whenPackagesNull() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        expectThrows(
-                IllegalArgumentException.class,
-                () -> backupManagerService.requestBackup(null, mObserver, 0));
-        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} throws an {@link IllegalArgumentException} if passed an empty {@code array} for
-     * packages.
-     */
-    @Test
-    public void testRequestBackup_whenPackagesEmpty() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        expectThrows(
-                IllegalArgumentException.class,
-                () -> backupManagerService.requestBackup(new String[0], mObserver, 0));
-        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if backup is disabled.
-     */
-    @Test
-    public void testRequestBackup_whenBackupDisabled() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        backupManagerService.setEnabled(false);
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-        verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the system user hasn't gone
-     * through SUW.
-     */
-    @Test
-    public void testRequestBackup_whenNotProvisioned() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        backupManagerService.setProvisioned(false);
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-        verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#ERROR_TRANSPORT_ABORTED} if the current transport is not
-     * registered.
-     */
-    @Test
-    public void testRequestBackup_whenTransportNotRegistered() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        setUpCurrentTransport(mTransportManager, mTransport.unregistered());
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        assertThat(result).isEqualTo(BackupManager.ERROR_TRANSPORT_ABORTED);
-        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#SUCCESS} and notifies the observer of {@link
-     * BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the specified app is not eligible for backup.
-     */
-    @Test
-    public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        mShadowPackageManager.addPackage(PACKAGE_1);
-        setUpCurrentTransport(mTransportManager, mTransport);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
-        // Haven't set PACKAGE_1 as eligible
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        assertThat(result).isEqualTo(BackupManager.SUCCESS);
-        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-        // TODO: We probably don't need to kick-off KeyValueBackupTask when list is empty
-        tearDownForRequestBackup();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a key value
-     * package succeeds.
-     */
-    @Test
-    @Config(shadows = ShadowKeyValueBackupTask.class)
-    public void testRequestBackup_whenPackageIsKeyValue() throws Exception {
-        setUpForRequestBackup(PACKAGE_1);
-        BackupManagerService backupManagerService = createBackupManagerServiceForRequestBackup();
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        mShadowBackupLooper.runToEndOfTasks();
-        assertThat(result).isEqualTo(BackupManager.SUCCESS);
-        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
-        assertThat(shadowTask.getQueue()).containsExactly(PACKAGE_1);
-        assertThat(shadowTask.getPendingFullBackups()).isEmpty();
-        // TODO: Assert more about KeyValueBackupTask
-        tearDownForRequestBackup();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a full
-     * backup package succeeds.
-     */
-    @Test
-    @Config(shadows = ShadowKeyValueBackupTask.class)
-    public void testRequestBackup_whenPackageIsFullBackup() throws Exception {
-        setUpForRequestBackup(PACKAGE_1);
-        ShadowAppBackupUtils.setAppGetsFullBackup(PACKAGE_1);
-        BackupManagerService backupManagerService = createBackupManagerServiceForRequestBackup();
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        mShadowBackupLooper.runToEndOfTasks();
-        assertThat(result).isEqualTo(BackupManager.SUCCESS);
-        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
-        assertThat(shadowTask.getQueue()).isEmpty();
-        assertThat(shadowTask.getPendingFullBackups()).containsExactly(PACKAGE_1);
-        // TODO: Assert more about KeyValueBackupTask
-        tearDownForRequestBackup();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#backupNow()} clears the calling identity
-     * for scheduling a job and then restores the original calling identity after the operation.
-     */
-    @Test
-    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
-    public void testBackupNow_clearsCallingIdentityForJobScheduler() {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        setUpPowerManager(backupManagerService);
-        ShadowBinder.setCallingUid(1);
-
-        backupManagerService.backupNow();
-
-        assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
-        assertThat(Binder.getCallingUid()).isEqualTo(1);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#backupNow()} restores the original calling
-     * identity if an exception is thrown during execution.
-     */
-    @Test
-    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
-    public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        setUpPowerManager(backupManagerService);
-        ShadowBinder.setCallingUid(1);
-
-        expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
-        assertThat(ShadowKeyValueBackupJobException.getCallingUid())
-                .isEqualTo(ShadowBinder.LOCAL_UID);
-        assertThat(Binder.getCallingUid()).isEqualTo(1);
-    }
-
-    private BackupManagerService createBackupManagerServiceForRequestBackup() {
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
-        return backupManagerService;
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#BackupManagerService(Context, Trampoline,
-     * HandlerThread, File, File, TransportManager)} posts a transport registration task to the
-     * backup handler thread.
-     */
-    @Test
-    public void testConstructor_postRegisterTransports() {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-
-        createBackupManagerService();
-
-        mShadowBackupLooper.runToEndOfTasks();
-        verify(mTransportManager).registerTransports();
-    }
-
-    /**
-     * Test verifying that the {@link BackupManagerService#BackupManagerService(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 BackupManagerService createBackupManagerService() {
-        return new BackupManagerService(
-                mContext,
-                new Trampoline(mContext),
-                mBackupThread,
-                mBaseStateDir,
-                mDataDir,
-                mTransportManager);
-    }
-
-    private BackupManagerService createInitializedBackupManagerService() {
-        return BackupManagerServiceTestUtils.createInitializedBackupManagerService(
-                mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
-    }
-
-    private void setUpPowerManager(BackupManagerService backupManagerService) {
-        PowerManager powerManagerMock = mock(PowerManager.class);
-        when(powerManagerMock.getPowerSaveState(anyInt()))
-                .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
-        backupManagerService.setPowerManager(powerManagerMock);
-    }
-
-    /**
-     * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
-     * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
-     */
-    @Implements(KeyValueBackupJob.class)
-    public static class ShadowKeyValueBackupJobException extends ShadowKeyValueBackupJob {
-        /**
-         * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
-         * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
-         */
-        public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
-            ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
-            throw new IllegalArgumentException();
-        }
+        verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args);
     }
 }
diff --git a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
new file mode 100644
index 0000000..9d43819
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startSilentBackupThread;
+import static com.android.server.backup.testing.TransportData.backupTransport;
+import static com.android.server.backup.testing.TransportData.d2dTransport;
+import static com.android.server.backup.testing.TransportData.localTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+
+import android.app.backup.BackupManager;
+import android.app.backup.IBackupObserver;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.HandlerThread;
+import android.os.PowerManager;
+import android.os.PowerSaveState;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+
+import com.android.server.backup.testing.BackupManagerServiceTestUtils;
+import com.android.server.backup.testing.TransportData;
+import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.testing.shadows.ShadowAppBackupUtils;
+import com.android.server.testing.shadows.ShadowBinder;
+import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
+import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowContextWrapper;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.shadows.ShadowSettings;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Tests for the per-user instance of the backup/restore system service {@link
+ * UserBackupManagerService} that performs operations for its target user.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowAppBackupUtils.class})
+@Presubmit
+public class UserBackupManagerServiceTest {
+    private static final String TAG = "BMSTest";
+    private static final String PACKAGE_1 = "some.package.1";
+    private static final String PACKAGE_2 = "some.package.2";
+
+    @Mock private TransportManager mTransportManager;
+    private HandlerThread mBackupThread;
+    private ShadowLooper mShadowBackupLooper;
+    private File mBaseStateDir;
+    private File mDataDir;
+    private ShadowContextWrapper mShadowContext;
+    private Context mContext;
+    private TransportData mTransport;
+    private String mTransportName;
+    private ShadowPackageManager mShadowPackageManager;
+
+    /**
+     * Initialize state that {@link UserBackupManagerService} operations interact with. This
+     * includes setting up the transport, starting the backup thread, and creating backup data
+     * directories.
+     */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mTransport = backupTransport();
+        mTransportName = mTransport.transportName;
+
+        // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
+        // we should not fail tests because of this. This is not flakiness, the exceptions thrown
+        // don't interfere with the tests.
+        mBackupThread = startSilentBackupThread(TAG);
+        mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
+
+        ContextWrapper context = RuntimeEnvironment.application;
+        mShadowPackageManager = shadowOf(context.getPackageManager());
+        mContext = context;
+        mShadowContext = shadowOf(context);
+
+        File cacheDir = mContext.getCacheDir();
+        // Corresponds to /data/backup
+        mBaseStateDir = new File(cacheDir, "base_state");
+        // Corresponds to /cache/backup_stage
+        mDataDir = new File(cacheDir, "data");
+    }
+
+    /**
+     * Clean up and reset state that was created for testing {@link UserBackupManagerService}
+     * operations.
+     */
+    @After
+    public void tearDown() throws Exception {
+        mBackupThread.quit();
+        ShadowAppBackupUtils.reset();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} returns the
+     * current destination string of inputted transport if the transport is registered.
+     */
+    @Test
+    public void testDestinationString() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+                .thenReturn("destinationString");
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        String destination = backupManagerService.getDestinationString(mTransportName);
+
+        assertThat(destination).isEqualTo("destinationString");
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} returns
+     * {@code null} if the inputted transport is not registered.
+     */
+    @Test
+    public void testDestinationString_whenTransportNotRegistered() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+                .thenThrow(TransportNotRegisteredException.class);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        String destination = backupManagerService.getDestinationString(mTransportName);
+
+        assertThat(destination).isNull();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} throws a
+     * {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testDestinationString_withoutPermission() throws Exception {
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+                .thenThrow(TransportNotRegisteredException.class);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () -> backupManagerService.getDestinationString(mTransportName));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} returns
+     * {@code false} when the given app is not eligible for backup.
+     */
+    @Test
+    public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpCurrentTransport(mTransportManager, mTransport);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+
+        assertThat(result).isFalse();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} returns
+     * {@code true} when the given app is eligible for backup.
+     */
+    @Test
+    public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
+        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+
+        assertThat(result).isTrue();
+        verify(mTransportManager)
+                .disposeOfTransportClient(eq(transportMock.transportClient), any());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} throws a
+     * {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        setUpCurrentTransport(mTransportManager, mTransport);
+        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () -> backupManagerService.isAppEligibleForBackup(PACKAGE_1));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+     * returns an {@code array} of only apps that are eligible for backup from an {@array} of
+     * inputted apps.
+     */
+    @Test
+    public void testFilterAppsEligibleForBackup() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
+        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        String[] filtered =
+                backupManagerService.filterAppsEligibleForBackup(
+                        new String[] {PACKAGE_1, PACKAGE_2});
+
+        assertThat(filtered).asList().containsExactly(PACKAGE_1);
+        verify(mTransportManager)
+                .disposeOfTransportClient(eq(transportMock.transportClient), any());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+     * returns an empty {@code array} if no inputted apps are eligible for backup.
+     */
+    @Test
+    public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        String[] filtered =
+                backupManagerService.filterAppsEligibleForBackup(
+                        new String[] {PACKAGE_1, PACKAGE_2});
+
+        assertThat(filtered).isEmpty();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+     * throws a {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        setUpCurrentTransport(mTransportManager, mTransport);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.filterAppsEligibleForBackup(
+                                new String[] {PACKAGE_1, PACKAGE_2}));
+    }
+
+    /* Tests for select transport */
+
+    private ComponentName mNewTransportComponent;
+    private TransportData mNewTransport;
+    private TransportMock mNewTransportMock;
+    private TransportData mOldTransport;
+    private TransportMock mOldTransportMock;
+
+    private void setUpForSelectTransport() throws Exception {
+        mNewTransport = backupTransport();
+        mNewTransportComponent = mNewTransport.getTransportComponent();
+        mOldTransport = d2dTransport();
+        List<TransportMock> transportMocks =
+                setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
+        mNewTransportMock = transportMocks.get(0);
+        mOldTransportMock = transportMocks.get(1);
+        when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
+                .thenReturn(mOldTransport.transportName);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransport(String)}
+     * successfully switches the current transport to the inputted transport, returns the name of
+     * the old transport, and disposes of the transport client after the operation.
+     */
+    @Test
+    public void testSelectBackupTransport() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        String oldTransport =
+                backupManagerService.selectBackupTransport(mNewTransport.transportName);
+
+        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+        assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
+        verify(mTransportManager)
+                .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransport(String)} throws a
+     * {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testSelectBackupTransport_withoutPermission() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+     * ISelectBackupTransportCallback)} successfully switches the current transport to the inputted
+     * transport and disposes of the transport client after the operation.
+     */
+    @Test
+    public void testSelectBackupTransportAsync() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+                .thenReturn(BackupManager.SUCCESS);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+        verify(callback).onSuccess(eq(mNewTransport.transportName));
+        verify(mTransportManager)
+                .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+     * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
+     * transport and notifies the inputted callback of failure when it fails to register the
+     * transport.
+     */
+    @Test
+    public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+                .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
+        verify(callback).onFailure(anyInt());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+     * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
+     * transport and notifies the inputted callback of failure when the transport gets unregistered.
+     */
+    @Test
+    public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
+        setUpTransports(mTransportManager, mTransport.unregistered());
+        ComponentName newTransportComponent = mTransport.getTransportComponent();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
+                .thenReturn(BackupManager.SUCCESS);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
+        verify(callback).onFailure(anyInt());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+     * ISelectBackupTransportCallback)} throws a {@link SecurityException} if the caller does not
+     * have backup permission.
+     */
+    @Test
+    public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        ComponentName newTransportComponent = mNewTransport.getTransportComponent();
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.selectBackupTransportAsync(
+                                newTransportComponent, mock(ISelectBackupTransportCallback.class)));
+    }
+
+    private String getSettingsTransport() {
+        return ShadowSettings.ShadowSecure.getString(
+                mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+     * the {@link ComponentName} of the currently selected transport.
+     */
+    @Test
+    public void testGetCurrentTransportComponent() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getCurrentTransportComponent())
+                .thenReturn(mTransport.getTransportComponent());
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+        assertThat(transportComponent).isEqualTo(mTransport.getTransportComponent());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+     * {@code null} if there is no currently selected transport.
+     */
+    @Test
+    public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+        assertThat(transportComponent).isNull();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+     * {@code null} if the currently selected transport is not registered.
+     */
+    @Test
+    public void testGetCurrentTransportComponent_whenTransportNotRegistered() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getCurrentTransportComponent())
+                .thenThrow(TransportNotRegisteredException.class);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+        assertThat(transportComponent).isNull();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} throws a
+     * {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
+    }
+
+    /* Tests for updating transport attributes */
+
+    private static final int PACKAGE_UID = 10;
+    private ComponentName mTransportComponent;
+    private int mTransportUid;
+
+    private void setUpForUpdateTransportAttributes() throws Exception {
+        mTransportComponent = mTransport.getTransportComponent();
+        String transportPackage = mTransportComponent.getPackageName();
+
+        ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
+        shadowPackageManager.addPackage(transportPackage);
+        shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
+
+        mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} succeeds if the uid of the transport
+     * is same as the uid of the caller.
+     */
+    @Test
+    public void
+            testUpdateTransportAttributes_whenTransportUidEqualsCallingUid_callsTransportManager()
+                    throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        Intent configurationIntent = new Intent();
+        Intent dataManagementIntent = new Intent();
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        backupManagerService.updateTransportAttributes(
+                mTransportUid,
+                mTransportComponent,
+                mTransportName,
+                configurationIntent,
+                "currentDestinationString",
+                dataManagementIntent,
+                "dataManagementLabel");
+
+        verify(mTransportManager)
+                .updateTransportAttributes(
+                        eq(mTransportComponent),
+                        eq(mTransportName),
+                        eq(configurationIntent),
+                        eq("currentDestinationString"),
+                        eq(dataManagementIntent),
+                        eq("dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link SecurityException} if
+     * the uid of the transport is not equal to the uid of the caller.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
+            throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid + 1,
+                                mTransportComponent,
+                                mTransportName,
+                                new Intent(),
+                                "currentDestinationString",
+                                new Intent(),
+                                "dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+     * given a {@code null} transport component.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
+            throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                RuntimeException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                null,
+                                mTransportName,
+                                new Intent(),
+                                "currentDestinationString",
+                                new Intent(),
+                                "dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+     * given a {@code null} transport name.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                RuntimeException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                mTransportComponent,
+                                null,
+                                new Intent(),
+                                "currentDestinationString",
+                                new Intent(),
+                                "dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+     * given a {@code null} destination string.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
+            throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                RuntimeException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                mTransportComponent,
+                                mTransportName,
+                                new Intent(),
+                                null,
+                                new Intent(),
+                                "dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+     * given either a {@code null} data management label or {@code null} data management intent, but
+     * not both.
+     */
+    @Test
+    public void
+            testUpdateTransportAttributes_whenDataManagementArgsNullityDontMatch_throwsException()
+                    throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                RuntimeException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                mTransportComponent,
+                                mTransportName,
+                                new Intent(),
+                                "currentDestinationString",
+                                null,
+                                "dataManagementLabel"));
+
+        expectThrows(
+                RuntimeException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                mTransportComponent,
+                                mTransportName,
+                                new Intent(),
+                                "currentDestinationString",
+                                new Intent(),
+                                null));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} succeeds if the caller has backup
+     * permission.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
+            throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        Intent configurationIntent = new Intent();
+        Intent dataManagementIntent = new Intent();
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        backupManagerService.updateTransportAttributes(
+                mTransportUid,
+                mTransportComponent,
+                mTransportName,
+                configurationIntent,
+                "currentDestinationString",
+                dataManagementIntent,
+                "dataManagementLabel");
+
+        verify(mTransportManager)
+                .updateTransportAttributes(
+                        eq(mTransportComponent),
+                        eq(mTransportName),
+                        eq(configurationIntent),
+                        eq("currentDestinationString"),
+                        eq(dataManagementIntent),
+                        eq("dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link SecurityException} if
+     * the caller does not have backup permission.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
+            throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                mTransportComponent,
+                                mTransportName,
+                                new Intent(),
+                                "currentDestinationString",
+                                new Intent(),
+                                "dataManagementLabel"));
+    }
+
+    /* Tests for request backup */
+
+    @Mock private IBackupObserver mObserver;
+
+    private void setUpForRequestBackup(String... packages) throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        for (String packageName : packages) {
+            mShadowPackageManager.addPackage(packageName);
+            ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
+        }
+        setUpCurrentTransport(mTransportManager, mTransport);
+    }
+
+    private void tearDownForRequestBackup() {
+        ShadowKeyValueBackupTask.reset();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} throws a {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testRequestBackup_whenPermissionDenied() throws Exception {
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () -> backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} throws an {@link IllegalArgumentException} if passed {@null} for packages.
+     */
+    @Test
+    public void testRequestBackup_whenPackagesNull() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                IllegalArgumentException.class,
+                () -> backupManagerService.requestBackup(null, mObserver, 0));
+        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} throws an {@link IllegalArgumentException} if passed an empty {@code array} for
+     * packages.
+     */
+    @Test
+    public void testRequestBackup_whenPackagesEmpty() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                IllegalArgumentException.class,
+                () -> backupManagerService.requestBackup(new String[0], mObserver, 0));
+        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if backup is disabled.
+     */
+    @Test
+    public void testRequestBackup_whenBackupDisabled() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        backupManagerService.setEnabled(false);
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+        verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the system user hasn't gone
+     * through SUW.
+     */
+    @Test
+    public void testRequestBackup_whenNotProvisioned() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        backupManagerService.setProvisioned(false);
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+        verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#ERROR_TRANSPORT_ABORTED} if the current transport is not
+     * registered.
+     */
+    @Test
+    public void testRequestBackup_whenTransportNotRegistered() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpCurrentTransport(mTransportManager, mTransport.unregistered());
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        backupManagerService.setEnabled(true);
+        backupManagerService.setProvisioned(true);
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        assertThat(result).isEqualTo(BackupManager.ERROR_TRANSPORT_ABORTED);
+        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#SUCCESS} and notifies the observer of {@link
+     * BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the specified app is not eligible for backup.
+     */
+    @Test
+    public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        mShadowPackageManager.addPackage(PACKAGE_1);
+        setUpCurrentTransport(mTransportManager, mTransport);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        backupManagerService.setEnabled(true);
+        backupManagerService.setProvisioned(true);
+        // Haven't set PACKAGE_1 as eligible
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        assertThat(result).isEqualTo(BackupManager.SUCCESS);
+        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+        // TODO: We probably don't need to kick-off KeyValueBackupTask when list is empty
+        tearDownForRequestBackup();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a key value
+     * package succeeds.
+     */
+    @Test
+    @Config(shadows = ShadowKeyValueBackupTask.class)
+    public void testRequestBackup_whenPackageIsKeyValue() throws Exception {
+        setUpForRequestBackup(PACKAGE_1);
+        UserBackupManagerService backupManagerService =
+                createBackupManagerServiceForRequestBackup();
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(BackupManager.SUCCESS);
+        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
+        assertThat(shadowTask.getQueue()).containsExactly(PACKAGE_1);
+        assertThat(shadowTask.getPendingFullBackups()).isEmpty();
+        // TODO: Assert more about KeyValueBackupTask
+        tearDownForRequestBackup();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a full
+     * backup package succeeds.
+     */
+    @Test
+    @Config(shadows = ShadowKeyValueBackupTask.class)
+    public void testRequestBackup_whenPackageIsFullBackup() throws Exception {
+        setUpForRequestBackup(PACKAGE_1);
+        ShadowAppBackupUtils.setAppGetsFullBackup(PACKAGE_1);
+        UserBackupManagerService backupManagerService =
+                createBackupManagerServiceForRequestBackup();
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(BackupManager.SUCCESS);
+        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
+        assertThat(shadowTask.getQueue()).isEmpty();
+        assertThat(shadowTask.getPendingFullBackups()).containsExactly(PACKAGE_1);
+        // TODO: Assert more about KeyValueBackupTask
+        tearDownForRequestBackup();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#backupNow()} clears the calling identity
+     * for scheduling a job and then restores the original calling identity after the operation.
+     */
+    @Test
+    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
+    public void testBackupNow_clearsCallingIdentityForJobScheduler() {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        setUpPowerManager(backupManagerService);
+        ShadowBinder.setCallingUid(1);
+
+        backupManagerService.backupNow();
+
+        assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
+        assertThat(Binder.getCallingUid()).isEqualTo(1);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#backupNow()} restores the original
+     * calling identity if an exception is thrown during execution.
+     */
+    @Test
+    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
+    public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        setUpPowerManager(backupManagerService);
+        ShadowBinder.setCallingUid(1);
+
+        expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
+        assertThat(ShadowKeyValueBackupJobException.getCallingUid())
+                .isEqualTo(ShadowBinder.LOCAL_UID);
+        assertThat(Binder.getCallingUid()).isEqualTo(1);
+    }
+
+    private UserBackupManagerService createBackupManagerServiceForRequestBackup() {
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        backupManagerService.setEnabled(true);
+        backupManagerService.setProvisioned(true);
+        return backupManagerService;
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#UserBackupManagerService(Context,
+     * Trampoline, HandlerThread, File, File, TransportManager)} posts a transport registration task
+     * to the backup handler thread.
+     */
+    @Test
+    public void testConstructor_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(
+                mContext,
+                new Trampoline(mContext),
+                mBackupThread,
+                mBaseStateDir,
+                mDataDir,
+                mTransportManager);
+    }
+
+    private UserBackupManagerService createInitializedBackupManagerService() {
+        return BackupManagerServiceTestUtils.createInitializedUserBackupManagerService(
+                mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
+    }
+
+    private void setUpPowerManager(UserBackupManagerService backupManagerService) {
+        PowerManager powerManagerMock = mock(PowerManager.class);
+        when(powerManagerMock.getPowerSaveState(anyInt()))
+                .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+        backupManagerService.setPowerManager(powerManagerMock);
+    }
+
+    /**
+     * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
+     * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
+     */
+    @Implements(KeyValueBackupJob.class)
+    public static class ShadowKeyValueBackupJobException extends ShadowKeyValueBackupJob {
+        /**
+         * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
+         * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
+         */
+        public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+            ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
+            throw new IllegalArgumentException();
+        }
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index fd7ced2..423512c 100644
--- a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -1,10 +1,10 @@
 package com.android.server.backup.fullbackup;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
 
 import static com.google.common.truth.Truth.assertThat;
 
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
index 6ee6eb6..a14cc51 100644
--- a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
@@ -46,6 +46,7 @@
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.testing.TransportData;
 import com.android.server.backup.testing.TransportTestUtils;
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
@@ -71,7 +72,7 @@
 @Config(shadows = ShadowSlog.class)
 @Presubmit
 public class PerformInitializeTaskTest {
-    @Mock private BackupManagerService mBackupManagerService;
+    @Mock private UserBackupManagerService mBackupManagerService;
     @Mock private TransportManager mTransportManager;
     @Mock private OnTaskFinishedListener mListener;
     @Mock private IBackupTransport mTransportBinder;
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
index a0afb5e..a1b8a95 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
@@ -26,7 +26,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.testing.shadows.ShadowEventLog;
 import com.android.server.testing.shadows.ShadowSlog;
 
@@ -41,7 +41,7 @@
 @Config(shadows = {ShadowEventLog.class, ShadowSlog.class})
 @Presubmit
 public class KeyValueBackupReporterTest {
-    @Mock private BackupManagerService mBackupManagerService;
+    @Mock private UserBackupManagerService mBackupManagerService;
     @Mock private IBackupObserver mObserver;
     @Mock private IBackupManagerMonitor mMonitor;
 
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 a69f007..1aa4999 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -26,7 +26,7 @@
 
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils
-        .createInitializedBackupManagerService;
+        .createInitializedUserBackupManagerService;
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils
         .setUpBackupManagerServiceBasics;
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils
@@ -103,12 +103,12 @@
 
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.EventLogTags;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.KeyValueBackupJob;
 import com.android.server.backup.PackageManagerBackupAgent;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.BackupHandler;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.remote.RemoteCall;
@@ -178,7 +178,7 @@
     @Mock private IBackupObserver mObserver;
     @Mock private IBackupManagerMonitor mMonitor;
     @Mock private OnTaskFinishedListener mListener;
-    private BackupManagerService mBackupManagerService;
+    private UserBackupManagerService mBackupManagerService;
     private TransportData mTransport;
     private ShadowLooper mShadowBackupLooper;
     private Handler mBackupHandler;
@@ -227,7 +227,7 @@
         setUpBinderCallerAndApplicationAsSystem(mApplication);
         mBackupManagerService =
                 spy(
-                        createInitializedBackupManagerService(
+                        createInitializedUserBackupManagerService(
                                 mContext, mBaseStateDir, mDataDir, mTransportManager));
         setUpBackupManagerServiceBasics(
                 mBackupManagerService,
@@ -720,7 +720,7 @@
     }
 
     /**
-     * Agent unavailable means {@link BackupManagerService#bindToAgentSynchronous(ApplicationInfo,
+     * Agent unavailable means {@link UserBackupManagerService#bindToAgentSynchronous(ApplicationInfo,
      * int)} returns {@code null}.
      *
      * @see #setUpAgent(PackageData)
@@ -2597,7 +2597,7 @@
      *
      * <ul>
      *   <li>The transport being initialized with {@link IBackupTransport#initializeDevice()}
-     *   <li>{@link BackupManagerService#resetBackupState(File)} being called, which will:
+     *   <li>{@link UserBackupManagerService#resetBackupState(File)} being called, which will:
      *       <ul>
      *         <li>Reset processed packages journal.
      *         <li>Reset current token to 0.
@@ -2617,7 +2617,7 @@
 
     /**
      * Forces transport initialization and call to {@link
-     * BackupManagerService#resetBackupState(File)}
+     * UserBackupManagerService#resetBackupState(File)}
      */
     private void deletePmStateFile() throws IOException {
         Files.deleteIfExists(getStateFile(mTransport, PM_PACKAGE));
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 0e2b95b..859392d 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -51,8 +51,8 @@
 
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.BackupHandler;
 import com.android.server.backup.testing.TransportData;
 import com.android.server.backup.testing.TransportTestUtils;
@@ -85,7 +85,7 @@
     private static final long TOKEN_1 = 1L;
     private static final long TOKEN_2 = 2L;
 
-    @Mock private BackupManagerService mBackupManagerService;
+    @Mock private UserBackupManagerService mBackupManagerService;
     @Mock private TransportManager mTransportManager;
     @Mock private IRestoreObserver mObserver;
     @Mock private IBackupManagerMonitor mMonitor;
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 f307730..bacc44e 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -37,9 +37,9 @@
 import android.util.Log;
 
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.Trampoline;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 
 import org.mockito.stubbing.Answer;
 import org.robolectric.shadows.ShadowApplication;
@@ -49,26 +49,26 @@
 import java.lang.Thread.UncaughtExceptionHandler;
 import java.util.concurrent.atomic.AtomicReference;
 
-/** Test utils for {@link BackupManagerService} and friends. */
+/** 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.
      */
-    public static BackupManagerService createInitializedBackupManagerService(
+    public static UserBackupManagerService createInitializedUserBackupManagerService(
             Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
-        return createInitializedBackupManagerService(
+        return createInitializedUserBackupManagerService(
                 context, startBackupThread(null), baseStateDir, dataDir, transportManager);
     }
 
-    public static BackupManagerService createInitializedBackupManagerService(
+    public static UserBackupManagerService createInitializedUserBackupManagerService(
             Context context,
             HandlerThread backupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
-        BackupManagerService backupManagerService =
-                new BackupManagerService(
+        UserBackupManagerService backupManagerService =
+                new UserBackupManagerService(
                         context,
                         new Trampoline(context),
                         backupThread,
@@ -80,15 +80,16 @@
     }
 
     /**
-     * Sets up basic mocks for {@link BackupManagerService} mock. If {@code backupManagerService} is
-     * a spy, make sure you provide in the arguments the same objects that the original object uses.
+     * Sets up basic mocks for {@link UserBackupManagerService} mock. If {@code
+     * backupManagerService} is a spy, make sure you provide in the arguments the same objects that
+     * the original object uses.
      *
      * <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)}.
      */
     @SuppressWarnings("ResultOfMethodCallIgnored")
     public static void setUpBackupManagerServiceBasics(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             Application application,
             TransportManager transportManager,
             PackageManager packageManager,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
index ca80664..ac5d2da 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
@@ -18,8 +18,8 @@
 
 import android.annotation.Nullable;
 
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.keyvalue.KeyValueBackupReporter;
 import com.android.server.backup.keyvalue.KeyValueBackupTask;
@@ -54,7 +54,7 @@
 
     @Implementation
     protected void __constructor__(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             String transportDirName,
             List<String> queue,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
index 228d4eb..2cebbeb 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
@@ -21,7 +21,7 @@
 import android.app.backup.IRestoreObserver;
 import android.content.pm.PackageInfo;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.restore.PerformUnifiedRestoreTask;
 import com.android.server.backup.transport.TransportClient;
@@ -47,7 +47,7 @@
         sLastShadow = null;
     }
 
-    private BackupManagerService mBackupManagerService;
+    private UserBackupManagerService mBackupManagerService;
     @Nullable private PackageInfo mPackage;
     private boolean mIsFullSystemRestore;
     @Nullable private String[] mFilterSet;
@@ -55,7 +55,7 @@
 
     @Implementation
     protected void __constructor__(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             IRestoreObserver observer,
             IBackupManagerMonitor monitor,
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
new file mode 100644
index 0000000..ebc816d
--- /dev/null
+++ b/services/tests/mockingservicestests/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+android_test {
+    name: "FrameworksMockingServicesTests",
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "services.core",
+        "services.net",
+        "androidx.test.runner",
+        "mockito-target-extended-minus-junit4",
+        "platform-test-annotations",
+    ],
+
+    libs: [
+        "android.test.mock",
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    jni_libs: [
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+
+    certificate: "platform",
+    platform_apis: true,
+    test_suites: ["device-tests"],
+
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk
deleted file mode 100644
index b21b3e4..0000000
--- a/services/tests/mockingservicestests/Android.mk
+++ /dev/null
@@ -1,42 +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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    services.core \
-    services.net \
-    androidx.test.runner \
-    mockito-target-extended-minus-junit4 \
-    platform-test-annotations \
-
-LOCAL_JAVA_LIBRARIES := android.test.mock android.test.base android.test.runner
-
-LOCAL_JNI_SHARED_LIBRARIES := \
-    libdexmakerjvmtiagent \
-    libstaticjvmtiagent \
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
new file mode 100644
index 0000000..e804342
--- /dev/null
+++ b/services/tests/servicestests/Android.bp
@@ -0,0 +1,104 @@
+//########################################################################
+// Build FrameworksServicesTests package
+//########################################################################
+
+android_test {
+    name: "FrameworksServicesTests",
+
+    // Include all test java files.
+    srcs: [
+        "src/**/*.java",
+
+        "aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl",
+        "aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl",
+
+        "test-apps/JobTestApp/src/**/*.java",
+
+        "test-apps/SuspendTestApp/src/**/*.java",
+    ],
+    static_libs: [
+        "frameworks-base-testutils",
+        "services.accessibility",
+        "services.appwidget",
+        "services.autofill",
+        "services.backup",
+        "services.core",
+        "services.devicepolicy",
+        "services.net",
+        "services.usage",
+        "guava",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "mockito-target-minus-junit4",
+        "platform-test-annotations",
+        "ShortcutManagerTestUtils",
+        "truth-prebuilt",
+        "testables",
+        "testng",
+        "ub-uiautomator",
+        "platformprotosnano",
+        "hamcrest-library",
+        "servicestests-utils",
+    ],
+
+    aidl: {
+        local_include_dirs: ["aidl"],
+    },
+
+    libs: [
+        "android.hidl.manager-V1.0-java",
+        "android.hardware.tv.cec-V1.0-java",
+        "android.test.mock",
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    platform_apis: true,
+    test_suites: ["device-tests"],
+
+    certificate: "platform",
+
+    // These are not normally accessible from apps so they must be explicitly included.
+    jni_libs: [
+        "libbacktrace",
+        "libbase",
+        "libbinder",
+        "libbinderthreadstate",
+        "libc++",
+        "libcutils",
+        "liblog",
+        "liblzma",
+        "libnativehelper",
+        "libnetdaidl",
+        "libui",
+        "libunwind",
+        "libutils",
+    ],
+
+    dxflags: ["--multi-dex"],
+
+    optimize: {
+        enabled: false,
+    },
+}
+
+java_library {
+    name: "servicestests-utils",
+    srcs: [
+        "utils/**/*.java",
+    ],
+    static_libs: [
+        "android-support-test",
+        "mockito-target-minus-junit4",
+    ],
+    libs: [
+        "android.test.runner",
+    ],
+}
+
+filegroup {
+    name: "servicestests-SuspendTestApp-files",
+    srcs: [
+        "src/com/android/server/pm/SuspendPackagesTest.java",
+    ],
+}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
deleted file mode 100644
index e2f8995..0000000
--- a/services/tests/servicestests/Android.mk
+++ /dev/null
@@ -1,83 +0,0 @@
-#########################################################################
-# Build FrameworksServicesTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-java-files-under, utils) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    frameworks-base-testutils \
-    services.accessibility \
-    services.appwidget \
-    services.autofill \
-    services.backup \
-    services.core \
-    services.devicepolicy \
-    services.net \
-    services.usage \
-    guava \
-    androidx.test.runner \
-    androidx.test.rules \
-    mockito-target-minus-junit4 \
-    platform-test-annotations \
-    ShortcutManagerTestUtils \
-    truth-prebuilt \
-    testables \
-    testng \
-    ub-uiautomator\
-    platformprotosnano \
-    hamcrest-library
-
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
-
-LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl \
-    aidl/com/android/servicestests/aidl/ICmdReceiverService.aidl
-LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/JobTestApp/src)
-LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/SuspendTestApp/src)
-
-LOCAL_JAVA_LIBRARIES := \
-    android.hidl.manager-V1.0-java \
-    android.hardware.tv.cec-V1.0-java \
-    android.test.mock \
-    android.test.base android.test.runner \
-
-LOCAL_PACKAGE_NAME := FrameworksServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
-    libbacktrace \
-    libbase \
-    libbinder \
-    libbinderthreadstate \
-    libc++ \
-    libcutils \
-    liblog \
-    liblzma \
-    libnativehelper \
-    libnetdaidl \
-    libui \
-    libunwind \
-    libutils
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/servicestests/aidl/Android.bp b/services/tests/servicestests/aidl/Android.bp
new file mode 100644
index 0000000..d4e53dd
--- /dev/null
+++ b/services/tests/servicestests/aidl/Android.bp
@@ -0,0 +1,22 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+    name: "servicestests-aidl",
+    sdk_version: "current",
+    srcs: [
+        "com/android/servicestests/aidl/INetworkStateObserver.aidl",
+        "com/android/servicestests/aidl/ICmdReceiverService.aidl",
+    ],
+}
diff --git a/services/tests/servicestests/aidl/Android.mk b/services/tests/servicestests/aidl/Android.mk
deleted file mode 100644
index 166da1d..0000000
--- a/services/tests/servicestests/aidl/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := \
-        com/android/servicestests/aidl/INetworkStateObserver.aidl \
-        com/android/servicestests/aidl/ICmdReceiverService.aidl
-LOCAL_MODULE := servicestests-aidl
-include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/shortcut_share_targets.xml b/services/tests/servicestests/res/xml/shortcut_share_targets.xml
new file mode 100644
index 0000000..ec696e9
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_share_targets.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<!-- Test XML resource to read share-targets from, used in ShortcutManagerTest1.java -->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
+    <shortcut
+        android:shortcutId="dummy_shortcut1"
+        android:enabled="true"
+        android:shortcutShortLabel="@string/shortcut_title1">
+        <intent
+            android:action="android.intent.action.VIEW"
+            android:targetPackage="com.test.somepackage"
+            android:targetClass="com.test.somepackage.someclass" />
+        <categories android:name="android.shortcut.conversation" />
+    </shortcut>
+
+    <!-- Valid share target definition -->
+    <share-target android:targetClass="com.test.directshare.TestActivity1">
+        <data
+            android:scheme="http"
+            android:host="www.google.com"
+            android:port="1234"
+            android:path="somePath"
+            android:pathPrefix="somePathPrefix"
+            android:pathPattern="somePathPattern"
+            android:mimeType="text/plain"/>
+        <category android:name="com.test.category.CATEGORY1"/>
+        <category android:name="com.test.category.CATEGORY2"/>
+    </share-target>
+
+    <!-- Share target missing data tag, will be dropped -->
+    <share-target android:targetClass="com.test.directshare.TestActivity">
+        <category android:name="com.test.category.CATEGORY2"/>
+    </share-target>
+
+    <!-- Share target missing target class, will be dropped -->
+    <share-target>
+        <data
+            android:scheme="file"
+            android:host="www.somehost.com"
+            android:port="1234"
+            android:mimeType="video/*"/>
+        <category android:name="com.test.category.CATEGORY3"/>
+    </share-target>
+
+    <shortcut
+        android:shortcutId="dummy_shortcut2"
+        android:enabled="true"
+        android:shortcutShortLabel="@string/shortcut_title1">
+        <intent
+            android:action="android.intent.action.VIEW"
+            android:targetPackage="com.test.somepackage"
+            android:targetClass="com.test.somepackage.someclass" />
+        <categories android:name="android.shortcut.conversation" />
+    </shortcut>
+
+    <!-- Share target missing category, will be dropped -->
+    <share-target android:targetClass="com.test.directshare.TestActivity">
+        <data
+            android:scheme="content"
+            android:mimeType="text/plain"/>
+    </share-target>
+
+    <!-- Valid share target definition -->
+    <share-target android:targetClass="com.test.directshare.TestActivity5">
+        <category android:name="com.test.category.CATEGORY5"/>
+        <category android:name="com.test.category.CATEGORY6"/>
+        <data android:mimeType="video/mp4"/>
+        <data
+            android:scheme="content"
+            android:mimeType="video/*"/>
+    </share-target>
+</shortcuts>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 113ee2d..92efc3c 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -148,6 +148,9 @@
 
 import com.google.common.util.concurrent.AbstractFuture;
 
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -162,9 +165,6 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.InputStream;
@@ -843,6 +843,18 @@
         assertTrue(mService.isUidForeground(UID_B));
     }
 
+    @Test
+    public void testAppIdleTempWhitelisting() throws Exception {
+        mService.setAppIdleWhitelist(UID_A, true);
+        mService.setAppIdleWhitelist(UID_B, false);
+        int[] whitelistedIds = mService.getAppIdleWhitelist();
+        assertTrue(Arrays.binarySearch(whitelistedIds, UID_A) >= 0);
+        assertTrue(Arrays.binarySearch(whitelistedIds, UID_B) < 0);
+        assertFalse(mService.isUidIdle(UID_A));
+        // Can't currently guarantee UID_B's app idle state.
+        // TODO: expand with multiple app idle states.
+    }
+
     private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
         RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
                 ZoneId.systemDefault());
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index d52051eec..479a19b 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -34,7 +34,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.testutils.PackageManagerStub;
 
 import org.junit.Before;
@@ -97,7 +97,7 @@
         applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
         applicationInfo.uid = Process.SYSTEM_UID;
         applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
-        applicationInfo.packageName = BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
+        applicationInfo.packageName = UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
         boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                 mPackageManagerStub);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 4774985..d43b677 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -51,8 +51,8 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.servicestests.R;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.FileMetadata;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.restore.PerformAdbRestoreTask;
 import com.android.server.backup.restore.RestorePolicy;
 import com.android.server.backup.testutils.PackageManagerStub;
@@ -150,7 +150,7 @@
 
         assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
         assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
-        assertThat(fileMetadata.path).isEqualTo(BackupManagerService.BACKUP_MANIFEST_FILENAME);
+        assertThat(fileMetadata.path).isEqualTo(UserBackupManagerService.BACKUP_MANIFEST_FILENAME);
 
         tarBackupReader.skipTarPadding(fileMetadata.size);
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index a3348c2..1f5c64e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -91,8 +91,8 @@
 import com.android.server.SystemService;
 import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
 import com.android.server.pm.ShortcutUser.PackageWithUser;
-
 import com.android.server.wm.ActivityTaskManagerInternal;
+
 import org.junit.Assert;
 import org.mockito.ArgumentCaptor;
 import org.mockito.invocation.InvocationOnMock;
@@ -105,8 +105,6 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -762,6 +760,7 @@
         LocalServices.addService(UsageStatsManagerInternal.class, mMockUsageStatsManagerInternal);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
         LocalServices.addService(ActivityManagerInternal.class, mMockActivityManagerInternal);
+        LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
         LocalServices.addService(ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
         LocalServices.removeServiceForTest(UserManagerInternal.class);
         LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
@@ -963,6 +962,10 @@
         return getInstrumentation().getContext();
     }
 
+    protected Context getClientContext() {
+        return mClientContext;
+    }
+
     protected ShortcutManager getManager() {
         return mManager;
     }
@@ -1792,6 +1795,15 @@
     }
 
     /**
+     * @return all share targets stored internally for the caller.
+     */
+    protected List<ShareTargetInfo> getCallerShareTargets() {
+        final ShortcutPackage p = mService.getPackageShortcutForTest(
+                getCallingPackage(), getCallingUserId());
+        return p == null ? null : p.getAllShareTargetsForTest();
+    }
+
+    /**
      * @return all shortcuts owned by caller that are actually visible via ShortcutManager.
      * See also {@link #getCallerShortcuts}.
      */
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index fa73447..3172afb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -106,6 +106,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.function.BiConsumer;
@@ -398,7 +399,7 @@
         assertEquals(3, mManager.getRemainingCallCount());
     }
 
-   public void testPublishWithNoActivity() {
+    public void testPublishWithNoActivity() {
         // If activity is not explicitly set, use the default one.
 
         mRunningUsers.put(USER_10, true);
@@ -8015,4 +8016,56 @@
 
         assertFalse(mInternal.isForegroundDefaultLauncher("another", uid));
     }
+
+    public void testParseShareTargetsFromManifest() {
+        // These values must exactly match the content of shortcuts_share_targets.xml resource
+        List<ShareTargetInfo> expectedValues = new ArrayList<>();
+        expectedValues.add(new ShareTargetInfo(
+                new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
+                        "http", "www.google.com", "1234", "somePath", "somePathPattern",
+                        "somePathPrefix", "text/plain")}, "com.test.directshare.TestActivity1",
+                new String[]{"com.test.category.CATEGORY1", "com.test.category.CATEGORY2"}));
+        expectedValues.add(new ShareTargetInfo(new ShareTargetInfo.TargetData[]{
+                new ShareTargetInfo.TargetData(null, null, null, null, null, null, "video/mp4"),
+                new ShareTargetInfo.TargetData("content", null, null, null, null, null, "video/*")},
+                "com.test.directshare.TestActivity5",
+                new String[]{"com.test.category.CATEGORY5", "com.test.category.CATEGORY6"}));
+
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_share_targets);
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+        List<ShareTargetInfo> shareTargets = getCallerShareTargets();
+
+        assertNotNull(shareTargets);
+        assertEquals(expectedValues.size(), shareTargets.size());
+
+        for (int i = 0; i < expectedValues.size(); i++) {
+            ShareTargetInfo expected = expectedValues.get(i);
+            ShareTargetInfo actual = shareTargets.get(i);
+
+            assertEquals(expected.mTargetData.length, actual.mTargetData.length);
+            for (int j = 0; j < expected.mTargetData.length; j++) {
+                assertEquals(expected.mTargetData[j].mScheme, actual.mTargetData[j].mScheme);
+                assertEquals(expected.mTargetData[j].mHost, actual.mTargetData[j].mHost);
+                assertEquals(expected.mTargetData[j].mPort, actual.mTargetData[j].mPort);
+                assertEquals(expected.mTargetData[j].mPath, actual.mTargetData[j].mPath);
+                assertEquals(expected.mTargetData[j].mPathPrefix,
+                        actual.mTargetData[j].mPathPrefix);
+                assertEquals(expected.mTargetData[j].mPathPattern,
+                        actual.mTargetData[j].mPathPattern);
+                assertEquals(expected.mTargetData[j].mMimeType, actual.mTargetData[j].mMimeType);
+            }
+
+            assertEquals(expected.mTargetClass, actual.mTargetClass);
+
+            assertEquals(expected.mCategories.length, actual.mCategories.length);
+            for (int j = 0; j < expected.mCategories.length; j++) {
+                assertEquals(expected.mCategories[j], actual.mCategories[j]);
+            }
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 76d52fd..9b59f91 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -144,8 +144,8 @@
 
         assertExpectException(
                 IllegalArgumentException.class, "Short label must be provided", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                    .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                    .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
                     .build();
             assertTrue(getManager().setDynamicShortcuts(list(si)));
         });
@@ -153,15 +153,15 @@
         // same for add.
         assertExpectException(
                 IllegalArgumentException.class, "Short label must be provided", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                    .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                    .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
                     .build();
             assertTrue(getManager().addDynamicShortcuts(list(si)));
         });
 
         assertExpectException(NullPointerException.class, "Intent must be provided", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                    .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                    .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
                     .setShortLabel("x")
                     .build();
             assertTrue(getManager().setDynamicShortcuts(list(si)));
@@ -169,8 +169,8 @@
 
         // same for add.
         assertExpectException(NullPointerException.class, "Intent must be provided", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                    .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                    .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
                     .setShortLabel("x")
                     .build();
             assertTrue(getManager().addDynamicShortcuts(list(si)));
@@ -178,7 +178,7 @@
 
         assertExpectException(
                 IllegalStateException.class, "does not belong to package", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
                     .setActivity(new ComponentName("xxx", "s"))
                     .build();
             assertTrue(getManager().setDynamicShortcuts(list(si)));
@@ -187,7 +187,7 @@
         // same for add.
         assertExpectException(
                 IllegalStateException.class, "does not belong to package", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
                     .setActivity(new ComponentName("xxx", "s"))
                     .build();
             assertTrue(getManager().addDynamicShortcuts(list(si)));
@@ -198,24 +198,24 @@
 
         assertExpectException(
                 IllegalStateException.class, "is not main", () -> {
-                    ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                            .setActivity(new ComponentName(getTestContext(), "s"))
+                    ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                            .setActivity(new ComponentName(getClientContext(), "s"))
                             .build();
                     assertTrue(getManager().setDynamicShortcuts(list(si)));
                 });
         // For add
         assertExpectException(
                 IllegalStateException.class, "is not main", () -> {
-                    ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                            .setActivity(new ComponentName(getTestContext(), "s"))
+                    ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                            .setActivity(new ComponentName(getClientContext(), "s"))
                             .build();
                     assertTrue(getManager().addDynamicShortcuts(list(si)));
                 });
         // For update
         assertExpectException(
                 IllegalStateException.class, "is not main", () -> {
-                    ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                            .setActivity(new ComponentName(getTestContext(), "s"))
+                    ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                            .setActivity(new ComponentName(getClientContext(), "s"))
                             .build();
                     assertTrue(getManager().updateShortcuts(list(si)));
                 });
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
index f12619c..19d18ca 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
@@ -35,7 +35,6 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.Display;
-import android.view.IApplicationToken;
 
 import androidx.test.filters.SmallTest;
 
@@ -111,16 +110,9 @@
         final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
 
-        // Set TestAppWindowContainerController & assign first app token state to be good to go.
-        final WindowTestUtils.TestAppWindowContainerController controller1 =
-                createAppWindowController(dc1, token1.appToken);
-        final WindowTestUtils.TestAppWindowContainerController controller2 =
-                createAppWindowController(dc1, token2.appToken);
-        controller1.setContainer(token1);
         token1.allDrawn = true;
         token1.startingDisplayed = true;
         token1.startingMoved = true;
-        controller2.setContainer(token2);
 
         // Simulate activity resume / finish flows to prepare app transition & set visibility,
         // make sure transition is set as expected for each display.
@@ -132,8 +124,8 @@
         assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition());
         // One activity window is visible for resuming & the other activity window is invisible
         // for finishing in different display.
-        controller1.setVisibility(true, false);
-        controller2.setVisibility(false, false);
+        token1.setVisibility(true, false);
+        token2.setVisibility(false, false);
 
         // Make sure each display is in animating stage.
         assertTrue(dc1.mOpeningApps.size() > 0);
@@ -174,16 +166,4 @@
         assertFalse(dc1.mOpeningApps.contains(token1));
     }
 
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
-            DisplayContent dc, IApplicationToken token) {
-        return createAppWindowController(
-                new WindowTestUtils.TestTaskWindowContainerController(
-                        createStackControllerOnDisplay(dc)), token);
-    }
-
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
-            WindowTestUtils.TestTaskWindowContainerController taskController,
-            IApplicationToken token) {
-        return new WindowTestUtils.TestAppWindowContainerController(taskController, token);
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
deleted file mode 100644
index 415b5d9..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.res.Configuration.EMPTY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController;
-
-import org.junit.Test;
-
-/**
- * Test class for {@link AppWindowContainerController}.
- *
- * atest FrameworksServicesTests:AppWindowContainerControllerTests
- */
-@FlakyTest(bugId = 74078662)
-@SmallTest
-@Presubmit
-public class AppWindowContainerControllerTests extends WindowTestsBase {
-
-    private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
-
-    @Test
-    public void testRemoveContainer() {
-        final WindowTestUtils.TestAppWindowContainerController controller =
-                createAppWindowController();
-
-        // Assert token was added to display.
-        assertNotNull(mDisplayContent.getWindowToken(controller.mToken.asBinder()));
-        // Assert that the container was created and linked.
-        assertNotNull(controller.mContainer);
-
-        controller.removeContainer(mDisplayContent.getDisplayId());
-
-        // Assert token was remove from display.
-        assertNull(mDisplayContent.getWindowToken(controller.mToken.asBinder()));
-        // Assert that the container was removed.
-        assertNull(controller.mContainer);
-    }
-
-    @Test
-    public void testSetOrientation() {
-        final WindowTestUtils.TestAppWindowContainerController controller =
-                createAppWindowController();
-
-        // Assert orientation is unspecified to start.
-        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
-
-        controller.setOrientation(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getDisplayId(),
-                EMPTY /* displayConfig */, false /* freezeScreenIfNeeded */);
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, controller.getOrientation());
-
-        controller.removeContainer(mDisplayContent.getDisplayId());
-        // Assert orientation is unspecified to after container is removed.
-        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
-
-        // Reset display frozen state
-        mWm.mDisplayFrozen = false;
-    }
-
-    private void assertHasStartingWindow(AppWindowToken atoken) {
-        assertNotNull(atoken.startingSurface);
-        assertNotNull(atoken.startingData);
-        assertNotNull(atoken.startingWindow);
-    }
-
-    private void assertNoStartingWindow(AppWindowToken atoken) {
-        assertNull(atoken.startingSurface);
-        assertNull(atoken.startingWindow);
-        assertNull(atoken.startingData);
-        atoken.forAllWindows(windowState -> {
-            assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
-        }, true);
-    }
-
-    @Test
-    public void testCreateRemoveStartingWindow() {
-        final WindowTestUtils.TestAppWindowContainerController controller =
-                createAppWindowController();
-        controller.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-        final AppWindowToken atoken = controller.getAppWindowToken(mDisplayContent);
-        assertHasStartingWindow(atoken);
-        controller.removeStartingWindow();
-        waitUntilHandlersIdle();
-        assertNoStartingWindow(atoken);
-    }
-
-    @Test
-    public void testAddRemoveRace() {
-        // There was once a race condition between adding and removing starting windows
-        for (int i = 0; i < 1000; i++) {
-            final WindowTestUtils.TestAppWindowContainerController controller =
-                    createAppWindowController();
-            controller.addStartingWindow(mPackageName,
-                    android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                    false, false);
-            controller.removeStartingWindow();
-            waitUntilHandlersIdle();
-            assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
-
-            controller.getAppWindowToken(
-                    mDisplayContent).getParent().getParent().removeImmediately();
-        }
-    }
-
-    @Test
-    public void testTransferStartingWindow() {
-        final WindowTestUtils.TestAppWindowContainerController controller1 =
-                createAppWindowController();
-        final WindowTestUtils.TestAppWindowContainerController controller2 =
-                createAppWindowController();
-        controller1.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-        controller2.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
-                true, true, false, true, false, false);
-        waitUntilHandlersIdle();
-        assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
-        assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
-    }
-
-    @Test
-    public void testTransferStartingWindowWhileCreating() {
-        final WindowTestUtils.TestAppWindowContainerController controller1 =
-                createAppWindowController();
-        final WindowTestUtils.TestAppWindowContainerController controller2 =
-                createAppWindowController();
-        ((TestWindowManagerPolicy) mWm.mPolicy).setRunnableWhenAddingSplashScreen(() -> {
-
-            // Surprise, ...! Transfer window in the middle of the creation flow.
-            controller2.addStartingWindow(mPackageName,
-                    android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
-                    true, true, false, true, false, false);
-        });
-        controller1.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-        assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
-        assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
-    }
-
-    @Test
-    public void testTryTransferStartingWindowFromHiddenAboveToken() {
-
-        // Add two tasks on top of each other.
-        TestTaskWindowContainerController taskController =
-                new WindowTestUtils.TestTaskWindowContainerController(this);
-        final WindowTestUtils.TestAppWindowContainerController controllerTop =
-                createAppWindowController(taskController);
-        final WindowTestUtils.TestAppWindowContainerController controllerBottom =
-                createAppWindowController(taskController);
-
-        // Add a starting window.
-        controllerTop.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-
-        // Make the top one invisible, and try transfering the starting window from the top to the
-        // bottom one.
-        controllerTop.setVisibility(false, false);
-        controllerBottom.mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
-
-        // Assert that the bottom window now has the starting window.
-        assertNoStartingWindow(controllerTop.getAppWindowToken(mDisplayContent));
-        assertHasStartingWindow(controllerBottom.getAppWindowToken(mDisplayContent));
-    }
-
-    @Test
-    public void testReparent() {
-        final StackWindowController stackController =
-            createStackControllerOnDisplay(mDisplayContent);
-        final WindowTestUtils.TestTaskWindowContainerController taskController1 =
-                new WindowTestUtils.TestTaskWindowContainerController(stackController);
-        final WindowTestUtils.TestAppWindowContainerController appWindowController1 =
-                createAppWindowController(taskController1);
-        final WindowTestUtils.TestTaskWindowContainerController taskController2 =
-                new WindowTestUtils.TestTaskWindowContainerController(stackController);
-        final WindowTestUtils.TestAppWindowContainerController appWindowController2 =
-                createAppWindowController(taskController2);
-        final WindowTestUtils.TestTaskWindowContainerController taskController3 =
-                new WindowTestUtils.TestTaskWindowContainerController(stackController);
-
-        try {
-            appWindowController1.reparent(taskController1, 0);
-            fail("Should not be able to reparent to the same parent");
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
-
-        try {
-            taskController3.setContainer(null);
-            appWindowController1.reparent(taskController3, 0);
-            fail("Should not be able to reparent to a task that doesn't have a container");
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
-
-        // Reparent the app window and ensure that it is moved
-        appWindowController1.reparent(taskController2, 0);
-        assertEquals(taskController2.mContainer, appWindowController1.mContainer.getParent());
-        assertEquals(0, ((WindowTestUtils.TestAppWindowToken) appWindowController1.mContainer)
-                .positionInParent());
-        assertEquals(1, ((WindowTestUtils.TestAppWindowToken) appWindowController2.mContainer)
-                .positionInParent());
-    }
-
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController() {
-        return createAppWindowController(
-                new WindowTestUtils.TestTaskWindowContainerController(this));
-    }
-
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
-            WindowTestUtils.TestTaskWindowContainerController taskController) {
-        return new WindowTestUtils.TestAppWindowContainerController(taskController);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index 552390d..99abbf7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -31,8 +32,11 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_UNSET;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -66,6 +70,8 @@
     Task mTask;
     WindowTestUtils.TestAppWindowToken mToken;
 
+    private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
+
     @Before
     public void setUp() throws Exception {
         mStack = createTaskStackOnDisplay(mDisplayContent);
@@ -251,7 +257,7 @@
                 "closingWindow");
         closingWindow.mAnimatingExit = true;
         closingWindow.mRemoveOnExit = true;
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
 
         // We pretended that we were running an exit animation, but that should have been cleared up
@@ -261,6 +267,124 @@
     }
 
     @Test
+    public void testSetOrientation() {
+        // Assert orientation is unspecified to start.
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mToken.getOrientation());
+
+        mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
+
+        mDisplayContent.removeAppToken(mToken.token);
+        // Assert orientation is unset to after container is removed.
+        assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
+
+        // Reset display frozen state
+        mWm.mDisplayFrozen = false;
+    }
+
+    @Test
+    public void testCreateRemoveStartingWindow() {
+        mToken.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+        assertHasStartingWindow(mToken);
+        mToken.removeStartingWindow();
+        waitUntilHandlersIdle();
+        assertNoStartingWindow(mToken);
+    }
+
+    @Test
+    public void testAddRemoveRace() {
+        // There was once a race condition between adding and removing starting windows
+        for (int i = 0; i < 1000; i++) {
+            final WindowTestUtils.TestAppWindowToken appToken = createIsolatedTestAppWindowToken();
+
+            appToken.addStartingWindow(mPackageName,
+                    android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                    false, false);
+            appToken.removeStartingWindow();
+            waitUntilHandlersIdle();
+            assertNoStartingWindow(appToken);
+
+            appToken.getParent().getParent().removeImmediately();
+        }
+    }
+
+    @Test
+    public void testTransferStartingWindow() {
+        final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
+        final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
+        token1.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+        token2.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, token1.appToken.asBinder(),
+                true, true, false, true, false, false);
+        waitUntilHandlersIdle();
+        assertNoStartingWindow(token1);
+        assertHasStartingWindow(token2);
+    }
+
+    @Test
+    public void testTransferStartingWindowWhileCreating() {
+        final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
+        final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
+        ((TestWindowManagerPolicy) token1.mService.mPolicy).setRunnableWhenAddingSplashScreen(
+                () -> {
+                    // Surprise, ...! Transfer window in the middle of the creation flow.
+                    token2.addStartingWindow(mPackageName,
+                            android.R.style.Theme, null, "Test", 0, 0, 0, 0,
+                            token1.appToken.asBinder(), true, true, false,
+                            true, false, false);
+                });
+        token1.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+        assertNoStartingWindow(token1);
+        assertHasStartingWindow(token2);
+    }
+
+    private WindowTestUtils.TestAppWindowToken createIsolatedTestAppWindowToken() {
+        final TaskStack taskStack = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTaskInStack(taskStack, 0 /* userId */);
+        return createTestAppWindowTokenForGivenTask(task);
+    }
+
+    private WindowTestUtils.TestAppWindowToken createTestAppWindowTokenForGivenTask(Task task) {
+        final WindowTestUtils.TestAppWindowToken appToken =
+                WindowTestUtils.createTestAppWindowToken(mDisplayContent);
+        task.addChild(appToken, 0);
+        waitUntilHandlersIdle();
+        return appToken;
+    }
+
+    @Test
+    public void testTryTransferStartingWindowFromHiddenAboveToken() {
+        // Add two tasks on top of each other.
+        final WindowTestUtils.TestAppWindowToken tokenTop = createIsolatedTestAppWindowToken();
+        final WindowTestUtils.TestAppWindowToken tokenBottom =
+                createTestAppWindowTokenForGivenTask(tokenTop.getTask());
+
+        // Add a starting window.
+        tokenTop.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+
+        // Make the top one invisible, and try transferring the starting window from the top to the
+        // bottom one.
+        tokenTop.setVisibility(false, false);
+        tokenBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded();
+
+        // Assert that the bottom window now has the starting window.
+        assertNoStartingWindow(tokenTop);
+        assertHasStartingWindow(tokenBottom);
+    }
+
+    @Test
     public void testTransitionAnimationPositionAndBounds() {
         final Rect stackBounds = new Rect(
                 0/* left */, 0 /* top */, 1000 /* right */, 1000 /* bottom */);
@@ -285,4 +409,19 @@
         assertEquals(expectedY, outPosition.y);
         assertEquals(expectedBounds, outBounds);
     }
+
+    private void assertHasStartingWindow(AppWindowToken atoken) {
+        assertNotNull(atoken.startingSurface);
+        assertNotNull(atoken.startingData);
+        assertNotNull(atoken.startingWindow);
+    }
+
+    private void assertNoStartingWindow(AppWindowToken atoken) {
+        assertNull(atoken.startingSurface);
+        assertNull(atoken.startingWindow);
+        assertNull(atoken.startingData);
+        atoken.forAllWindows(windowState -> {
+            assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
+        }, true);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index 991981f..a6415d1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -145,7 +145,7 @@
     }
 
     @Test
-    public void testUpdateDimsAppliesSize() {
+    public void testUpdateDimsAppliesCrop() {
         mDimmer.dimAbove(mTransaction, 0.8f);
 
         int width = 100;
@@ -153,7 +153,7 @@
         Rect bounds = new Rect(0, 0, width, height);
         mDimmer.updateDims(mTransaction, bounds);
 
-        verify(mTransaction).setSize(getDimLayer(), width, height);
+        verify(mTransaction).setWindowCrop(getDimLayer(), width, height);
         verify(mTransaction).show(getDimLayer());
     }
 
@@ -242,13 +242,13 @@
         SurfaceControl dimLayer = getDimLayer();
         bounds.set(0, 0, 10, 10);
         mDimmer.updateDims(mTransaction, bounds);
+        verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height());
         verify(mTransaction, times(1)).show(dimLayer);
-        verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height());
         verify(mTransaction).setPosition(dimLayer, 0, 0);
 
         bounds.set(10, 10, 30, 30);
         mDimmer.updateDims(mTransaction, bounds);
-        verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height());
+        verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height());
         verify(mTransaction).setPosition(dimLayer, 10, 10);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
index 55e766d..ad293d9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
@@ -171,7 +171,7 @@
         try {
             final SurfaceControl surface = new SurfaceControl.Builder(appSession)
                     .setName("drag surface")
-                    .setSize(100, 100)
+                    .setBufferSize(100, 100)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java
new file mode 100644
index 0000000..241b987
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.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 com.android.server.wm;
+
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSource;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@Presubmit
+public class InsetsSourceProviderTest extends WindowTestsBase {
+
+    private InsetsSourceProvider mProvider = new InsetsSourceProvider(
+            new InsetsSource(TYPE_TOP_BAR));
+
+    @Test
+    public void testPostLayout() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        topBar.mHasSurface = true;
+        mProvider.setWindow(topBar, null);
+        mProvider.onPostLayout();
+        assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
+        assertEquals(Insets.of(0, 100, 0, 0),
+                mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+                        false /* ignoreVisibility */));
+    }
+
+    @Test
+    public void testPostLayout_invisible() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        mProvider.setWindow(topBar, null);
+        mProvider.onPostLayout();
+        assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+                        false /* ignoreVisibility */));
+    }
+
+    @Test
+    public void testPostLayout_frameProvider() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        mProvider.setWindow(topBar,
+                (displayFrames, windowState, rect) -> {
+                    rect.set(10, 10, 20, 20);
+                });
+        mProvider.onPostLayout();
+        assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java
new file mode 100644
index 0000000..7505db1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsState;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@Presubmit
+public class InsetsStateControllerTest extends WindowTestsBase {
+
+    @Test
+    public void testStripForDispatch_notOwn() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .setWindow(topBar, null);
+        topBar.setInsetProvider(
+                mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR));
+        assertNotNull(mDisplayContent.getInsetsStateController().getInsetsForDispatch(app)
+                .getSource(TYPE_TOP_BAR));
+    }
+
+    @Test
+    public void testStripForDispatch_own() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .setWindow(topBar, null);
+        topBar.setInsetProvider(
+                mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR));
+        assertEquals(new InsetsState(),
+                mDisplayContent.getInsetsStateController().getInsetsForDispatch(topBar));
+    }
+
+    @Test
+    public void testStripForDispatch_navBar() {
+        final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .setWindow(topBar, null);
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_NAVIGATION_BAR)
+                .setWindow(navBar, null);
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_IME)
+                .setWindow(ime, null);
+        assertEquals(new InsetsState(),
+                mDisplayContent.getInsetsStateController().getInsetsForDispatch(navBar));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 6833dc5..e638a6a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -225,11 +225,9 @@
             mTransaction = transaction;
             mParent = wm.makeSurfaceBuilder(mSession)
                     .setName("test surface parent")
-                    .setSize(3000, 3000)
                     .build();
             mSurface = wm.makeSurfaceBuilder(mSession)
                     .setName("test surface")
-                    .setSize(1, 1)
                     .build();
             mFinishedCallbackCalled = false;
             mLeash = null;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index d2c0765..792e8a6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -48,7 +48,7 @@
     public void testGetClosingApps_closing() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
@@ -64,9 +64,9 @@
                 "closingWindow");
         final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
                 FIRST_APPLICATION_WINDOW, "openingWindow");
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
-        openingWindow.mAppToken.setVisibility(null, true /* visible */, TRANSIT_UNSET,
+        openingWindow.mAppToken.commitVisibility(null, true /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
@@ -79,7 +79,7 @@
     public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index 1af79e4..bbf508d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -17,8 +17,6 @@
 package com.android.server.wm;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.platform.test.annotations.Presubmit;
@@ -37,6 +35,7 @@
 @Presubmit
 public class TaskWindowContainerControllerTests extends WindowTestsBase {
 
+    /* Comment out due to removal of AppWindowContainerController
     @Test
     public void testRemoveContainer() {
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -49,7 +48,9 @@
         assertNull(taskController.mContainer);
         assertNull(appController.mContainer);
     }
+    */
 
+    /* Comment out due to removal of AppWindowContainerController
     @Test
     public void testRemoveContainer_deferRemoval() {
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -74,6 +75,7 @@
         assertNull(appController.mContainer);
         assertNull(app.getController());
     }
+    */
 
     @Test
     public void testReparent() {
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
index 99deeb9..432af0d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
@@ -24,6 +24,7 @@
 import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IWindow;
+import android.view.InsetsState;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -39,6 +40,9 @@
             Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
             DisplayCutout.ParcelableWrapper displayCutout) throws RemoteException {
     }
+    @Override
+    public void insetsChanged(InsetsState insetsState) throws RemoteException {
+    }
 
     @Override
     public void moved(int newX, int newY) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 60c0459..2e47c35 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -18,7 +18,6 @@
 
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -116,12 +115,11 @@
                 ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
                 long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
                 int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
-                boolean launchTaskBehind, boolean alwaysFocusable,
-                AppWindowContainerController controller) {
+                boolean launchTaskBehind, boolean alwaysFocusable, ActivityRecord activityRecord) {
             super(service, token, activityComponent, voiceInteraction, dc,
                     inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
                     orientation, rotationAnimationHint, configChanges, launchTaskBehind,
-                    alwaysFocusable, controller);
+                    alwaysFocusable, activityRecord);
         }
 
         int getWindowsCount() {
@@ -275,45 +273,6 @@
         }
     }
 
-    public static class TestAppWindowContainerController extends AppWindowContainerController {
-
-        final IApplicationToken mToken;
-
-        TestAppWindowContainerController(TestTaskWindowContainerController taskController) {
-            this(taskController, new TestIApplicationToken());
-        }
-
-        TestAppWindowContainerController(TestTaskWindowContainerController taskController,
-                IApplicationToken token) {
-            super(taskController, token, new ComponentName("", "") /* activityComponent */,
-                    null /* listener */, 0 /* index */, SCREEN_ORIENTATION_UNSPECIFIED,
-                    true /* fullscreen */, true /* showForAllUsers */, 0 /* configChanges */,
-                    false /* voiceInteraction */, false /* launchTaskBehind */,
-                    false /* alwaysFocusable */, 0 /* targetSdkVersion */,
-                    0 /* rotationAnimationHint */, 0 /* inputDispatchingTimeoutNanos */,
-                    taskController.mService);
-            mToken = token;
-        }
-
-        @Override
-        AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
-                ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
-                long inputDispatchingTimeoutNanos,
-                boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
-                int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
-                boolean alwaysFocusable, AppWindowContainerController controller) {
-            return new TestAppWindowToken(service, token, activityComponent, voiceInteraction, dc,
-                    inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
-                    orientation,
-                    rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
-                    controller);
-        }
-
-        AppWindowToken getAppWindowToken(DisplayContent dc) {
-            return (AppWindowToken) dc.getWindowToken(mToken.asBinder());
-        }
-    }
-
     public static class TestIApplicationToken implements IApplicationToken {
 
         private final Binder mBinder = new Binder();
diff --git a/services/tests/servicestests/test-apps/Android.mk b/services/tests/servicestests/test-apps/Android.mk
deleted file mode 100644
index 5053e7d..0000000
--- a/services/tests/servicestests/test-apps/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-subdir-makefiles)
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
new file mode 100644
index 0000000..13e6644
--- /dev/null
+++ b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+    name: "ConnTestApp",
+
+    test_suites: ["device-tests"],
+
+    static_libs: ["servicestests-aidl"],
+    srcs: ["**/*.java"],
+
+    platform_apis: true,
+    certificate: "platform",
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk b/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
deleted file mode 100644
index 18b8c2d..0000000
--- a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := servicestests-aidl
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := ConnTestApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
new file mode 100644
index 0000000..ae1eca7
--- /dev/null
+++ b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+    name: "JobTestApp",
+
+    sdk_version: "current",
+
+    test_suites: ["device-tests"],
+
+    srcs: ["**/*.java"],
+
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.mk b/services/tests/servicestests/test-apps/JobTestApp/Android.mk
deleted file mode 100644
index 7893c91..0000000
--- a/services/tests/servicestests/test-apps/JobTestApp/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := JobTestApp
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
new file mode 100644
index 0000000..7257275
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp
@@ -0,0 +1,39 @@
+// 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.
+
+android_test_helper_app {
+    name: "SuspendTestApp",
+
+    test_suites: ["device-tests"],
+
+    static_libs: [
+        "androidx.test.runner",
+        "ub-uiautomator",
+    ],
+
+    srcs: [
+        "**/*.java",
+        ":servicestests-SuspendTestApp-files",
+    ],
+
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+
+    platform_apis: true,
+
+}
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
deleted file mode 100644
index ab222b9..0000000
--- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
+++ /dev/null
@@ -1,34 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.runner ub-uiautomator
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java
-
-LOCAL_PACKAGE_NAME := SuspendTestApp
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp
new file mode 100644
index 0000000..c2cb6881
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/Android.bp
@@ -0,0 +1,26 @@
+// 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.
+
+java_library {
+    name: "ShortcutManagerTestUtils",
+
+    srcs: ["src/**/*.java"],
+
+    libs: [
+        "mockito-target",
+        "android.test.runner.stubs",
+    ],
+
+    sdk_version: "test_current",
+}
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
deleted file mode 100644
index 019bcbd3..0000000
--- a/services/tests/shortcutmanagerutils/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := \
-    mockito-target \
-    android.test.runner.stubs
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := ShortcutManagerTestUtils
-
-LOCAL_SDK_VERSION := test_current
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
new file mode 100644
index 0000000..ca8cc0d
--- /dev/null
+++ b/services/tests/uiservicestests/Android.bp
@@ -0,0 +1,58 @@
+//########################################################################
+// Build FrameworksUiServicesTests package
+//########################################################################
+
+android_test {
+    name: "FrameworksUiServicesTests",
+
+    // Include test java files
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "services.accessibility",
+        "services.core",
+        "services.devicepolicy",
+        "services.net",
+        "services.usage",
+        "guava",
+        "android-support-test",
+        "mockito-target-inline-minus-junit4",
+        "platform-test-annotations",
+        "testables",
+    ],
+
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+
+    dxflags: ["--multi-dex"],
+
+    platform_apis: true,
+    test_suites: ["device-tests"],
+
+    certificate: "platform",
+
+    compile_multilib: "both",
+
+    // These are not normally accessible from apps so they must be explicitly included.
+    jni_libs: [
+        "libdexmakerjvmtiagent",
+        "libmultiplejvmtiagentsinterferenceagent",
+        "libbacktrace",
+        "libbase",
+        "libbinder",
+        "libbinderthreadstate",
+        "libc++",
+        "libcutils",
+        "liblog",
+        "liblzma",
+        "libnativehelper",
+        "libnetdaidl",
+        "libui",
+        "libunwindstack",
+        "libutils",
+    ],
+}
diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk
deleted file mode 100644
index f3f4355..0000000
--- a/services/tests/uiservicestests/Android.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-#########################################################################
-# Build FrameworksUiServicesTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include test java files and source from notifications package.
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-	$(call all-java-files-under, ../../core/java/com/android/server/notification) \
-	$(call all-java-files-under, ../../core/java/com/android/server/slice) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    services.accessibility \
-    services.core \
-    services.devicepolicy \
-    services.net \
-    services.usage \
-    guava \
-    android-support-test \
-    mockito-target-inline-minus-junit4 \
-    platform-test-annotations \
-    testables
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PACKAGE_NAME := FrameworksUiServicesTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_MULTILIB := both
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
-    libdexmakerjvmtiagent \
-    libmultiplejvmtiagentsinterferenceagent \
-    libbacktrace \
-    libbase \
-    libbinder \
-    libbinderthreadstate \
-    libc++ \
-    libcutils \
-    liblog \
-    liblzma \
-    libnativehelper \
-    libnetdaidl \
-    libui \
-    libunwindstack \
-    libutils
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-include $(BUILD_PACKAGE)
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 3266b8b..d950360 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -38,10 +38,8 @@
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.os.Build.VERSION_CODES.O_MR1;
 import static android.os.Build.VERSION_CODES.P;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -68,16 +66,15 @@
 
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
-import android.app.Application;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
+import android.app.ITransientNotification;
+import android.app.IUriGrantsManager;
 import android.app.Notification;
 import android.app.Notification.MessagingStyle.Message;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
-import android.app.ITransientNotification;
-import android.app.IUriGrantsManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
@@ -100,7 +97,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.MediaStore;
 import android.provider.Settings.Secure;
 import android.service.notification.Adjustment;
@@ -116,7 +112,6 @@
 import android.text.Html;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
-import android.util.Log;
 
 import com.android.internal.R;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -288,6 +283,7 @@
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
         when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
+        when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
 
         // write to a test file; the system file isn't readable from tests
         mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -1735,7 +1731,8 @@
     }
 
     @Test
-    public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception {
+    public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess()
+            throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
         when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
@@ -2509,6 +2506,7 @@
 
         mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
+        verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.sbn));
     }
 
     @Test
@@ -2517,8 +2515,11 @@
         mService.addNotification(r);
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((true)));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false);
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((false)));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
     }
 
@@ -2529,8 +2530,12 @@
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
         assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(false), eq((true)));
+
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false);
         assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(
+                eq(r.sbn), eq(false), eq((false)));
     }
 
     @Test
@@ -3459,11 +3464,12 @@
         ApplicationInfo info = new ApplicationInfo();
         info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
         when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
 
-        assertTrue(mService.isCallerInstantApp("any", 45770, 0));
+        assertTrue(mService.isCallerInstantApp(45770, 0));
 
         info.privateFlags = 0;
-        assertFalse(mService.isCallerInstantApp("any", 575370, 0));
+        assertFalse(mService.isCallerInstantApp(575370, 0));
     }
 
     @Test
@@ -3472,8 +3478,9 @@
         info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
         when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(10))).thenReturn(info);
         when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(null);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
 
-        assertTrue(mService.isCallerInstantApp("any", 68638450, 10));
+        assertTrue(mService.isCallerInstantApp(68638450, 10));
     }
 
     @Test
@@ -3689,4 +3696,38 @@
                 new TestableToastCallback(), 2000, 0);
         assertEquals(1, mService.mToastQueue.size());
     }
+
+    @Test
+    public void testOnNotificationSmartReplySent() {
+        final int replyIndex = 2;
+        final String reply = "Hello";
+        final boolean generatedByAssistant = true;
+
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationSmartReplySent(
+                r.getKey(), replyIndex, reply, generatedByAssistant);
+        verify(mAssistants).notifyAssistantSuggestedReplySent(
+                eq(r.sbn), eq(reply), eq(generatedByAssistant));
+    }
+
+    @Test
+    public void testOnNotificationActionClick() {
+        final int actionIndex = 2;
+        final Notification.Action action =
+                new Notification.Action.Builder(null, "text", null).build();
+        final boolean generatedByAssistant = false;
+
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        NotificationVisibility notificationVisibility =
+                NotificationVisibility.obtain(r.getKey(), 1, 2, true);
+        mService.mNotificationDelegate.onNotificationActionClick(
+                10, 10, r.getKey(), actionIndex, action, notificationVisibility,
+                generatedByAssistant);
+        verify(mAssistants).notifyAssistantActionClicked(
+                eq(r.sbn), eq(actionIndex), eq(action), eq(generatedByAssistant));
+    }
 }
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
new file mode 100644
index 0000000..cdba9a1
--- /dev/null
+++ b/services/tests/wmtests/Android.bp
@@ -0,0 +1,52 @@
+//########################################################################
+// Build WmTests package
+//########################################################################
+
+android_test {
+    name: "WmTests",
+
+    // We only want this apk build for tests.
+
+    // Include all test java files.
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "frameworks-base-testutils",
+        "services.core",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "mockito-target-extended-minus-junit4",
+        "platform-test-annotations",
+        "servicestests-utils",
+        "truth-prebuilt",
+        "testables",
+        "ub-uiautomator",
+        "hamcrest-library",
+    ],
+
+    libs: [
+        "android.test.mock",
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    // These are not normally accessible from apps so they must be explicitly included.
+    jni_libs: [
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+
+    platform_apis: true,
+    test_suites: ["device-tests"],
+
+    certificate: "platform",
+
+    dxflags: ["--multi-dex"],
+
+    optimize: {
+        enabled: false,
+    },
+
+}
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
deleted file mode 100644
index 67c2860..0000000
--- a/services/tests/wmtests/Android.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-#########################################################################
-# Build WmTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-java-files-under, ../servicestests/utils) \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    frameworks-base-testutils \
-    services.core \
-    androidx.test.runner \
-    androidx.test.rules \
-    mockito-target-extended-minus-junit4 \
-    platform-test-annotations \
-    truth-prebuilt \
-    testables \
-    ub-uiautomator \
-    hamcrest-library
-
-LOCAL_JAVA_LIBRARIES := \
-    android.test.mock \
-    android.test.base \
-    android.test.runner \
-
-# These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := \
-    libdexmakerjvmtiagent \
-    libstaticjvmtiagent \
-
-LOCAL_PACKAGE_NAME := WmTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_DX_FLAGS := --multi-dex
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index cb2a8ec..5bf3d2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -62,8 +62,9 @@
     @Test
     public void testLastFocusedStackIsUpdatedWhenMovingStack() {
         // Create a stack at bottom.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack stack = new StackBuilder(mSupervisor).setOnTop(!ON_TOP).build();
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final ActivityStack stack =
+                new StackBuilder(mRootActivityContainer).setOnTop(!ON_TOP).build();
         final ActivityStack prevFocusedStack = display.getFocusedStack();
 
         stack.moveToFront("moveStackToFront");
@@ -83,7 +84,7 @@
     @Test
     public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
         // Create a pinned stack and move to front.
-        final ActivityStack pinnedStack = mSupervisor.getDefaultDisplay().createStack(
+        final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
         final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor)
                 .setStack(pinnedStack).build();
@@ -96,7 +97,7 @@
 
         // Create a fullscreen stack and move to front.
         final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
-                mSupervisor.getDefaultDisplay());
+                mRootActivityContainer.getDefaultDisplay());
         fullscreenStack.moveToFront("moveFullscreenStackToFront");
 
         // The focused stack should be the fullscreen stack.
@@ -138,7 +139,7 @@
         final ActivityDisplay display = spy(createNewActivityDisplay());
         doReturn(false).when(display).shouldDestroyContentOnRemove();
         doReturn(true).when(display).supportsSystemDecorations();
-        mSupervisor.addChild(display, ActivityDisplay.POSITION_TOP);
+        mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP);
 
         // Put home stack on the display.
         final ActivityStack homeStack = display.createStack(
@@ -175,14 +176,14 @@
      */
     @Test
     public void testTopRunningActivity() {
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
         final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final ActivityStack stack = new StackBuilder(mSupervisor).build();
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
         final ActivityRecord activity = stack.getTopActivity();
 
         // Create empty stack on top.
         final ActivityStack emptyStack =
-                new StackBuilder(mSupervisor).setCreateActivity(false).build();
+                new StackBuilder(mRootActivityContainer).setCreateActivity(false).build();
 
         // Make sure the top running activity is not affected when keyguard is not locked.
         assertTopRunningActivity(activity, display);
@@ -225,7 +226,7 @@
      */
     @Test
     public void testAlwaysOnTopStackLocation() {
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
         final ActivityStack alwaysOnTopStack = display.createStack(WINDOWING_MODE_FREEFORM,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index c7f0521..0e30037 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -74,7 +74,7 @@
 
         // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
         // This seems to be the easiest way to create an ActivityRecord.
-        mStack = mSupervisor.getDefaultDisplay().createStack(
+        mStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
         mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 170bd33..b6f1817 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -29,10 +29,9 @@
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
 
-import static junit.framework.TestCase.assertNotNull;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -65,7 +64,7 @@
     @Before
     public void setUp() throws Exception {
         setupActivityTaskManagerService();
-        mStack = new StackBuilder(mSupervisor).build();
+        mStack = new StackBuilder(mRootActivityContainer).build();
         mTask = mStack.getChildAt(0);
         mActivity = mTask.getTopActivity();
     }
@@ -86,7 +85,7 @@
     public void testStackCleanupOnTaskRemoval() {
         mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING);
         // Stack should be gone on task removal.
-        assertNull(mService.mStackSupervisor.getStack(mStack.mStackId));
+        assertNull(mService.mRootActivityContainer.getStack(mStack.mStackId));
     }
 
     @Test
@@ -116,7 +115,7 @@
         assertFalse(pauseFound.value);
 
         // Clear focused stack
-        final ActivityDisplay display = mActivity.mStackSupervisor.getDefaultDisplay();
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
         when(display.getFocusedStack()).thenReturn(null);
 
         // In the unfocused stack, the activity should move to paused.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index 8a6d587..78a67d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -38,8 +38,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.wm.ActivityStackSupervisor
-        .MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -83,78 +82,11 @@
     @Before
     public void setUp() throws Exception {
         setupActivityTaskManagerService();
-        mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
     }
 
     /**
-     * This test ensures that we do not try to restore a task based off an invalid task id. We
-     * should expect {@code null} to be returned in this case.
-     */
-    @Test
-    public void testRestoringInvalidTask() {
-        ((TestActivityDisplay) mSupervisor.getDefaultDisplay()).removeAllTasks();
-        TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
-                MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
-        assertNull(task);
-    }
-
-    /**
-     * This test ensures that an existing task in the pinned stack is moved to the fullscreen
-     * activity stack when a new task is added.
-     */
-    @Test
-    public void testReplacingTaskInPinnedStack() {
-        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(mFullscreenStack).build();
-        final TaskRecord firstTask = firstActivity.getTask();
-
-        final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(mFullscreenStack).build();
-        final TaskRecord secondTask = secondActivity.getTask();
-
-        mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
-
-        // Ensure full screen stack has both tasks.
-        ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
-
-        // Move first activity to pinned stack.
-        final Rect sourceBounds = new Rect();
-        mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds,
-                0f /*aspectRatio*/, "initialMove");
-
-        final ActivityDisplay display = mFullscreenStack.getDisplay();
-        ActivityStack pinnedStack = display.getPinnedStack();
-        // Ensure a task has moved over.
-        ensureStackPlacement(pinnedStack, firstTask);
-        ensureStackPlacement(mFullscreenStack, secondTask);
-
-        // Move second activity to pinned stack.
-        mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds,
-                0f /*aspectRatio*/, "secondMove");
-
-        // Need to get stacks again as a new instance might have been created.
-        pinnedStack = display.getPinnedStack();
-        mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-        // Ensure stacks have swapped tasks.
-        ensureStackPlacement(pinnedStack, secondTask);
-        ensureStackPlacement(mFullscreenStack, firstTask);
-    }
-
-    private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
-        final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
-        assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
-
-        if (tasks == null) {
-            return;
-        }
-
-        for (TaskRecord task : tasks) {
-            assertTrue(stackTasks.contains(task));
-        }
-    }
-
-    /**
      * Ensures that an activity is removed from the stopping activities list once it is resumed.
      */
     @Test
@@ -179,7 +111,7 @@
 
         // #notifyAll will be called on the ActivityManagerService. we must hold the object lock
         // when this happens.
-        synchronized (mSupervisor.mService.mGlobalLock) {
+        synchronized (mService.mGlobalLock) {
             final WaitResult taskToFrontWait = new WaitResult();
             mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
             mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
@@ -198,334 +130,4 @@
             assertEquals(deliverToTopWait.who, firstActivity.realActivity);
         }
     }
-
-    @Test
-    public void testApplySleepTokensLocked() {
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final ActivityStack stack = mock(ActivityStack.class);
-        display.addChild(stack, 0 /* position */);
-
-        // Make sure we wake and resume in the case the display is turning on and the keyguard is
-        // not showing.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                false /* keyguardShowing */, true /* expectWakeFromSleep */,
-                true /* expectResumeTopActivity */);
-
-        // Make sure we wake and don't resume when the display is turning on and the keyguard is
-        // showing.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                true /* keyguardShowing */, true /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-
-        // Make sure we wake and don't resume when the display is turning on and the keyguard is
-        // not showing as unfocused.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, false /* isFocusedStack */,
-                false /* keyguardShowing */, true /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-
-        // Should not do anything if the display state hasn't changed.
-        verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                false /* keyguardShowing */, false /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-    }
-
-    private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
-            ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
-            boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
-            boolean expectResumeTopActivity) {
-        reset(stack);
-
-        doReturn(displayShouldSleep).when(display).shouldSleep();
-        doReturn(displaySleeping).when(display).isSleeping();
-        doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
-
-        doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
-        doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
-        mSupervisor.applySleepTokensLocked(true);
-        verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
-        verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
-                null /* target */, null /* targetOptions */);
-    }
-
-    /**
-     * Verifies that removal of activity with task and stack is done correctly.
-     */
-    @Test
-    public void testRemovingStackOnAppCrash() {
-        final ActivityDisplay defaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
-        final int originalStackCount = defaultDisplay.getChildCount();
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(stack).build();
-
-        assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
-
-        // Let's pretend that the app has crashed.
-        firstActivity.app.setThread(null);
-        mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test");
-
-        // Verify that the stack was removed.
-        assertEquals(originalStackCount, defaultDisplay.getChildCount());
-    }
-
-    @Test
-    public void testFocusability() {
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(stack).build();
-
-        // Under split screen primary we should be focusable when not minimized
-        mService.mStackSupervisor.setDockedStackMinimized(false);
-        assertTrue(stack.isFocusable());
-        assertTrue(activity.isFocusable());
-
-        // Under split screen primary we should not be focusable when minimized
-        mService.mStackSupervisor.setDockedStackMinimized(true);
-        assertFalse(stack.isFocusable());
-        assertFalse(activity.isFocusable());
-
-        final ActivityStack pinnedStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(pinnedStack).build();
-
-        // We should not be focusable when in pinned mode
-        assertFalse(pinnedStack.isFocusable());
-        assertFalse(pinnedActivity.isFocusable());
-
-        // Add flag forcing focusability.
-        pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
-
-        // We should not be focusable when in pinned mode
-        assertTrue(pinnedStack.isFocusable());
-        assertTrue(pinnedActivity.isFocusable());
-
-        // Without the overridding activity, stack should not be focusable.
-        pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability",
-                REMOVE_TASK_MODE_DESTROYING);
-        assertFalse(pinnedStack.isFocusable());
-    }
-
-    /**
-     * Verify that split-screen primary stack will be chosen if activity is launched that targets
-     * split-screen secondary, but a matching existing instance is found on top of split-screen
-     * primary stack.
-     */
-    @Test
-    public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
-        // Create primary split-screen stack with a task and an activity.
-        final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
-                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
-                        true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
-        final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
-
-        // Find a launch stack for the top activity in split-screen primary, while requesting
-        // split-screen secondary.
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
-        final ActivityStack result = mSupervisor.getLaunchStack(r, options, task, true /* onTop */);
-
-        // Assert that the primary stack is returned.
-        assertEquals(primaryStack, result);
-    }
-
-    /**
-     * Verify split-screen primary stack & task can resized by
-     * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
-     */
-    @Test
-    public void testResizeDockedStackForSplitScreenPrimary() {
-        final Rect taskSize = new Rect(0, 0, 600, 600);
-        final Rect stackSize = new Rect(0, 0, 300, 300);
-
-        // Create primary split-screen stack with a task.
-        final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
-                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
-                        true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
-
-        // Resize dock stack.
-        mService.resizeDockedStack(stackSize, taskSize, null, null, null);
-
-        // Verify dock stack & its task bounds if is equal as resized result.
-        assertEquals(primaryStack.getBounds(), stackSize);
-        assertEquals(task.getBounds(), taskSize);
-    }
-
-    /**
-     * Verify that home stack would be moved to front when the top activity is Recents.
-     */
-    @Test
-    public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
-        // Create stack/task on default display.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final TestActivityStack targetStack = new StackBuilder(mSupervisor).setOnTop(false).build();
-        final TaskRecord targetTask = targetStack.getChildAt(0);
-
-        // Create Recents on top of the display.
-        final ActivityStack stack =
-                new StackBuilder(mSupervisor).setActivityType(ACTIVITY_TYPE_RECENTS).build();
-
-        final String reason = "findTaskToMoveToFront";
-        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
-                false);
-
-        verify(display).moveHomeStackToFront(contains(reason));
-    }
-
-    /**
-     * Verify that home stack won't be moved to front if the top activity on other display is
-     * Recents.
-     */
-    @Test
-    public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
-        // Create stack/task on default display.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
-
-        // Create Recents on secondary display.
-        final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
-                ActivityDisplay.POSITION_TOP);
-        final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
-        new ActivityBuilder(mService).setTask(task).build();
-
-        final String reason = "findTaskToMoveToFront";
-        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
-                false);
-
-        verify(display, never()).moveHomeStackToFront(contains(reason));
-    }
-
-    /**
-     * Verify if a stack is not at the topmost position, it should be able to resume its activity if
-     * the stack is the top focused.
-     */
-    @Test
-    public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
-        // Create a stack at bottom.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */));
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
-        final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
-        display.positionChildAtBottom(targetStack);
-
-        // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
-        // is the current top focused stack.
-        assertFalse(targetStack.isTopStackOnDisplay());
-        doReturn(targetStack).when(mSupervisor).getTopDisplayFocusedStack();
-
-        // Use the stack as target to resume.
-        mSupervisor.resumeFocusedStacksTopActivitiesLocked(
-                targetStack, activity, null /* targetOptions */);
-
-        // Verify the target stack should resume its activity.
-        verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
-                eq(activity), eq(null /* targetOptions */));
-    }
-
-    /**
-     * Tests home activities that targeted sdk before Q cannot start on secondary display.
-     */
-    @Test
-    public void testStartHomeTargetSdkBeforeQ() throws Exception {
-        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
-        mSupervisor.addChild(secondDisplay, POSITION_TOP);
-        doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
-        final ActivityInfo info = new ActivityInfo();
-        info.launchMode = LAUNCH_MULTIPLE;
-        info.applicationInfo = new ApplicationInfo();
-        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
-        assertTrue(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
-                false /* allowInstrumenting */));
-
-        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
-        assertFalse(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
-                false /* allowInstrumenting */));
-    }
-
-    /**
-     * Tests that home activities can be started on the displays that supports system decorations.
-     */
-    @Test
-    public void testStartHomeOnAllDisplays() {
-        // Create secondary displays.
-        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
-        mSupervisor.addChild(secondDisplay, POSITION_TOP);
-        doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
-        // Create mock tasks and other necessary mocks.
-        TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
-        final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
-        TaskRecord.setTaskRecordFactory(factory);
-        doAnswer(i -> taskBuilder.build()).when(factory)
-                .create(any(), anyInt(), any(), any(), any(), any());
-        doReturn(true).when(mService.mStackSupervisor)
-                .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
-        doReturn(true).when(mSupervisor).canStartHomeOnDisplay(any(), anyInt(), anyBoolean());
-
-        mSupervisor.startHomeOnAllDisplays(0, "testStartHome");
-
-        assertTrue(mSupervisor.getDefaultDisplay().getTopStack().isActivityTypeHome());
-        assertNotNull(secondDisplay.getTopStack());
-        assertTrue(secondDisplay.getTopStack().isActivityTypeHome());
-    }
-
-    /**
-     * Tests that home activities won't be started before booting when display added.
-     */
-    @Test
-    public void testNotStartHomeBeforeBoot() {
-        final int displayId = 1;
-        final boolean isBooting = mService.mAmInternal.isBooting();
-        final boolean isBooted = mService.mAmInternal.isBooted();
-        try {
-            mService.mAmInternal.setBooting(false);
-            mService.mAmInternal.setBooted(false);
-            mSupervisor.onDisplayAdded(displayId);
-            verify(mSupervisor, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
-        } finally {
-            mService.mAmInternal.setBooting(isBooting);
-            mService.mAmInternal.setBooted(isBooted);
-        }
-    }
-
-    /**
-     * Tests whether home can be started if being instrumented.
-     */
-    @Test
-    public void testCanStartHomeWhenInstrumented() {
-        final ActivityInfo info = new ActivityInfo();
-        info.applicationInfo = new ApplicationInfo();
-        final WindowProcessController app = mock(WindowProcessController.class);
-        doReturn(app).when(mService).getProcessController(any(), anyInt());
-
-        // Can not start home if we don't want to start home while home is being instrumented.
-        doReturn(true).when(app).isInstrumenting();
-        assertFalse(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
-                false /* allowInstrumenting*/));
-
-        // Can start home for other cases.
-        assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
-                true /* allowInstrumenting*/));
-
-        doReturn(false).when(app).isInstrumenting();
-        assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
-                false /* allowInstrumenting*/));
-        assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
-                true /* allowInstrumenting*/));
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 2fe45b8..0da0b24 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -74,7 +74,7 @@
     @Before
     public void setUp() throws Exception {
         setupActivityTaskManagerService();
-        mDefaultDisplay = mSupervisor.getDefaultDisplay();
+        mDefaultDisplay = mRootActivityContainer.getDefaultDisplay();
         mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */));
         mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
@@ -112,7 +112,7 @@
         r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
         assertEquals(r, mStack.getResumedActivity());
 
-        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         mTask.reparent(destStack, true /* toTop */, TaskRecord.REPARENT_KEEP_STACK_AT_FRONT,
@@ -130,7 +130,7 @@
         r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
         assertEquals(r, mStack.getResumedActivity());
 
-        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
 
@@ -239,8 +239,8 @@
                 .setUid(UserHandle.PER_USER_RANGE * 2).build();
         taskOverlay.mTaskOverlay = true;
 
-        final ActivityStackSupervisor.FindTaskResult result =
-                new ActivityStackSupervisor.FindTaskResult();
+        final RootActivityContainer.FindTaskResult result =
+                new RootActivityContainer.FindTaskResult();
         mStack.findTaskLocked(r, result);
 
         assertEquals(r, task.getTopActivity(false /* includeOverlays */));
@@ -700,7 +700,7 @@
         // should be destroyed immediately with updating configuration to restore original state.
         final ActivityRecord activity1 = finishCurrentActivity(stack1);
         assertEquals(DESTROYING, activity1.getState());
-        verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */,
+        verify(mRootActivityContainer).ensureVisibilityAndConfig(eq(null) /* starting */,
                 eq(display.mDisplayId), anyBoolean(), anyBoolean());
     }
 
@@ -778,7 +778,7 @@
         final ActivityDisplay display = mock(ActivityDisplay.class);
         final KeyguardController keyguardController = mSupervisor.getKeyguardController();
 
-        doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
+        doReturn(display).when(mRootActivityContainer).getActivityDisplay(anyInt());
         doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
         doReturn(displaySleeping).when(display).isSleeping();
         doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index 9d93c85..2ba2fdb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -74,7 +74,7 @@
         final ActivityRecord activity = new ActivityBuilder(mService).build();
         final ActivityRecord source = new ActivityBuilder(mService).build();
         final int startFlags = random.nextInt();
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        final ActivityStack stack = mService.mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final WindowProcessController wpc = new WindowProcessController(mService,
                 mService.mContext.getApplicationInfo(), "name", 12345,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 27fa20b..350114c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -90,6 +90,8 @@
     @Mock
     private ActivityTaskManagerService mService;
     @Mock
+    private RootActivityContainer mRootActivityContainer;
+    @Mock
     private ActivityStackSupervisor mSupervisor;
     @Mock
     private DevicePolicyManagerInternal mDevicePolicyManager;
@@ -111,7 +113,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mService.mAmInternal = mAmInternal;
-        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext);
+        mInterceptor = new ActivityStartInterceptor(
+                mService, mSupervisor, mRootActivityContainer, mContext);
         mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
                 TEST_START_FLAGS, TEST_CALLING_PACKAGE);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 50aa541..d78fbcd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.app.ActivityManager.START_ABORTED;
 import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
 import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
@@ -69,6 +70,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.service.voice.IVoiceInteractionSession;
@@ -110,6 +112,7 @@
     private static final int FAKE_CALLING_UID = 666;
     private static final int FAKE_REAL_CALLING_UID = 667;
     private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
+    private static final int UNIMPORTANT_UID = 12345;
 
     @Before
     public void setUp() throws Exception {
@@ -125,7 +128,7 @@
     public void testUpdateLaunchBounds() {
         // When in a non-resizeable stack, the task bounds should be updated.
         final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+                .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
                 .build();
         final Rect bounds = new Rect(10, 10, 100, 100);
@@ -136,7 +139,7 @@
 
         // When in a resizeable stack, the stack bounds should be updated as well.
         final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+                .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
                 .build();
         assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class);
@@ -314,7 +317,7 @@
      * Creates a {@link ActivityStarter} with default parameters and necessary mocks.
      *
      * @param launchFlags The intent flags to launch activity.
-     * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for
+     * @param mockGetLaunchStack Whether to mock {@link RootActivityContainer#getLaunchStack} for
      *                           always launching to the testing stack. Set to false when allowing
      *                           the activity can be launched to any stack that is decided by real
      *                           implementation.
@@ -323,14 +326,14 @@
     private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
             boolean mockGetLaunchStack) {
         // always allow test to start activity.
-        doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
+        doReturn(true).when(mSupervisor).checkStartAnyActivityPermission(
                 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
                 anyBoolean(), anyBoolean(), any(), any(), any());
 
         // instrument the stack and task used.
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+        final TaskRecord task = new TaskBuilder(mSupervisor)
                 .setCreateStack(false)
                 .build();
 
@@ -343,9 +346,9 @@
 
         if (mockGetLaunchStack) {
             // Direct starter to use spy stack.
-            doReturn(stack).when(mService.mStackSupervisor)
+            doReturn(stack).when(mRootActivityContainer)
                     .getLaunchStack(any(), any(), any(), anyBoolean());
-            doReturn(stack).when(mService.mStackSupervisor)
+            doReturn(stack).when(mRootActivityContainer)
                     .getLaunchStack(any(), any(), any(), anyBoolean(), any());
         }
 
@@ -441,7 +444,7 @@
         final ActivityStack focusStack = focusActivity.getStack();
         focusStack.moveToFront("testSplitScreenDeliverToTop");
 
-        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+        doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt());
 
         final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
 
@@ -473,7 +476,7 @@
         // Enter split-screen. Primary stack should have focus.
         focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
 
-        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+        doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt());
 
         final int result = starter.setReason("testSplitScreenMoveToFront").execute();
 
@@ -486,7 +489,7 @@
      */
     @Test
     public void testTaskModeViolation() {
-        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+        final ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
         ((TestActivityDisplay) display).removeAllTasks();
         assertNoTasks(display);
 
@@ -551,6 +554,85 @@
     }
 
     /**
+     * This test ensures that unsupported usecases aren't aborted when background starts are
+     * allowed.
+     */
+    @Test
+    public void testBackgroundActivityStartsAllowed_noStartsAborted() {
+        doReturn(true).when(mService).isBackgroundActivityStartsEnabled();
+
+        runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted",
+                false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, false, false);
+    }
+
+    /**
+     * This test ensures that unsupported usecases are aborted when background starts are
+     * disallowed.
+     */
+    @Test
+    public void testBackgroundActivityStartsDisallowed_unsupportedStartsAborted() {
+        doReturn(false).when(mService).isBackgroundActivityStartsEnabled();
+
+        runAndVerifyBackgroundActivityStartsSubtest("disallowed_unsupportedUsecase_aborted",
+                true, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, false, false);
+    }
+
+    /**
+     * This test ensures that supported usecases aren't aborted when background starts are
+     * disallowed.
+     * The scenarios each have only one condidion that makes them supported.
+     */
+    @Test
+    public void testBackgroundActivityStartsDisallowed_supportedStartsNotAborted() {
+        doReturn(false).when(mService).isBackgroundActivityStartsEnabled();
+
+        runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted",
+                false, Process.ROOT_UID, false, PROCESS_STATE_TOP + 1, false, false);
+        runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted",
+                false, Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1, false, false);
+        runAndVerifyBackgroundActivityStartsSubtest("disallowed_hasVisibleWindow_notAborted",
+                false, UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1, false, false);
+        runAndVerifyBackgroundActivityStartsSubtest("disallowed_processStateTop_notAborted",
+                false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP, false, false);
+        runAndVerifyBackgroundActivityStartsSubtest("disallowed_hasForegroundActivities_notAborted",
+                false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, true, false);
+        runAndVerifyBackgroundActivityStartsSubtest("disallowed_callerIsRecents_notAborted",
+                false, UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, false, true);
+    }
+
+    private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
+            int testCallingUid, boolean hasVisibleWindow, int procState,
+            boolean hasForegroundActivities, boolean callerIsRecents) {
+        // window visibility
+        doReturn(hasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
+                testCallingUid);
+        // process importance
+        doReturn(procState).when(mService).getUidStateLocked(testCallingUid);
+        // foreground activities
+        final IApplicationThread caller = mock(IApplicationThread.class);
+        final ApplicationInfo ai = new ApplicationInfo();
+        ai.uid = testCallingUid;
+        final WindowProcessController callerApp =
+                new WindowProcessController(mService, ai, null, testCallingUid, -1, null, null);
+        callerApp.setHasForegroundActivities(hasForegroundActivities);
+        doReturn(callerApp).when(mService).getProcessController(caller);
+        // caller is recents
+        RecentTasks recentTasks = mock(RecentTasks.class);
+        mService.mStackSupervisor.setRecentTasks(recentTasks);
+        doReturn(callerIsRecents).when(recentTasks).isCallerRecents(testCallingUid);
+
+        final ActivityOptions options = spy(ActivityOptions.makeBasic());
+        ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK).setCaller(caller)
+                .setCallingUid(testCallingUid).setActivityOptions(new SafeActivityOptions(options));
+
+        final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute();
+
+        assertEquals(ActivityStarter.getExternalResult(
+                shouldHaveAborted ? START_ABORTED : START_SUCCESS), result);
+        verify(options, times(shouldHaveAborted ? 1 : 0)).abort();
+    }
+
+    /**
      * This test ensures that when starting an existing single task activity on secondary display
      * which is not the top focused display, it should deliver new intent to the activity and not
      * create a new stack.
@@ -562,7 +644,7 @@
 
         // Create a secondary display at bottom.
         final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
-        mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM);
+        mRootActivityContainer.addChild(secondaryDisplay, POSITION_BOTTOM);
         final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
@@ -600,7 +682,7 @@
 
         // Create a secondary display with an activity.
         final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
-        mSupervisor.addChild(secondaryDisplay, POSITION_TOP);
+        mRootActivityContainer.addChild(secondaryDisplay, POSITION_TOP);
         final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
                 secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
                         ACTIVITY_TYPE_STANDARD, false /* onTop */));
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 caabdbd..ead9731 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -35,6 +35,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 
@@ -66,6 +67,8 @@
 import com.android.server.AttributeCache;
 import com.android.server.ServiceThread;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.am.PendingIntentController;
+import com.android.server.firewall.IntentFirewall;
 import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.junit.After;
@@ -91,6 +94,7 @@
     final TestInjector mTestInjector = new TestInjector();
 
     ActivityTaskManagerService mService;
+    RootActivityContainer mRootActivityContainer;
     ActivityStackSupervisor mSupervisor;
 
     // Default package name
@@ -115,27 +119,14 @@
     }
 
     ActivityTaskManagerService createActivityTaskManagerService() {
-        final TestActivityTaskManagerService atm =
-                spy(new TestActivityTaskManagerService(mContext));
-        setupActivityManagerService(atm);
-        return atm;
+        mService = new TestActivityTaskManagerService(mContext);
+        mSupervisor = mService.mStackSupervisor;
+        mRootActivityContainer = mService.mRootActivityContainer;
+        return mService;
     }
 
     void setupActivityTaskManagerService() {
-        mService = createActivityTaskManagerService();
-        mSupervisor = mService.mStackSupervisor;
-    }
-
-    ActivityManagerService createActivityManagerService() {
-        final TestActivityTaskManagerService atm =
-                spy(new TestActivityTaskManagerService(mContext));
-        return setupActivityManagerService(atm);
-    }
-
-    ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
-        final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
-        setupActivityManagerService(am, atm);
-        return am;
+        createActivityTaskManagerService();
     }
 
     /** Creates a {@link TestActivityDisplay}. */
@@ -150,36 +141,10 @@
     /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
     TestActivityDisplay addNewActivityDisplayAt(int position) {
         final TestActivityDisplay display = createNewActivityDisplay();
-        mSupervisor.addChild(display, position);
+        mRootActivityContainer.addChild(display, position);
         return display;
     }
 
-    void setupActivityManagerService(
-            TestActivityManagerService am, TestActivityTaskManagerService atm) {
-        atm.setActivityManagerService(am.mIntentFirewall, am.mPendingIntentController);
-        atm.mAmInternal = am.getLocalService();
-        am.mAtmInternal = atm.getLocalService();
-        // Makes sure the supervisor is using with the spy object.
-        atm.mStackSupervisor.setService(atm);
-        doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
-        doReturn(mock(IPackageManager.class)).when(atm).getPackageManager();
-        PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
-        doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
-        doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
-        doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
-        am.mActivityTaskManager = atm;
-        am.mWindowManager = prepareMockWindowManager();
-        atm.setWindowManager(am.mWindowManager);
-
-        // Put a home stack on the default display, so that we'll always have something focusable.
-        final TestActivityStackSupervisor supervisor =
-                (TestActivityStackSupervisor) atm.mStackSupervisor;
-        supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-        final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
-                .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
-        new ActivityBuilder(atm).setTask(task).build();
-    }
-
     /**
      * Builder for creating new activities.
      */
@@ -268,7 +233,9 @@
                     aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
                     0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
                     mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
-            activity.mWindowContainerController = mock(AppWindowContainerController.class);
+            spyOn(activity);
+            activity.mAppWindowToken = mock(AppWindowToken.class);
+            doNothing().when(activity).removeWindowContainer();
 
             if (mTaskRecord != null) {
                 mTaskRecord.addActivityToTop(activity);
@@ -354,7 +321,7 @@
 
         TaskRecord build() {
             if (mStack == null && mCreateStack) {
-                mStack = mSupervisor.getDefaultDisplay().createStack(
+                mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
             }
 
@@ -405,23 +372,72 @@
         }
     }
 
-    protected static class TestActivityTaskManagerService extends ActivityTaskManagerService {
-        private LockTaskController mLockTaskController;
-        private ActivityTaskManagerInternal mInternal;
+    protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
         private PackageManagerInternal mPmInternal;
 
         // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
         // We keep the reference in order to prevent creating it twice.
-        private ActivityStackSupervisor mTestStackSupervisor;
+        ActivityStackSupervisor mTestStackSupervisor;
+
+        ActivityDisplay mDefaultDisplay;
 
         TestActivityTaskManagerService(Context context) {
             super(context);
+            spyOn(this);
+
+            mUgmInternal = mock(UriGrantsManagerInternal.class);
+
             mSupportsMultiWindow = true;
             mSupportsMultiDisplay = true;
             mSupportsSplitScreenMultiWindow = true;
             mSupportsFreeformWindowManagement = true;
             mSupportsPictureInPicture = true;
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+            final TestActivityManagerService am =
+                    new TestActivityManagerService(mTestInjector, this);
+
+            spyOn(getLifecycleManager());
+            spyOn(getLockTaskController());
+            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+            // allow background activity starts by default
+            doReturn(true).when(this).isBackgroundActivityStartsEnabled();
+        }
+
+        void setActivityManagerService(IntentFirewall intentFirewall,
+                PendingIntentController intentController, ActivityManagerInternal amInternal,
+                WindowManagerService wm) {
+            mAmInternal = amInternal;
+            setActivityManagerService(intentFirewall, intentController);
+            initRootActivityContainerMocks(wm);
+            setWindowManager(wm);
+        }
+
+        void initRootActivityContainerMocks(WindowManagerService wm) {
+            spyOn(mRootActivityContainer);
+            mRootActivityContainer.setWindowContainer(mock(RootWindowContainer.class));
+            mRootActivityContainer.mWindowManager = wm;
+            mRootActivityContainer.mDisplayManager =
+                    (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+            doNothing().when(mRootActivityContainer).setWindowManager(any());
+            // Invoked during {@link ActivityStack} creation.
+            doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
+            // Always keep things awake.
+            doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
+            // Called when moving activity to pinned stack.
+            doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
+                    anyBoolean());
+
+            // 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);
+            spyOn(mDefaultDisplay);
+            mRootActivityContainer.addChild(mDefaultDisplay, ActivityDisplay.POSITION_TOP);
+            mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+            final TaskRecord task = new TaskBuilder(mStackSupervisor)
+                    .setStack(mDefaultDisplay.getHomeStack()).build();
+            new ActivityBuilder(this).setTask(task).build();
+
+            doReturn(mDefaultDisplay).when(mRootActivityContainer).getDefaultDisplay();
         }
 
         @Override
@@ -430,54 +446,17 @@
         }
 
         @Override
-        public LockTaskController getLockTaskController() {
-            if (mLockTaskController == null) {
-                mLockTaskController = spy(super.getLockTaskController());
-            }
-
-            return mLockTaskController;
-        }
-
-        @Override
         void updateUsageStats(ActivityRecord component, boolean resumed) {
         }
 
         @Override
-        protected final ActivityStackSupervisor createStackSupervisor() {
+        protected ActivityStackSupervisor createStackSupervisor() {
             if (mTestStackSupervisor == null) {
-                final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
-                final KeyguardController keyguardController = mock(KeyguardController.class);
-
-                // Invoked during {@link ActivityStack} creation.
-                doNothing().when(supervisor).updateUIDsPresentOnDisplay();
-                // Always keep things awake.
-                doReturn(true).when(supervisor).hasAwakeDisplay();
-                // Called when moving activity to pinned stack.
-                doNothing().when(supervisor).ensureActivitiesVisibleLocked(any(), anyInt(),
-                        anyBoolean());
-                // Do not schedule idle timeouts
-                doNothing().when(supervisor).scheduleIdleTimeoutLocked(any());
-                // unit test version does not handle launch wake lock
-                doNothing().when(supervisor).acquireLaunchWakelock();
-                doReturn(keyguardController).when(supervisor).getKeyguardController();
-
-                supervisor.initialize();
-                mTestStackSupervisor = supervisor;
+                mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
             }
             return mTestStackSupervisor;
         }
 
-        protected ActivityStackSupervisor createTestSupervisor() {
-            return new TestActivityStackSupervisor(this, mH.getLooper());
-        }
-
-        ActivityTaskManagerInternal getLocalService() {
-            if (mInternal == null) {
-                mInternal = new ActivityTaskManagerService.LocalService();
-            }
-            return mInternal;
-        }
-
         @Override
         PackageManagerInternal getPackageManagerInternalLocked() {
             if (mPmInternal == null) {
@@ -524,24 +503,31 @@
         }
     }
 
+    // TODO: Replace this with a mock object since we are no longer in AMS package.
     /**
      * An {@link ActivityManagerService} subclass which provides a test
      * {@link ActivityStackSupervisor}.
      */
-    static class TestActivityManagerService extends ActivityManagerService {
+    class TestActivityManagerService extends ActivityManagerService {
 
-        private ActivityManagerInternal mInternal;
-
-        TestActivityManagerService(TestInjector testInjector) {
+        TestActivityManagerService(TestInjector testInjector, TestActivityTaskManagerService atm) {
             super(testInjector, testInjector.mHandlerThread);
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
-        }
+            spyOn(this);
 
-        ActivityManagerInternal getLocalService() {
-            if (mInternal == null) {
-                mInternal = new LocalService();
-            }
-            return mInternal;
+            mWindowManager = prepareMockWindowManager();
+            mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+            atm.setActivityManagerService(mIntentFirewall, mPendingIntentController,
+                    new LocalService(), mWindowManager);
+
+            mActivityTaskManager = atm;
+            mAtmInternal = atm.mInternal;
+
+            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+            PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
+            doReturn(mockPackageManager).when(this).getPackageManagerInternalLocked();
+            doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
+            doNothing().when(this).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
         }
     }
 
@@ -549,24 +535,22 @@
      * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
      * setup not available in the test environment. Also specifies an injector for
      */
-    protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
-        private ActivityDisplay mDisplay;
+    protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
         private KeyguardController mKeyguardController;
 
-        public TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+        TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
             super(service, looper);
-            mDisplayManager =
-                    (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
+            spyOn(this);
             mWindowManager = prepareMockWindowManager();
             mKeyguardController = mock(KeyguardController.class);
-            setWindowContainerController(mock(RootWindowContainerController.class));
-        }
 
-        @Override
-        public void initialize() {
-            super.initialize();
-            mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY));
-            addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+            // Do not schedule idle timeouts
+            doNothing().when(this).scheduleIdleTimeoutLocked(any());
+            // unit test version does not handle launch wake lock
+            doNothing().when(this).acquireLaunchWakelock();
+            doReturn(mKeyguardController).when(this).getKeyguardController();
+
+            initialize();
         }
 
         @Override
@@ -575,11 +559,6 @@
         }
 
         @Override
-        ActivityDisplay getDefaultDisplay() {
-            return mDisplay;
-        }
-
-        @Override
         void setWindowManager(WindowManagerService wm) {
             mWindowManager = wm;
         }
@@ -596,7 +575,7 @@
                 DisplayInfo info) {
             if (displayId == DEFAULT_DISPLAY) {
                 return new TestActivityDisplay(supervisor,
-                        supervisor.mDisplayManager.getDisplay(displayId));
+                        supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
             }
             final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                     info, DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -604,7 +583,7 @@
         }
 
         TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
-            super(supervisor, display);
+            super(supervisor.mService.mRootActivityContainer, display);
             // Normally this comes from display-properties as exposed by WM. Without that, just
             // hard-code to FULLSCREEN for tests.
             setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -615,7 +594,7 @@
         @Override
         <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
                 int stackId, boolean onTop) {
-            return new StackBuilder(mSupervisor).setDisplay(this)
+            return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this)
                     .setWindowingMode(windowingMode).setActivityType(activityType)
                     .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
         }
@@ -757,8 +736,8 @@
         }
     }
 
-    protected static class StackBuilder {
-        private final ActivityStackSupervisor mSupervisor;
+    static class StackBuilder {
+        private final RootActivityContainer mRootActivityContainer;
         private ActivityDisplay mDisplay;
         private int mStackId = -1;
         private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
@@ -766,9 +745,9 @@
         private boolean mOnTop = true;
         private boolean mCreateActivity = true;
 
-        StackBuilder(ActivityStackSupervisor supervisor) {
-            mSupervisor = supervisor;
-            mDisplay = mSupervisor.getDefaultDisplay();
+        StackBuilder(RootActivityContainer root) {
+            mRootActivityContainer = root;
+            mDisplay = mRootActivityContainer.getDefaultDisplay();
         }
 
         StackBuilder setWindowingMode(int windowingMode) {
@@ -805,7 +784,8 @@
         <T extends ActivityStack> T build() {
             final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
             if (mWindowingMode == WINDOWING_MODE_PINNED) {
-                return (T) new PinnedActivityStack(mDisplay, stackId, mSupervisor, mOnTop) {
+                return (T) new PinnedActivityStack(mDisplay, stackId,
+                        mRootActivityContainer.mStackSupervisor, mOnTop) {
                     @Override
                     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
                         return new Rect(50, 50, 100, 100);
@@ -821,7 +801,8 @@
                     }
                 };
             } else {
-                return (T) new TestActivityStack(mDisplay, stackId, mSupervisor, mWindowingMode,
+                return (T) new TestActivityStack(mDisplay, stackId,
+                        mRootActivityContainer.mStackSupervisor, mWindowingMode,
                         mActivityType, mOnTop, mCreateActivity);
             }
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index dc22bc1..2c3c66b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -110,8 +110,9 @@
         final DisplayInfo info = new DisplayInfo();
         info.uniqueId = mDisplayUniqueId;
         mTestDisplay = createNewActivityDisplay(info);
-        mSupervisor.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
-        when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(mTestDisplay);
+        mRootActivityContainer.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
+        when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId)))
+                .thenReturn(mTestDisplay);
 
         ActivityStack stack = mTestDisplay.createStack(TEST_WINDOWING_MODE,
                 ACTIVITY_TYPE_STANDARD, /* onTop */ true);
@@ -184,7 +185,7 @@
     public void testReturnsEmptyDisplayIfDisplayIsNotFound() {
         mTarget.saveTask(mTestTask);
 
-        when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null);
+        when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null);
 
         mTarget.getLaunchParams(mTestTask, null, mResult);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 33e6063..6259fa6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -104,6 +104,7 @@
             new DexmakerShareClassLoaderRule();
 
     @Mock private ActivityStackSupervisor mSupervisor;
+    @Mock private RootActivityContainer mRootActivityContainer;
     @Mock private IDevicePolicyManager mDevicePolicyManager;
     @Mock private IStatusBarService mStatusBarService;
     @Mock private WindowManagerService mWindowManager;
@@ -129,6 +130,7 @@
         }
 
         mSupervisor.mRecentTasks = mRecentTasks;
+        mSupervisor.mRootActivityContainer = mRootActivityContainer;
 
         mLockTaskController = new LockTaskController(mContext, mSupervisor,
                 new ImmediatelyExecuteHandler());
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 2f3f698..3c7b4b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -110,14 +110,13 @@
     @Before
     public void setUp() throws Exception {
         mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
-        mTestService = spy(new MyTestActivityTaskManagerService(mContext));
-        final TestActivityManagerService am = spy(new MyTestActivityManagerService());
-        setupActivityManagerService(am, mTestService);
+        mTestService = new MyTestActivityTaskManagerService(mContext);
         mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks();
         mRecentTasks.loadParametersFromResources(mContext.getResources());
-        mHomeStack = mTestService.mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+        mRunningTasks = (TestRunningTasks) mTestService.mStackSupervisor.mRunningTasks;
+        mHomeStack = mTestService.mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        mStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+        mStack = mTestService.mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         mCallbacksRecorder = new CallbacksRecorder();
         mRecentTasks.registerCallback(mCallbacksRecorder);
@@ -868,20 +867,20 @@
         }
 
         @Override
-        protected ActivityStackSupervisor createTestSupervisor() {
-            return new MyTestActivityStackSupervisor(this, mH.getLooper());
-        }
-
-    }
-
-    private class MyTestActivityManagerService extends TestActivityManagerService {
-        MyTestActivityManagerService() {
-            super(mTestInjector);
+        protected ActivityStackSupervisor createStackSupervisor() {
+            if (mTestStackSupervisor == null) {
+                mTestStackSupervisor = new MyTestActivityStackSupervisor(this, mH.getLooper());
+            }
+            return mTestStackSupervisor;
         }
 
         @Override
-        public boolean isUserRunning(int userId, int flags) {
-            return true;
+        void initRootActivityContainerMocks(WindowManagerService wm) {
+            super.initRootActivityContainerMocks(wm);
+            mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
+            mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
+            mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
+            mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
         }
     }
 
@@ -891,15 +890,6 @@
         }
 
         @Override
-        public void initialize() {
-            super.initialize();
-            mDisplay = getActivityDisplay(DEFAULT_DISPLAY);
-            mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
-            addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
-            addChild(mDisplay, ActivityDisplay.POSITION_TOP);
-        }
-
-        @Override
         RunningTasks createRunningTasks() {
             mRunningTasks = new TestRunningTasks();
             return mRunningTasks;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 50190e7..0ff67d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -25,6 +25,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
@@ -50,61 +51,50 @@
 public class RecentsAnimationTest extends ActivityTestsBase {
 
     private Context mContext = InstrumentationRegistry.getContext();
-    private TestActivityTaskManagerService mTestService;
     private ComponentName mRecentsComponent;
 
     @Before
     public void setUp() throws Exception {
         mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
-        mTestService = spy(new MyTestActivityTaskManagerService(mContext));
-        setupActivityManagerService(mTestService);
+        mService = new TestActivityTaskManagerService(mContext);
+
+        final RecentTasks recentTasks = mService.getRecentTasks();
+        spyOn(recentTasks);
+        mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
+        doReturn(mRecentsComponent).when(recentTasks).getRecentsComponent();
     }
 
     @Test
     public void testCancelAnimationOnStackOrderChange() {
         ActivityStack fullscreenStack =
-                mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+                mService.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        ActivityStack recentsStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+        ActivityStack recentsStack = mService.mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        ActivityRecord recentsActivity = new ActivityBuilder(mTestService)
+        ActivityRecord recentsActivity = new ActivityBuilder(mService)
                 .setComponent(mRecentsComponent)
                 .setCreateTask(true)
                 .setStack(recentsStack)
                 .build();
         ActivityStack fullscreenStack2 =
-                mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+                mService.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        ActivityRecord fsActivity = new ActivityBuilder(mTestService)
+        ActivityRecord fsActivity = new ActivityBuilder(mService)
                 .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
                 .setCreateTask(true)
                 .setStack(fullscreenStack2)
                 .build();
-        doReturn(true).when(mTestService.mWindowManager).canStartRecentsAnimation();
+        doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
 
         // Start the recents animation
         Intent recentsIntent = new Intent();
         recentsIntent.setComponent(mRecentsComponent);
-        mTestService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
+        mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
 
         fullscreenStack.moveToFront("Activity start");
 
         // Ensure that the recents animation was canceled
-        verify(mTestService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+        verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
                 eq(REORDER_KEEP_IN_PLACE), any());
     }
-
-    private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
-        MyTestActivityTaskManagerService(Context context) {
-            super(context);
-        }
-
-        @Override
-        protected RecentTasks createRecentTasks() {
-            RecentTasks recents = mock(RecentTasks.class);
-            doReturn(mRecentsComponent).when(recents).getRecentsComponent();
-            System.out.println(mRecentsComponent);
-            return recents;
-        }
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
new file mode 100644
index 0000000..631de99d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -0,0 +1,483 @@
+/*
+ * 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.app.ActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.app.ActivityOptions;
+import android.app.WaitResult;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.graphics.Rect;
+import android.os.Build;
+import android.platform.test.annotations.Presubmit;
+import androidx.test.filters.MediumTest;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for the {@link ActivityStackSupervisor} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityStackSupervisorTests
+ */
+@MediumTest
+@Presubmit
+public class RootActivityContainerTests extends ActivityTestsBase {
+    private ActivityStack mFullscreenStack;
+
+    @Before
+    public void setUp() throws Exception {
+        setupActivityTaskManagerService();
+        mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+    }
+
+    /**
+     * This test ensures that we do not try to restore a task based off an invalid task id. We
+     * should expect {@code null} to be returned in this case.
+     */
+    @Test
+    public void testRestoringInvalidTask() {
+        ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks();
+        TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/,
+                MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
+        assertNull(task);
+    }
+
+    /**
+     * This test ensures that an existing task in the pinned stack is moved to the fullscreen
+     * activity stack when a new task is added.
+     */
+    @Test
+    public void testReplacingTaskInPinnedStack() {
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+        final TaskRecord firstTask = firstActivity.getTask();
+
+        final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+        final TaskRecord secondTask = secondActivity.getTask();
+
+        mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
+
+        // Ensure full screen stack has both tasks.
+        ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
+
+        // Move first activity to pinned stack.
+        final Rect sourceBounds = new Rect();
+        mRootActivityContainer.moveActivityToPinnedStack(firstActivity, sourceBounds,
+                0f /*aspectRatio*/, "initialMove");
+
+        final ActivityDisplay display = mFullscreenStack.getDisplay();
+        ActivityStack pinnedStack = display.getPinnedStack();
+        // Ensure a task has moved over.
+        ensureStackPlacement(pinnedStack, firstTask);
+        ensureStackPlacement(mFullscreenStack, secondTask);
+
+        // Move second activity to pinned stack.
+        mRootActivityContainer.moveActivityToPinnedStack(secondActivity, sourceBounds,
+                0f /*aspectRatio*/, "secondMove");
+
+        // Need to get stacks again as a new instance might have been created.
+        pinnedStack = display.getPinnedStack();
+        mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        // Ensure stacks have swapped tasks.
+        ensureStackPlacement(pinnedStack, secondTask);
+        ensureStackPlacement(mFullscreenStack, firstTask);
+    }
+
+    private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
+        final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
+        assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
+
+        if (tasks == null) {
+            return;
+        }
+
+        for (TaskRecord task : tasks) {
+            assertTrue(stackTasks.contains(task));
+        }
+    }
+
+    @Test
+    public void testApplySleepTokens() {
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final KeyguardController keyguard = mSupervisor.getKeyguardController();
+        final ActivityStack stack = mock(ActivityStack.class);
+        display.addChild(stack, 0 /* position */);
+
+        // Make sure we wake and resume in the case the display is turning on and the keyguard is
+        // not showing.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                true /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // showing.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                true /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // not showing as unfocused.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, false /* isFocusedStack */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Should not do anything if the display state hasn't changed.
+        verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                false /* keyguardShowing */, false /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+    }
+
+    private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
+            ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
+            boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
+            boolean expectResumeTopActivity) {
+        reset(stack);
+
+        doReturn(displayShouldSleep).when(display).shouldSleep();
+        doReturn(displaySleeping).when(display).isSleeping();
+        doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
+
+        doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
+        doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
+        mRootActivityContainer.applySleepTokens(true);
+        verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+        verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
+                null /* target */, null /* targetOptions */);
+    }
+
+    /**
+     * Verifies that removal of activity with task and stack is done correctly.
+     */
+    @Test
+    public void testRemovingStackOnAppCrash() {
+        final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
+        final int originalStackCount = defaultDisplay.getChildCount();
+        final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(stack).build();
+
+        assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
+
+        // Let's pretend that the app has crashed.
+        firstActivity.app.setThread(null);
+        mRootActivityContainer.finishTopCrashedActivities(firstActivity.app, "test");
+
+        // Verify that the stack was removed.
+        assertEquals(originalStackCount, defaultDisplay.getChildCount());
+    }
+
+    @Test
+    public void testFocusability() {
+        final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(stack).build();
+
+        // Under split screen primary we should be focusable when not minimized
+        mRootActivityContainer.setDockedStackMinimized(false);
+        assertTrue(stack.isFocusable());
+        assertTrue(activity.isFocusable());
+
+        // Under split screen primary we should not be focusable when minimized
+        mRootActivityContainer.setDockedStackMinimized(true);
+        assertFalse(stack.isFocusable());
+        assertFalse(activity.isFocusable());
+
+        final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack(
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(pinnedStack).build();
+
+        // We should not be focusable when in pinned mode
+        assertFalse(pinnedStack.isFocusable());
+        assertFalse(pinnedActivity.isFocusable());
+
+        // Add flag forcing focusability.
+        pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
+
+        // We should not be focusable when in pinned mode
+        assertTrue(pinnedStack.isFocusable());
+        assertTrue(pinnedActivity.isFocusable());
+
+        // Without the overridding activity, stack should not be focusable.
+        pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability",
+                REMOVE_TASK_MODE_DESTROYING);
+        assertFalse(pinnedStack.isFocusable());
+    }
+
+    /**
+     * Verify that split-screen primary stack will be chosen if activity is launched that targets
+     * split-screen secondary, but a matching existing instance is found on top of split-screen
+     * primary stack.
+     */
+    @Test
+    public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
+        // Create primary split-screen stack with a task and an activity.
+        final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
+                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                        true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
+
+        // Find a launch stack for the top activity in split-screen primary, while requesting
+        // split-screen secondary.
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        final ActivityStack result =
+                mRootActivityContainer.getLaunchStack(r, options, task, true /* onTop */);
+
+        // Assert that the primary stack is returned.
+        assertEquals(primaryStack, result);
+    }
+
+    /**
+     * Verify split-screen primary stack & task can resized by
+     * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
+     */
+    @Test
+    public void testResizeDockedStackForSplitScreenPrimary() {
+        final Rect taskSize = new Rect(0, 0, 600, 600);
+        final Rect stackSize = new Rect(0, 0, 300, 300);
+
+        // Create primary split-screen stack with a task.
+        final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
+                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                        true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+
+        // Resize dock stack.
+        mService.resizeDockedStack(stackSize, taskSize, null, null, null);
+
+        // Verify dock stack & its task bounds if is equal as resized result.
+        assertEquals(primaryStack.getBounds(), stackSize);
+        assertEquals(task.getBounds(), taskSize);
+    }
+
+    /**
+     * Verify that home stack would be moved to front when the top activity is Recents.
+     */
+    @Test
+    public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
+        // Create stack/task on default display.
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final TestActivityStack targetStack =
+                new StackBuilder(mRootActivityContainer).setOnTop(false).build();
+        final TaskRecord targetTask = targetStack.getChildAt(0);
+
+        // Create Recents on top of the display.
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer).setActivityType(
+                ACTIVITY_TYPE_RECENTS).build();
+
+        final String reason = "findTaskToMoveToFront";
+        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+                false);
+
+        verify(display).moveHomeStackToFront(contains(reason));
+    }
+
+    /**
+     * Verify that home stack won't be moved to front if the top activity on other display is
+     * Recents.
+     */
+    @Test
+    public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
+        // Create stack/task on default display.
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+
+        // Create Recents on secondary display.
+        final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
+                ActivityDisplay.POSITION_TOP);
+        final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_RECENTS, true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        new ActivityBuilder(mService).setTask(task).build();
+
+        final String reason = "findTaskToMoveToFront";
+        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+                false);
+
+        verify(display, never()).moveHomeStackToFront(contains(reason));
+    }
+
+    /**
+     * Verify if a stack is not at the topmost position, it should be able to resume its activity if
+     * the stack is the top focused.
+     */
+    @Test
+    public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
+        // Create a stack at bottom.
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */));
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+        display.positionChildAtBottom(targetStack);
+
+        // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
+        // is the current top focused stack.
+        assertFalse(targetStack.isTopStackOnDisplay());
+        doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack();
+
+        // Use the stack as target to resume.
+        mRootActivityContainer.resumeFocusedStacksTopActivities(
+                targetStack, activity, null /* targetOptions */);
+
+        // Verify the target stack should resume its activity.
+        verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
+                eq(activity), eq(null /* targetOptions */));
+    }
+
+    /**
+     * Tests home activities that targeted sdk before Q cannot start on secondary display.
+     */
+    @Test
+    public void testStartHomeTargetSdkBeforeQ() throws Exception {
+        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+        mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
+        doReturn(true).when(secondDisplay).supportsSystemDecorations();
+
+        final ActivityInfo info = new ActivityInfo();
+        info.launchMode = LAUNCH_MULTIPLE;
+        info.applicationInfo = new ApplicationInfo();
+        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+        assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
+                false /* allowInstrumenting */));
+
+        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
+        assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
+                false /* allowInstrumenting */));
+    }
+
+    /**
+     * Tests that home activities can be started on the displays that supports system decorations.
+     */
+    @Test
+    public void testStartHomeOnAllDisplays() {
+        // Create secondary displays.
+        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+        mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
+        doReturn(true).when(secondDisplay).supportsSystemDecorations();
+
+        // Create mock tasks and other necessary mocks.
+        TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
+        final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
+        TaskRecord.setTaskRecordFactory(factory);
+        doAnswer(i -> taskBuilder.build()).when(factory)
+                .create(any(), anyInt(), any(), any(), any(), any());
+        doReturn(true).when(mRootActivityContainer)
+                .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
+        doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+                any(), anyInt(), anyBoolean());
+
+        mRootActivityContainer.startHomeOnAllDisplays(0, "testStartHome");
+
+        assertTrue(mRootActivityContainer.getDefaultDisplay().getTopStack().isActivityTypeHome());
+        assertNotNull(secondDisplay.getTopStack());
+        assertTrue(secondDisplay.getTopStack().isActivityTypeHome());
+    }
+
+    /**
+     * Tests that home activities won't be started before booting when display added.
+     */
+    @Test
+    public void testNotStartHomeBeforeBoot() {
+        final int displayId = 1;
+        final boolean isBooting = mService.mAmInternal.isBooting();
+        final boolean isBooted = mService.mAmInternal.isBooted();
+        try {
+            mService.mAmInternal.setBooting(false);
+            mService.mAmInternal.setBooted(false);
+            mRootActivityContainer.onDisplayAdded(displayId);
+            verify(mRootActivityContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
+        } finally {
+            mService.mAmInternal.setBooting(isBooting);
+            mService.mAmInternal.setBooted(isBooted);
+        }
+    }
+
+    /**
+     * Tests whether home can be started if being instrumented.
+     */
+    @Test
+    public void testCanStartHomeWhenInstrumented() {
+        final ActivityInfo info = new ActivityInfo();
+        info.applicationInfo = new ApplicationInfo();
+        final WindowProcessController app = mock(WindowProcessController.class);
+        doReturn(app).when(mService).getProcessController(any(), anyInt());
+
+        // Can not start home if we don't want to start home while home is being instrumented.
+        doReturn(true).when(app).isInstrumenting();
+        assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                false /* allowInstrumenting*/));
+
+        // Can start home for other cases.
+        assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                true /* allowInstrumenting*/));
+
+        doReturn(false).when(app).isInstrumenting();
+        assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                false /* allowInstrumenting*/));
+        assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                true /* allowInstrumenting*/));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 0e1624e..a8b6dc3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -63,7 +63,7 @@
         final int numStacks = 2;
         for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
             final ActivityStack stack =
-                    new StackBuilder(mSupervisor).setCreateActivity(false).build();
+                    new StackBuilder(mRootActivityContainer).setCreateActivity(false).build();
             display.addChild(stack, POSITION_BOTTOM);
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 6638eeb..bd8cd1f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -888,10 +888,10 @@
 
     @Test
     public void testAdjustBoundsToFitNewDisplay_LargerThanDisplay_RTL() {
-        final Configuration overrideConfig = mSupervisor.getOverrideConfiguration();
+        final Configuration overrideConfig = mRootActivityContainer.getOverrideConfiguration();
         // Egyptian Arabic is a RTL language.
         overrideConfig.setLayoutDirection(new Locale("ar", "EG"));
-        mSupervisor.onOverrideConfigurationChanged(overrideConfig);
+        mRootActivityContainer.onOverrideConfigurationChanged(overrideConfig);
 
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 152831f..4f573a4 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -27,8 +27,6 @@
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
@@ -846,8 +844,6 @@
             // Inform listeners if necessary
             if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
                     || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
-                    || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START
-                    || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_STOP
                     || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
                     || event.mEventType == UsageEvents.Event.USER_INTERACTION
                     || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
@@ -900,10 +896,6 @@
         switch (eventType) {
             case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
             case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
-            case UsageEvents.Event.FOREGROUND_SERVICE_START:
-                return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
-            case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
-                return REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
             case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
             case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
             case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index d940620..01e566c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -20,6 +20,7 @@
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
 import android.util.ArrayMap;
+import android.util.Log;
 
 import com.android.internal.util.XmlUtils;
 
@@ -89,11 +90,23 @@
         // Apply the offset to the beginTime to find the absolute time.
         stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
                 parser, LAST_TIME_ACTIVE_ATTR);
-        stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
-                parser, LAST_TIME_SERVICE_USED_ATTR);
+
+        try {
+            stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
+                    parser, LAST_TIME_SERVICE_USED_ATTR);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to parse mLastTimeForegroundServiceUsed", e);
+        }
+
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
-        stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
+
+        try {
+            stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
                 TOTAL_TIME_SERVICE_USED_ATTR);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
+        }
+
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
         stats.mAppLaunchCount = XmlUtils.readIntAttribute(parser, APP_LAUNCH_COUNT_ATTR,
                 0);
@@ -350,8 +363,17 @@
         }
 
         statsOut.endTime = statsOut.beginTime + XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
-        statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR);
-        statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR);
+        try {
+            statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to parse majorVersion", e);
+        }
+
+        try {
+            statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to parse minorVersion", e);
+        }
 
         int eventCode;
         int outerDepth = parser.getDepth();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 5ad7c30..185c886 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1838,6 +1838,13 @@
             "notify_international_call_on_wfc_bool";
 
     /**
+     * Flag to hide Preset APN details. If true, user cannot enter ApnEditor view of Preset APN,
+     * and cannot view details of the APN. If false, user can enter ApnEditor view of Preset APN.
+     * Default value is false.
+     */
+    public static final String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
+
+    /**
      * Flag specifying whether to show an alert dialog for video call charges.
      * By default this value is {@code false}.
      * @hide
@@ -2643,6 +2650,7 @@
         sDefaults.putBoolean(KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL,
                 false);
         sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
+        sDefaults.putBoolean(KEY_HIDE_PRESET_APN_DETAILS_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false);
         sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
                 null);
diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java
index e373797..269cda1 100644
--- a/telephony/java/android/telephony/MbmsGroupCallSession.java
+++ b/telephony/java/android/telephony/MbmsGroupCallSession.java
@@ -37,6 +37,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -107,14 +108,14 @@
      * {@link MbmsGroupCallSession} that you received before calling this method again.
      *
      * @param context The {@link Context} to use.
-     * @param executor The executor on which you wish to execute callbacks.
      * @param subscriptionId The subscription ID to use.
+     * @param executor The executor on which you wish to execute callbacks.
      * @param callback A callback object on which you wish to receive results of asynchronous
      *                 operations.
      * @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred.
      */
     public static @Nullable MbmsGroupCallSession create(@NonNull Context context,
-            @NonNull Executor executor, int subscriptionId,
+            int subscriptionId, @NonNull Executor executor,
             final @NonNull MbmsGroupCallSessionCallback callback) {
         if (!sIsInitialized.compareAndSet(false, true)) {
             throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession");
@@ -138,11 +139,11 @@
 
     /**
      * Create a new {@link MbmsGroupCallSession} using the system default data subscription ID.
-     * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+     * See {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
      */
     public static MbmsGroupCallSession create(@NonNull Context context,
             @NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) {
-        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
+        return create(context, SubscriptionManager.getDefaultSubscriptionId(), executor, callback);
     }
 
     /**
@@ -153,7 +154,7 @@
      * instance of {@link MbmsGroupCallSessionCallback}, but callbacks that have already been
      * enqueued will still be delivered.
      *
-     * It is safe to call {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)} to
+     * It is safe to call {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)} to
      * obtain another instance of {@link MbmsGroupCallSession} immediately after this method
      * returns.
      *
@@ -189,18 +190,19 @@
      * Asynchronous errors through the callback include any of the errors in
      * {@link MbmsErrors.GeneralErrors}.
      *
-     * @param executor The executor on which you wish to execute callbacks for this stream.
      * @param tmgi The TMGI, an identifier for the group call you want to join.
-     * @param saiArray An array of SAIs for the group call that should be negotiated separately with
+     * @param saiList A list of SAIs for the group call that should be negotiated separately with
      *                the carrier.
-     * @param frequencyArray An array of frequencies for the group call that should be negotiated
+     * @param frequencyList A lost of frequencies for the group call that should be negotiated
      *                separately with the carrier.
+     * @param executor The executor on which you wish to execute callbacks for this stream.
      * @param callback The callback that you want to receive information about the call on.
      * @return An instance of {@link GroupCall} through which the call can be controlled.
      *         May be {@code null} if an error occurred.
      */
-    public @Nullable GroupCall startGroupCall(@NonNull Executor executor, long tmgi, int[] saiArray,
-            int[] frequencyArray, @NonNull GroupCallCallback callback) {
+    public @Nullable GroupCall startGroupCall(long tmgi, @NonNull List<Integer> saiList,
+            @NonNull List<Integer> frequencyList, @NonNull Executor executor,
+            @NonNull GroupCallCallback callback) {
         IMbmsGroupCallService groupCallService = mService.get();
         if (groupCallService == null) {
             throw new IllegalStateException("Middleware not yet bound");
@@ -215,7 +217,7 @@
 
         try {
             int returnCode = groupCallService.startGroupCall(
-                    mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback);
+                    mSubscriptionId, tmgi, saiList, frequencyList, serviceCallback);
             if (returnCode == MbmsErrors.UNKNOWN) {
                 // Unbind and throw an obvious error
                 close();
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index d2001ae..22ddb4a 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -16,11 +16,15 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.annotation.IntDef;
+import android.telephony.TelephonyManager.NetworkType;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * @hide
@@ -50,6 +54,7 @@
      *
      * <p>One of {@link #CONNECTION_PRIMARY_SERVING}, {@link #CONNECTION_SECONDARY_SERVING}.
      */
+    @ConnectionStatus
     private int mCellConnectionStatus;
 
     /**
@@ -57,15 +62,33 @@
      */
     private int mCellBandwidthDownlinkKhz;
 
-    public PhysicalChannelConfig(int status, int bandwidth) {
-        mCellConnectionStatus = status;
-        mCellBandwidthDownlinkKhz = bandwidth;
-    }
+    /**
+     * The radio technology for this physical channel.
+     */
+    @NetworkType
+    private int mRat;
 
-    public PhysicalChannelConfig(Parcel in) {
-        mCellConnectionStatus = in.readInt();
-        mCellBandwidthDownlinkKhz = in.readInt();
-    }
+    /**
+     * The rough frequency range for this physical channel.
+     */
+    @ServiceState.FrequencyRange
+    private int mFrequencyRange;
+
+    /**
+     * The absolute radio frequency channel number, {@link Integer#MAX_VALUE} if unknown.
+     */
+    private int mChannelNumber;
+
+    /**
+     * A list of data calls mapped to this physical channel. An empty list means the physical
+     * channel has no data call mapped to it.
+     */
+    private int[] mContextIds;
+
+    /**
+     * The physical cell identifier for this cell - PCI, PSC, {@link Integer#MAX_VALUE} if known.
+     */
+    private int mPhysicalCellId;
 
     @Override
     public int describeContents() {
@@ -76,6 +99,11 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mCellConnectionStatus);
         dest.writeInt(mCellBandwidthDownlinkKhz);
+        dest.writeInt(mRat);
+        dest.writeInt(mChannelNumber);
+        dest.writeInt(mFrequencyRange);
+        dest.writeIntArray(mContextIds);
+        dest.writeInt(mPhysicalCellId);
     }
 
     /**
@@ -86,6 +114,60 @@
     }
 
     /**
+     * Get the list of data call ids mapped to this physical channel. This list is sorted into
+     * ascending numerical order. Each id in this list must match the id in
+     * {@link com.android.internal.telephony.dataconnection.DataConnection}. An empty list means the
+     * physical channel has no data call mapped to it.
+     *
+     * @return an integer list indicates the data call ids.
+     */
+    public int[] getContextIds() {
+        return mContextIds;
+    }
+
+    /**
+     * @return the rough frequency range for this physical channel.
+     * @see {@link ServiceState#FREQUENCY_RANGE_LOW}
+     * @see {@link ServiceState#FREQUENCY_RANGE_MID}
+     * @see {@link ServiceState#FREQUENCY_RANGE_HIGH}
+     * @see {@link ServiceState#FREQUENCY_RANGE_MMWAVE}
+     */
+    @ServiceState.FrequencyRange
+    public int getFrequencyRange() {
+        return mFrequencyRange;
+    }
+
+    /**
+     * @return the absolute radio frequency channel number for this physical channel,
+     * {@link Integer#MAX_VALUE} if unknown.
+     */
+    public int getChannelNumber() {
+        return mChannelNumber;
+    }
+
+    /**
+     * In UTRAN, this value is primary scrambling code. The range is [0, 511].
+     * Reference: 3GPP TS 25.213 section 5.2.2.
+     *
+     * In EUTRAN, this value is physical layer cell identity. The range is [0, 503].
+     * Reference: 3GPP TS 36.211 section 6.11.
+     *
+     * In 5G RAN, this value is physical layer cell identity. The range is [0, 1008].
+     * Reference: 3GPP TS 38.211 section 7.4.2.1.
+     *
+     * @return the physical cell identifier for this cell, {@link Integer#MAX_VALUE} if unknown.
+     */
+    public int getPhysicalCellId() {
+        return mPhysicalCellId;
+    }
+
+    /**The radio technology for this physical channel. */
+    @NetworkType
+    public int getRat() {
+        return mRat;
+    }
+
+    /**
      * Gets the connection status of the cell.
      *
      * @see #CONNECTION_PRIMARY_SERVING
@@ -125,12 +207,19 @@
 
         PhysicalChannelConfig config = (PhysicalChannelConfig) o;
         return mCellConnectionStatus == config.mCellConnectionStatus
-                && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz;
+                && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz
+                && mRat == config.mRat
+                && mFrequencyRange == config.mFrequencyRange
+                && mChannelNumber == config.mChannelNumber
+                && mPhysicalCellId == config.mPhysicalCellId
+                && Arrays.equals(mContextIds, config.mContextIds);
     }
 
     @Override
     public int hashCode() {
-        return (mCellBandwidthDownlinkKhz * 29) + (mCellConnectionStatus * 31);
+        return Objects.hash(
+                mCellConnectionStatus, mCellBandwidthDownlinkKhz, mRat, mFrequencyRange,
+                mChannelNumber, mPhysicalCellId, mContextIds);
     }
 
     public static final Parcelable.Creator<PhysicalChannelConfig> CREATOR =
@@ -147,11 +236,111 @@
     @Override
     public String toString() {
         return new StringBuilder()
-            .append("{mConnectionStatus=")
-            .append(getConnectionStatusString())
-            .append(",mCellBandwidthDownlinkKhz=")
-            .append(mCellBandwidthDownlinkKhz)
-            .append("}")
-            .toString();
+                .append("{mConnectionStatus=")
+                .append(getConnectionStatusString())
+                .append(",mCellBandwidthDownlinkKhz=")
+                .append(mCellBandwidthDownlinkKhz)
+                .append(",mRat=")
+                .append(mRat)
+                .append(",mFrequencyRange=")
+                .append(mFrequencyRange)
+                .append(",mChannelNumber=")
+                .append(mChannelNumber)
+                .append(",mContextIds=")
+                .append(mContextIds.toString())
+                .append(",mPhysicalCellId=")
+                .append(mPhysicalCellId)
+                .append("}")
+                .toString();
+    }
+
+    private PhysicalChannelConfig(Parcel in) {
+        mCellConnectionStatus = in.readInt();
+        mCellBandwidthDownlinkKhz = in.readInt();
+        mRat = in.readInt();
+        mChannelNumber = in.readInt();
+        mFrequencyRange = in.readInt();
+        mContextIds = in.createIntArray();
+        mPhysicalCellId = in.readInt();
+    }
+
+    private PhysicalChannelConfig(Builder builder) {
+        mCellConnectionStatus = builder.mCellConnectionStatus;
+        mCellBandwidthDownlinkKhz = builder.mCellBandwidthDownlinkKhz;
+        mRat = builder.mRat;
+        mChannelNumber = builder.mChannelNumber;
+        mFrequencyRange = builder.mFrequencyRange;
+        mContextIds = builder.mContextIds;
+        mPhysicalCellId = builder.mPhysicalCellId;
+    }
+
+    /** The builder of {@code PhysicalChannelConfig}. */
+    public static final class Builder {
+        private int mRat;
+        private int mFrequencyRange;
+        private int mChannelNumber;
+        private int mCellBandwidthDownlinkKhz;
+        private int mCellConnectionStatus;
+        private int[] mContextIds;
+        private int mPhysicalCellId;
+
+        /** @hide */
+        public Builder() {
+            mRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+            mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
+            mChannelNumber = Integer.MAX_VALUE;
+            mCellBandwidthDownlinkKhz = 0;
+            mCellConnectionStatus = CONNECTION_UNKNOWN;
+            mContextIds = new int[0];
+            mPhysicalCellId = Integer.MAX_VALUE;
+        }
+
+        /** @hide */
+        public PhysicalChannelConfig build() {
+            return new PhysicalChannelConfig(this);
+        }
+
+        /** @hide */
+        public Builder setRat(int rat) {
+            this.mRat = rat;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setFrequencyRange(int frequencyRange) {
+            this.mFrequencyRange = frequencyRange;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setChannelNumber(int channelNumber) {
+            this.mChannelNumber = channelNumber;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) {
+            this.mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setCellConnectionStatus(int connectionStatus) {
+            this.mCellConnectionStatus = connectionStatus;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setContextIds(int[] contextIds) {
+            if (contextIds != null) Arrays.sort(contextIds);
+            this.mContextIds = contextIds;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setPhysicalCellId(int physicalCellId) {
+            this.mPhysicalCellId = physicalCellId;
+            return this;
+        }
     }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 3200aea..34a8c96 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -147,7 +147,8 @@
     public static final Uri WFC_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc");
 
     /**
-     * A content {@link Uri} used to receive updates on advanced calling user setting.
+     * A content {@link Uri} used to receive updates on advanced calling user setting
+     * @see ImsMmTelManager#isAdvancedCallingSettingEnabled().
      * <p>
      * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
      * subscription advanced calling enabled
@@ -1426,7 +1427,7 @@
      * subscriptions in the slot.
      */
     @Nullable
-    public static int[] getSubscriptionIds(int slotIndex) {
+    public int[] getSubscriptionIds(int slotIndex) {
         return getSubId(slotIndex);
     }
 
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index bdba8c8..2d46ec2 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -17,8 +17,8 @@
 package android.telephony.emergency;
 
 import android.annotation.IntDef;
-import android.hardware.radio.V1_3.EmergencyNumberSource;
-import android.hardware.radio.V1_3.EmergencyServiceCategory;
+import android.hardware.radio.V1_4.EmergencyNumberSource;
+import android.hardware.radio.V1_4.EmergencyServiceCategory;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -138,7 +138,7 @@
     }
 
     /**
-     * The source to tell where the corresponding @1.3::EmergencyNumber comes from.
+     * The source to tell where the corresponding @1.4::EmergencyNumber comes from.
      *
      * The emergency number has one or more defined emergency number sources.
      *
diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java
index 9aca18e..25e274e 100644
--- a/telephony/java/android/telephony/mbms/GroupCall.java
+++ b/telephony/java/android/telephony/mbms/GroupCall.java
@@ -17,6 +17,7 @@
 package android.telephony.mbms;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.os.RemoteException;
 import android.telephony.MbmsGroupCallSession;
 import android.telephony.mbms.vendor.IMbmsGroupCallService;
@@ -24,6 +25,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 
 /**
  * Class used to represent a single MBMS group call. After a call has been started with
@@ -41,8 +43,26 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
     public @interface GroupCallState {}
+
+    /**
+     * Indicates that the group call is in a stopped state
+     *
+     * This can be reported after network action or after calling {@link #close}.
+     */
     public static final int STATE_STOPPED = 1;
+
+    /**
+     * Indicates that the group call is started.
+     *
+     * Data can be transmitted and received in this state.
+     */
     public static final int STATE_STARTED = 2;
+
+    /**
+     * Indicates that the group call is stalled.
+     *
+     * This may be due to a network issue or the device being temporarily out of range.
+     */
     public static final int STATE_STALLED = 3;
 
     /**
@@ -122,16 +142,17 @@
      * Send an update to the middleware when the SAI (Service Area Identifier) list and frequency
      * information of the group call has * changed. Callers must obtain this information from the
      * wireless carrier independently.
-     * @param saiArray New array of SAIs that the call is available on.
-     * @param frequencyArray New array of frequencies that the call is available on.
+     * @param saiList New list of SAIs that the call is available on.
+     * @param frequencyList New list of frequencies that the call is available on.
      */
-    public void updateGroupCall(int[] saiArray, int[] frequencyArray) {
+    public void updateGroupCall(@NonNull List<Integer> saiList,
+            @NonNull List<Integer> frequencyList) {
         if (mService == null) {
             throw new IllegalStateException("No group call service attached");
         }
 
         try {
-            mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray);
+            mService.updateGroupCall(mSubscriptionId, mTmgi, saiList, frequencyList);
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
index 001bb02..77e36bb 100644
--- a/telephony/java/android/telephony/mbms/GroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.mbms;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.Nullable;
 
 import java.lang.annotation.Retention;
@@ -26,7 +27,7 @@
  * A callback class for use when the application is in a group call. The middleware
  * will provide updates on the status of the call via this callback.
  */
-public class GroupCallCallback {
+public interface GroupCallCallback {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {
@@ -40,7 +41,7 @@
             MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
             MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
             MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
-    private @interface GroupCallError{}
+    @interface GroupCallError{}
 
     /**
      * Indicates broadcast signal strength is not available for this call.
@@ -48,7 +49,7 @@
      * This may be due to the call no longer being available due to geography
      * or timing (end of service)
      */
-    public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+    int SIGNAL_STRENGTH_UNAVAILABLE = -1;
 
     /**
      * Called by the middleware when it has detected an error condition in this group call. The
@@ -56,9 +57,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(@GroupCallError int errorCode, @Nullable String message) {
-        // default implementation empty
-    }
+    void onError(@GroupCallError int errorCode, @Nullable String message);
 
     /**
      * Called to indicate this call has changed state.
@@ -66,10 +65,8 @@
      * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
      * and {@link GroupCall#STATE_STALLED}.
      */
-    public void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
-            @GroupCall.GroupCallStateChangeReason int reason) {
-        // default implementation empty
-    }
+    void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+            @GroupCall.GroupCallStateChangeReason int reason);
 
     /**
      * Broadcast Signal Strength updated.
@@ -81,7 +78,5 @@
      * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
      * for this call due to timing, geography or popularity.
      */
-    public void onBroadcastSignalStrengthUpdated(int signalStrength) {
-        // default implementation empty
-    }
+    void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength);
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index 7c4321b..52e4d33 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -140,5 +140,21 @@
         public static final int ERROR_UNKNOWN_FILE_INFO = 403;
     }
 
+    /**
+     * Indicates the errors that are applicable only to the group call use-case.
+     */
+    public static class GroupCallErrors {
+        private GroupCallErrors() { }
+        /** Indicates that the middleware was unable to start the group call. */
+        public static final int ERROR_UNABLE_TO_START_SERVICE = 501;
+
+        /**
+         * Indicates that the app called
+         * {@link android.telephony.MbmsGroupCallSession#startGroupCall} more than once for the
+         * same {@code tmgi}.
+         */
+        public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502;
+    }
+
     private MbmsErrors() {}
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
index 7da734e..04e7ba1 100644
--- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.mbms;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.telephony.MbmsGroupCallSession;
@@ -29,9 +30,9 @@
 /**
  * A callback class that is used to receive information from the middleware on MBMS group-call
  * services. An instance of this object should be passed into
- * {@link MbmsGroupCallSession#create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ * {@link MbmsGroupCallSession#create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
  */
-public class MbmsGroupCallSessionCallback {
+public interface MbmsGroupCallSessionCallback {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {
@@ -48,7 +49,7 @@
             MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
             MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
             MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
-    private @interface GroupCallError{}
+    @interface GroupCallError{}
 
     /**
      * Called by the middleware when it has detected an error condition. The possible error codes
@@ -56,8 +57,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(@GroupCallError int errorCode, @Nullable String message) {
-    }
+    void onError(@GroupCallError int errorCode, @Nullable String message);
 
     /**
      * Indicates that the list of currently available SAIs has been updated. The app may use this
@@ -70,21 +70,22 @@
      * @param availableSais A list of lists of available SAIS in neighboring cells, where each list
      *                      contains the available SAIs in an individual cell.
      */
-    public void onAvailableSaisUpdated(List<Integer> currentSais,
-            List<List<Integer>> availableSais) {
-    }
+    void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
+            @NonNull List<List<Integer>> availableSais);
 
     /**
      * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
-     * via this callback may be used to establish a data-link interface with the modem before the
-     * middleware is ready.
-     * Note that this method may be called before {@link #onMiddlewareReady()}.
+     * via this callback may be used to establish a data-link interface with the modem.
+     *
+     * In order to establish the data-link interface, the multicast IP and port must be obtained
+     * out-of-band from the carrier. A {@link java.net.MulticastSocket} may then be constructed
+     * using a {@link java.net.NetworkInterface} with the name and interface supplied by this
+     * callback.
      *
      * @param interfaceName The interface name for the data link.
      * @param index The index for the data link.
      */
-    public void onServiceInterfaceAvailable(String interfaceName, int index) {
-    }
+    void onServiceInterfaceAvailable(@NonNull String interfaceName, int index);
 
     /**
      * Called to indicate that the middleware has been initialized and is ready.
@@ -94,6 +95,5 @@
      * delivered via {@link #onError(int, String)} with error code
      * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
      */
-    public void onMiddlewareReady() {
-    }
+    void onMiddlewareReady();
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
index 721256a..44cc24a 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
@@ -29,11 +29,11 @@
 
     void stopGroupCall(int subId, long tmgi);
 
-    void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
-        in int[] frequencyArray);
+    void updateGroupCall(int subscriptionId, long tmgi, in List saiList,
+        in List frequencyList);
 
-    int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
-        in int[] frequencyArray, IGroupCallCallback callback);
+    int startGroupCall(int subscriptionId, long tmgi, in List saiList,
+        in List frequencyList, IGroupCallCallback callback);
 
     void dispose(int subId);
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
index 3734ca7..e86a47d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
@@ -115,15 +115,16 @@
         }
 
         @Override
-        public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
-                int[] frequencyArray) {
+        public void updateGroupCall(int subscriptionId, long tmgi, List saiList,
+                List frequencyList) {
             MbmsGroupCallServiceBase.this.updateGroupCall(
-                    subscriptionId, tmgi, saiArray, frequencyArray);
+                    subscriptionId, tmgi, saiList, frequencyList);
         }
 
         @Override
-        public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray,
-                final int[] frequencyArray, final IGroupCallCallback callback)
+        public int startGroupCall(final int subscriptionId, final long tmgi,
+                final List saiList,
+                final List frequencyList, final IGroupCallCallback callback)
                 throws RemoteException {
             if (callback == null) {
                 throw new NullPointerException("Callback must not be null");
@@ -132,7 +133,7 @@
             final int uid = Binder.getCallingUid();
 
             int result = MbmsGroupCallServiceBase.this.startGroupCall(
-                    subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() {
+                    subscriptionId, tmgi, saiList, frequencyList, new GroupCallCallback() {
                         @Override
                         public void onError(final int errorCode, final String message) {
                             try {
@@ -209,13 +210,13 @@
      *
      * @param subscriptionId The subscription id to use.
      * @param tmgi The TMGI, an identifier for the group call.
-     * @param saiArray An array of SAIs for the group call.
-     * @param frequencyArray An array of frequencies for the group call.
+     * @param saiList A list of SAIs for the group call.
+     * @param frequencyList A list of frequencies for the group call.
      * @param callback The callback object on which the app wishes to receive updates.
      * @return Any error in {@link MbmsErrors.GeneralErrors}
      */
-    public int startGroupCall(int subscriptionId, long tmgi, int[] saiArray, int[] frequencyArray,
-            GroupCallCallback callback) {
+    public int startGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+            List<Integer> frequencyList, GroupCallCallback callback) {
         throw new UnsupportedOperationException("Not implemented");
     }
 
@@ -237,11 +238,11 @@
     /**
      * Called when the app receives new SAI and frequency information for the group call identified
      * by {@code tmgi}.
-     * @param saiArray New array of SAIs that the call is available on.
-     * @param frequencyArray New array of frequencies that the call is available on.
+     * @param saiList New list of SAIs that the call is available on.
+     * @param frequencyList New list of frequencies that the call is available on.
      */
-    public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
-            int[] frequencyArray) {
+    public void updateGroupCall(int subscriptionId, long tmgi, List<Integer> saiList,
+            List<Integer> frequencyList) {
         throw new UnsupportedOperationException("Not implemented");
     }
 
diff --git a/tests/TouchLatency/.gitignore b/tests/TouchLatency/.gitignore
index bd79078..7f4121a 100644
--- a/tests/TouchLatency/.gitignore
+++ b/tests/TouchLatency/.gitignore
@@ -3,4 +3,5 @@
 /.idea
 .DS_Store
 /build
+/gen
 .iml
diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle
index 2337110..2594322 100644
--- a/tests/TouchLatency/app/build.gradle
+++ b/tests/TouchLatency/app/build.gradle
@@ -1,13 +1,13 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 21
-    buildToolsVersion "21.1.2"
+    compileSdkVersion 28
+    buildToolsVersion '28.0.3'
 
     defaultConfig {
         applicationId "com.prefabulated.touchlatency"
         minSdkVersion 21
-        targetSdkVersion 21
+        targetSdkVersion 28
         versionCode 1
         versionName "1.0"
     }
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index b4b5ca7..360c22f 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -19,11 +19,9 @@
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Paint;
-import android.os.CountDownTimer;
+import android.graphics.Paint.Align;
 import android.os.Bundle;
-import android.text.method.Touch;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Menu;
@@ -31,15 +29,17 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.os.Trace;
-
-import java.util.ArrayList;
-import java.util.Collections;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
 
 class TouchLatencyView extends View implements View.OnTouchListener {
     private static final String LOG_TAG = "TouchLatency";
     private static final int BACKGROUND_COLOR = 0xFF400080;
     private static final int INNER_RADIUS = 70;
-    private static final int BALL_RADIUS = 100;
+    private static final int BALL_DIAMETER = 200;
+    private static final int SEC_TO_NANOS = 1000000000;
+    private static final float FPS_UPDATE_THRESHOLD = 20;
+    private static final long BALL_VELOCITY = 420;
 
     public TouchLatencyView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -58,13 +58,17 @@
         mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mRedPaint.setColor(0xFFFF0000);
         mRedPaint.setStyle(Paint.Style.FILL);
+        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mTextPaint.setColor(0xFFFFFFFF);
+        mTextPaint.setTextSize(100);
+        mTextPaint.setTextAlign(Align.RIGHT);
 
         mTouching = false;
 
-        mBallX = 100.0f;
-        mBallY = 100.0f;
-        mVelocityX = 7.0f;
-        mVelocityY = 7.0f;
+        mLastDrawNano = 0;
+        mFps = 0;
+        mLastFpsUpdate = 0;
+        mFrameCount = 0;
         Trace.endSection();
     }
 
@@ -113,43 +117,70 @@
         }
     }
 
+    private Paint getBallColor() {
+        if (mFps > 75)
+            return mGreenPaint;
+        else if (mFps > 45)
+            return mYellowPaint;
+        else
+            return mRedPaint;
+    }
+
     private void drawBall(Canvas canvas) {
         Trace.beginSection("TouchLatencyView drawBall");
         int width = canvas.getWidth();
         int height = canvas.getHeight();
+        float fps = 0f;
 
-        // Update position
-        mBallX += mVelocityX;
-        mBallY += mVelocityY;
+        long t = System.nanoTime();
+        long tDiff = t - mLastDrawNano;
+        mLastDrawNano = t;
+        mFrameCount++;
 
-        // Clamp and change velocity if necessary
-        float left = mBallX - BALL_RADIUS;
-        if (left < 0) {
-            left = 0;
-            mVelocityX *= -1;
+        if (tDiff < SEC_TO_NANOS) {
+            fps = 1f * SEC_TO_NANOS / tDiff;
         }
 
-        float top = mBallY - BALL_RADIUS;
-        if (top < 0) {
-            top = 0;
-            mVelocityY *= -1;
+        long fDiff = t - mLastFpsUpdate;
+        if (Math.abs(mFps - fps) > FPS_UPDATE_THRESHOLD) {
+            mFps = fps;
+            mLastFpsUpdate = t;
+            mFrameCount = 0;
+        } else if (fDiff > SEC_TO_NANOS) {
+            mFps = 1f * mFrameCount * SEC_TO_NANOS / fDiff;
+            mLastFpsUpdate = t;
+            mFrameCount = 0;
         }
 
-        float right = mBallX + BALL_RADIUS;
-        if (right > width) {
-            right = width;
-            mVelocityX *= -1;
-        }
+        final long pos = t * BALL_VELOCITY / SEC_TO_NANOS;
+        final long xMax = width - BALL_DIAMETER;
+        final long yMax = height - BALL_DIAMETER;
+        long xOffset = pos % xMax;
+        long yOffset = pos % yMax;
 
-        float bottom = mBallY + BALL_RADIUS;
-        if (bottom > height) {
-            bottom = height;
-            mVelocityY *= -1;
+        float left, right, top, bottom;
+
+        if (((pos / xMax) & 1) == 0) {
+            left = xMax - xOffset;
+        } else {
+            left = xOffset;
         }
+        right = left + BALL_DIAMETER;
+
+        if (((pos / yMax) & 1) == 0) {
+            top = yMax - yOffset;
+        } else {
+            top = yOffset;
+        }
+        bottom = top + BALL_DIAMETER;
 
         // Draw the ball
         canvas.drawColor(BACKGROUND_COLOR);
-        canvas.drawOval(left, top, right, bottom, mYellowPaint);
+        canvas.drawOval(left, top, right, bottom, getBallColor());
+        DecimalFormat df = new DecimalFormat("fps: #.##");
+        df.setRoundingMode(RoundingMode.HALF_UP);
+        canvas.drawText(df.format(mFps), width, 100, mTextPaint);
+
         invalidate();
         Trace.endSection();
     }
@@ -176,15 +207,15 @@
         Trace.endSection();
     }
 
-    private Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint;
+    private final Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint, mTextPaint;
     private int mMode;
 
     private boolean mTouching;
     private float mTouchX, mTouchY;
     private float mLastDrawnX, mLastDrawnY;
 
-    private float mBallX, mBallY;
-    private float mVelocityX, mVelocityY;
+    private long mLastDrawNano, mLastFpsUpdate, mFrameCount;
+    private float mFps;
 }
 
 public class TouchLatencyActivity extends Activity {
diff --git a/tests/TouchLatency/build.gradle b/tests/TouchLatency/build.gradle
index d3ff69d..03abe82 100644
--- a/tests/TouchLatency/build.gradle
+++ b/tests/TouchLatency/build.gradle
@@ -3,9 +3,10 @@
 buildscript {
     repositories {
         jcenter()
+        google()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:1.1.0'
+        classpath 'com.android.tools.build:gradle:3.2.1'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
@@ -15,5 +16,6 @@
 allprojects {
     repositories {
         jcenter()
+        google()
     }
 }
diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..111992a 100644
--- a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
+++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Tue Nov 27 13:37:59 PST 2018
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index ae3914e..d5987a5 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -26,6 +26,7 @@
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.IWindowSession;
+import android.view.InsetsState;
 import android.view.Surface;
 import android.view.View;
 import android.view.WindowManager;
@@ -105,7 +106,7 @@
                                 window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect,
                                 mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
                                 new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
-                                new Surface());
+                                new Surface(), new InsetsState());
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }
@@ -131,8 +132,9 @@
             final IWindowSession session = WindowManagerGlobal.getWindowSession();
             final Rect tmpRect = new Rect();
             try {
-                final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq, layoutParams,
-                        View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect);
+                final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq,
+                        layoutParams, View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect,
+                        new InsetsState());
             } catch (RemoteException e) {
                 e.printStackTrace();
             }
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index 771faaf..be1a455 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -47,6 +47,7 @@
         assertNull(c.getEncryption());
         assertNull(c.getAuthentication());
         assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId());
+        assertEquals(0, c.getXfrmInterfaceId());
     }
 
     private IpSecConfig getSampleConfig() {
@@ -77,6 +78,7 @@
         c.setNattKeepaliveInterval(42);
         c.setMarkValue(12);
         c.setMarkMask(23);
+        c.setXfrmInterfaceId(34);
 
         return c;
     }
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 9b919abf..4dc0341 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -71,6 +71,9 @@
     private final LinkAddress mLocalInnerAddress;
     private final int mFamily;
 
+    private static final int[] ADDRESS_FAMILIES =
+            new int[] {AF_INET, AF_INET6};
+
     @Parameterized.Parameters
     public static Collection ipSecConfigs() {
         return Arrays.asList(
@@ -196,6 +199,7 @@
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
@@ -231,6 +235,7 @@
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
@@ -304,7 +309,8 @@
                         eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0),
                         eq(config.getEncapType()),
                         eq(encapSocketPort),
-                        eq(config.getEncapRemotePort()));
+                        eq(config.getEncapRemotePort()),
+                        eq(config.getXfrmInterfaceId()));
     }
 
     @Test
@@ -430,6 +436,7 @@
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
         // quota is not released until the SPI is released by the Transform
         assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
@@ -452,6 +459,7 @@
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
@@ -469,6 +477,7 @@
                         anyString(),
                         anyInt(),
                         anyInt(),
+                        anyInt(),
                         anyInt());
         assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
 
@@ -504,6 +513,7 @@
                         anyString(),
                         eq(TEST_SPI),
                         anyInt(),
+                        anyInt(),
                         anyInt());
 
         // Verify quota and RefcountedResource objects cleaned up
@@ -572,11 +582,12 @@
 
         assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent);
         verify(mMockNetd)
-                .addVirtualTunnelInterface(
+                .ipSecAddTunnelInterface(
                         eq(createTunnelResp.interfaceName),
                         eq(mSourceAddr),
                         eq(mDestinationAddr),
                         anyInt(),
+                        anyInt(),
                         anyInt());
     }
 
@@ -591,7 +602,7 @@
 
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
-        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
         try {
             userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
                     createTunnelResp.resourceId);
@@ -614,7 +625,7 @@
 
         // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
-        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName));
         try {
             userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
                     createTunnelResp.resourceId);
@@ -624,6 +635,41 @@
     }
 
     @Test
+    public void testApplyTunnelModeTransform() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+
+        int transformResourceId = createTransformResp.resourceId;
+        int tunnelResourceId = createTunnelResp.resourceId;
+        mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT,
+                transformResourceId, "blessedPackage");
+
+        for (int selAddrFamily : ADDRESS_FAMILIES) {
+            verify(mMockNetd)
+                    .ipSecUpdateSecurityPolicy(
+                            eq(mUid),
+                            eq(selAddrFamily),
+                            eq(IpSecManager.DIRECTION_OUT),
+                            anyString(),
+                            anyString(),
+                            eq(TEST_SPI),
+                            anyInt(), // iKey/oKey
+                            anyInt(), // mask
+                            eq(tunnelResourceId));
+        }
+
+        ipSecConfig.setXfrmInterfaceId(tunnelResourceId);
+        verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
+    }
+
+    @Test
     public void testAddRemoveAddressFromTunnelInterface() throws Exception {
         for (String pkgName : new String[]{"blessedPackage", "systemPackage"}) {
             IpSecTunnelInterfaceResponse createTunnelResp =
diff --git a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
index b399b0d..6e07b26 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
@@ -40,6 +40,7 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -70,6 +71,7 @@
     private @Mock Handler mHandler;
     private @Mock IpConnectivityLog mLogger;
     private @Mock NetworkAgentInfo mAgent;
+    private @Mock NetworkAgentInfo mNotMeteredAgent;
     private @Mock NetworkInfo mNetworkInfo;
     private @Mock NetworkRequest mRequest;
     private @Mock TelephonyManager mTelephony;
@@ -87,6 +89,10 @@
     private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
     private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
 
+    private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
+    private static final int RETURN_CODE_DNS_SUCCESS = 0;
+    private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+
     @Before
     public void setUp() throws IOException {
         MockitoAnnotations.initMocks(this);
@@ -95,6 +101,12 @@
                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
         mAgent.networkInfo = mNetworkInfo;
 
+        mNotMeteredAgent.linkProperties = new LinkProperties();
+        mNotMeteredAgent.networkCapabilities = new NetworkCapabilities()
+            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+            .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+        mNotMeteredAgent.networkInfo = mNetworkInfo;
+
         when(mAgent.network()).thenReturn(mNetwork);
         when(mDependencies.getNetwork(any())).thenReturn(mNetwork);
         when(mDependencies.getRandom()).thenReturn(mRandom);
@@ -138,6 +150,40 @@
         when(mNetwork.getAllByName(any())).thenReturn(new InetAddress[] {
             InetAddress.parseNumericAddress("192.168.0.0")
         });
+
+        setMinDataStallEvaluateInterval(500);
+        setDataStallEvaluationType(1 << DATA_STALL_EVALUATION_TYPE_DNS);
+        setValidDataStallDnsTimeThreshold(500);
+        setConsecutiveDnsTimeoutThreshold(5);
+    }
+
+    private class WrappedNetworkMonitor extends NetworkMonitor {
+        private long mProbeTime = 0;
+
+        WrappedNetworkMonitor(Context context, Handler handler,
+                NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
+                IpConnectivityLog logger, Dependencies deps) {
+                super(context, handler, networkAgentInfo, defaultRequest, logger, deps);
+        }
+
+        @Override
+        protected long getLastProbeTime() {
+            return mProbeTime;
+        }
+
+        protected void setLastProbeTime(long time) {
+            mProbeTime = time;
+        }
+    }
+
+    WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
+        return new WrappedNetworkMonitor(
+                mContext, mHandler, mAgent, mRequest, mLogger, mDependencies);
+    }
+
+    WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
+        return new WrappedNetworkMonitor(
+                mContext, mHandler, mNotMeteredAgent, mRequest, mLogger, mDependencies);
     }
 
     NetworkMonitor makeMonitor() {
@@ -272,6 +318,113 @@
         assertPortal(makeMonitor().isCaptivePortal());
     }
 
+    @Test
+    public void testIsDataStall_EvaluationDisabled() {
+        setDataStallEvaluationType(0);
+        WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+        assertFalse(wrappedMonitor.isDataStall());
+    }
+
+    @Test
+    public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() {
+        WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        assertTrue(wrappedMonitor.isDataStall());
+    }
+
+    @Test
+    public void testIsDataStall_EvaluationDnsOnMeteredNetwork() {
+        WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+        assertFalse(wrappedMonitor.isDataStall());
+
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        assertTrue(wrappedMonitor.isDataStall());
+    }
+
+    @Test
+    public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() {
+        WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        makeDnsTimeoutEvent(wrappedMonitor, 3);
+        assertFalse(wrappedMonitor.isDataStall());
+        // Reset consecutive timeout counts.
+        makeDnsSuccessEvent(wrappedMonitor, 1);
+        makeDnsTimeoutEvent(wrappedMonitor, 2);
+        assertFalse(wrappedMonitor.isDataStall());
+
+        makeDnsTimeoutEvent(wrappedMonitor, 3);
+        assertTrue(wrappedMonitor.isDataStall());
+
+        // Set the value to larger than the default dns log size.
+        setConsecutiveDnsTimeoutThreshold(51);
+        wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        makeDnsTimeoutEvent(wrappedMonitor, 50);
+        assertFalse(wrappedMonitor.isDataStall());
+
+        makeDnsTimeoutEvent(wrappedMonitor, 1);
+        assertTrue(wrappedMonitor.isDataStall());
+    }
+
+    @Test
+    public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() {
+        // Test dns events happened in valid dns time threshold.
+        WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        assertFalse(wrappedMonitor.isDataStall());
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        assertTrue(wrappedMonitor.isDataStall());
+
+        // Test dns events happened before valid dns time threshold.
+        setValidDataStallDnsTimeThreshold(0);
+        wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        assertFalse(wrappedMonitor.isDataStall());
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        assertFalse(wrappedMonitor.isDataStall());
+    }
+
+    private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
+        for (int i = 0; i < count; i++) {
+            wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
+                    RETURN_CODE_DNS_TIMEOUT);
+        }
+    }
+
+    private void makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
+        for (int i = 0; i < count; i++) {
+            wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
+                    RETURN_CODE_DNS_SUCCESS);
+        }
+    }
+
+    private void setDataStallEvaluationType(int type) {
+        when(mDependencies.getSetting(any(),
+            eq(Settings.Global.DATA_STALL_EVALUATION_TYPE), anyInt())).thenReturn(type);
+    }
+
+    private void setMinDataStallEvaluateInterval(int time) {
+        when(mDependencies.getSetting(any(),
+            eq(Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL), anyInt())).thenReturn(time);
+    }
+
+    private void setValidDataStallDnsTimeThreshold(int time) {
+        when(mDependencies.getSetting(any(),
+            eq(Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD), anyInt())).thenReturn(time);
+    }
+
+    private void setConsecutiveDnsTimeoutThreshold(int num) {
+        when(mDependencies.getSetting(any(),
+            eq(Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD), anyInt()))
+            .thenReturn(num);
+    }
+
     private void setFallbackUrl(String url) {
         when(mDependencies.getSetting(any(),
                 eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url);
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index af7123b..f2bd770 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -27,9 +27,17 @@
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.os.Process.SYSTEM_UID;
 
+import static com.android.server.connectivity.PermissionMonitor.NETWORK;
+import static com.android.server.connectivity.PermissionMonitor.SYSTEM;
+
+import static junit.framework.Assert.fail;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.spy;
@@ -40,6 +48,8 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Build;
+import android.os.INetworkManagementService;
+import android.os.UserHandle;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -48,12 +58,19 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.HashMap;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PermissionMonitorTest {
-    private static final int MOCK_UID = 10001;
-    private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" };
+    private static final int MOCK_USER1 = 0;
+    private static final int MOCK_USER2 = 1;
+    private static final int MOCK_UID1 = 10001;
+    private static final String MOCK_PACKAGE1 = "appName1";
+    private static final String SYSTEM_PACKAGE1 = "sysName1";
+    private static final String SYSTEM_PACKAGE2 = "sysName2";
     private static final String PARTITION_SYSTEM = "system";
     private static final String PARTITION_OEM = "oem";
     private static final String PARTITION_PRODUCT = "product";
@@ -63,6 +80,7 @@
 
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
+    @Mock private INetworkManagementService mNMS;
 
     private PermissionMonitor mPermissionMonitor;
 
@@ -70,8 +88,7 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES);
-        mPermissionMonitor = spy(new PermissionMonitor(mContext, null));
+        mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS));
     }
 
     private boolean hasBgPermission(String partition, int targetSdkVersion, int uid,
@@ -80,7 +97,8 @@
         packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion;
         packageInfo.applicationInfo.uid = uid;
         when(mPackageManager.getPackageInfoAsUser(
-                eq(MOCK_PACKAGE_NAMES[0]), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+                eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[] {MOCK_PACKAGE1});
         return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
     }
 
@@ -143,16 +161,16 @@
 
     @Test
     public void testHasUseBackgroundNetworksPermission() throws Exception {
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, NETWORK_STACK));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
-        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID,
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS));
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
 
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID));
-        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
     }
 
     @Test
@@ -172,15 +190,150 @@
 
     @Test
     public void testHasUseBackgroundNetworksPermissionVendorApp() throws Exception {
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, NETWORK_STACK));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID,
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS));
-        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
 
-        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID));
-        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+    }
+
+    private class NMSMonitor {
+        private final HashMap<Integer, Boolean> mApps = new HashMap<>();
+
+        NMSMonitor(INetworkManagementService mockNMS) throws Exception {
+            // Add hook to verify and track result of setPermission.
+            doAnswer((InvocationOnMock invocation) -> {
+                final Object[] args = invocation.getArguments();
+                final Boolean isSystem = args[0].equals("SYSTEM");
+                for (final int uid : (int[]) args[1]) {
+                    // TODO: Currently, permission monitor will send duplicate commands for each uid
+                    // corresponding to each user. Need to fix that and uncomment below test.
+                    // if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) {
+                    //     fail("uid " + uid + " is already set to " + isSystem);
+                    // }
+                    mApps.put(uid, isSystem);
+                }
+                return null;
+            }).when(mockNMS).setPermission(anyString(), any(int[].class));
+
+            // Add hook to verify and track result of clearPermission.
+            doAnswer((InvocationOnMock invocation) -> {
+                final Object[] args = invocation.getArguments();
+                for (final int uid : (int[]) args[0]) {
+                    // TODO: Currently, permission monitor will send duplicate commands for each uid
+                    // corresponding to each user. Need to fix that and uncomment below test.
+                    // if (!mApps.containsKey(uid)) {
+                    //     fail("uid " + uid + " does not exist.");
+                    // }
+                    mApps.remove(uid);
+                }
+                return null;
+            }).when(mockNMS).clearPermission(any(int[].class));
+        }
+
+        public void expectPermission(Boolean permission, int[] users, int[] apps) {
+            for (final int user : users) {
+                for (final int app : apps) {
+                    final int uid = UserHandle.getUid(user, app);
+                    if (!mApps.containsKey(uid)) {
+                        fail("uid " + uid + " does not exist.");
+                    }
+                    if (mApps.get(uid) != permission) {
+                        fail("uid " + uid + " has wrong permission: " +  permission);
+                    }
+                }
+            }
+        }
+
+        public void expectNoPermission(int[] users, int[] apps) {
+            for (final int user : users) {
+                for (final int app : apps) {
+                    final int uid = UserHandle.getUid(user, app);
+                    if (mApps.containsKey(uid)) {
+                        fail("uid " + uid + " has listed permissions, expected none.");
+                    }
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testUserAndPackageAddRemove() throws Exception {
+        final NMSMonitor mNMSMonitor = new NMSMonitor(mNMS);
+
+        // MOCK_UID1: MOCK_PACKAGE1 only has network permission.
+        // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
+        // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission.
+        doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
+        doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(SYSTEM_PACKAGE1));
+        doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(SYSTEM_PACKAGE2));
+        doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
+                eq(MOCK_PACKAGE1));
+
+        // Add SYSTEM_PACKAGE2, expect only have network permission.
+        mPermissionMonitor.onUserAdded(MOCK_USER1);
+        addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+        // Add SYSTEM_PACKAGE1, expect permission escalate.
+        addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID});
+
+        mPermissionMonitor.onUserAdded(MOCK_USER2);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+
+        addPackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
+        mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{MOCK_UID1});
+
+        // Remove MOCK_UID1, expect no permission left for all user.
+        mPermissionMonitor.onPackageRemoved(MOCK_UID1);
+        removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1});
+
+        // Remove SYSTEM_PACKAGE1, expect permission downgrade.
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2});
+        removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID});
+
+        mPermissionMonitor.onUserRemoved(MOCK_USER1);
+        mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID});
+
+        // Remove all packages, expect no permission left.
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{});
+        removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID, MOCK_UID1});
+
+        // Remove last user, expect no redundant clearPermission is invoked.
+        mPermissionMonitor.onUserRemoved(MOCK_USER2);
+        mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2},
+                new int[]{SYSTEM_UID, MOCK_UID1});
+    }
+
+    // Normal package add/remove operations will trigger multiple intent for uids corresponding to
+    // each user. To simulate generic package operations, the onPackageAdded/Removed will need to be
+    // called multiple times with the uid corresponding to each user.
+    private void addPackageForUsers(int[] users, String packageName, int uid) {
+        for (final int user : users) {
+            mPermissionMonitor.onPackageAdded(packageName, UserHandle.getUid(user, uid));
+        }
+    }
+
+    private void removePackageForUsers(int[] users, int uid) {
+        for (final int user : users) {
+            mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid));
+        }
     }
 }
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index a6ed9f2..8081812 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -23,15 +23,15 @@
 import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
 import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
 import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
 import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
 
@@ -39,19 +39,18 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.mock;
 
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -91,9 +90,9 @@
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
-import android.os.test.TestLooper;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.test.TestLooper;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -126,7 +125,6 @@
 public class TetheringTest {
     private static final int IFINDEX_OFFSET = 100;
 
-    private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
     private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
     private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
     private static final String TEST_USB_IFNAME = "test_rndis0";
@@ -370,61 +368,6 @@
         mServiceContext.unregisterReceiver(mBroadcastReceiver);
     }
 
-    private void setupForRequiredProvisioning() {
-        // Produce some acceptable looking provision app setting if requested.
-        when(mResources.getStringArray(
-                com.android.internal.R.array.config_mobile_hotspot_provision_app))
-                .thenReturn(PROVISIONING_APP_NAME);
-        // Don't disable tethering provisioning unless requested.
-        when(mSystemProperties.getBoolean(eq(Tethering.DISABLE_PROVISIONING_SYSPROP_KEY),
-                                          anyBoolean())).thenReturn(false);
-        // Act like the CarrierConfigManager is present and ready unless told otherwise.
-        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
-                .thenReturn(mCarrierConfigManager);
-        when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
-        mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
-    }
-
-    @Test
-    public void canRequireProvisioning() {
-        setupForRequiredProvisioning();
-        sendConfigurationChanged();
-        assertTrue(mTethering.isTetherProvisioningRequired());
-    }
-
-    @Test
-    public void toleratesCarrierConfigManagerMissing() {
-        setupForRequiredProvisioning();
-        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
-                .thenReturn(null);
-        sendConfigurationChanged();
-        // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
-        // We therefore still require provisioning.
-        assertTrue(mTethering.isTetherProvisioningRequired());
-    }
-
-    @Test
-    public void toleratesCarrierConfigMissing() {
-        setupForRequiredProvisioning();
-        when(mCarrierConfigManager.getConfig()).thenReturn(null);
-        sendConfigurationChanged();
-        // We still have a provisioning app configured, so still require provisioning.
-        assertTrue(mTethering.isTetherProvisioningRequired());
-    }
-
-    @Test
-    public void provisioningNotRequiredWhenAppNotFound() {
-        setupForRequiredProvisioning();
-        when(mResources.getStringArray(
-                com.android.internal.R.array.config_mobile_hotspot_provision_app))
-                .thenReturn(null);
-        assertTrue(!mTethering.isTetherProvisioningRequired());
-        when(mResources.getStringArray(
-                com.android.internal.R.array.config_mobile_hotspot_provision_app))
-                .thenReturn(new String[] {"malformedApp"});
-        assertTrue(!mTethering.isTetherProvisioningRequired());
-    }
-
     private void sendWifiApStateChanged(int state) {
         final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
         intent.putExtra(EXTRA_WIFI_AP_STATE, state);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
new file mode 100644
index 0000000..0f72229
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.connectivity.tethering;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.util.SharedLog;
+import android.os.PersistableBundle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CarrierConfigManager;
+
+import com.android.internal.R;
+import com.android.server.connectivity.MockableSystemProperties;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class EntitlementManagerTest {
+
+    private static final int EVENT_EM_UPDATE = 1;
+    private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+
+    @Mock private CarrierConfigManager mCarrierConfigManager;
+    @Mock private Context mContext;
+    @Mock private ContentResolver mContent;
+    @Mock private MockableSystemProperties mSystemProperties;
+    @Mock private Resources mResources;
+    @Mock private SharedLog mLog;
+
+    // Like so many Android system APIs, these cannot be mocked because it is marked final.
+    // We have to use the real versions.
+    private final PersistableBundle mCarrierConfig = new PersistableBundle();
+
+    private EntitlementManager mEnMgr;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getContentResolver()).thenReturn(mContent);
+        when(mResources.getStringArray(R.array.config_tether_dhcp_range))
+            .thenReturn(new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_usb_regexs))
+            .thenReturn(new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
+            .thenReturn(new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+            .thenReturn(new String[0]);
+        when(mResources.getIntArray(R.array.config_tether_upstream_types))
+            .thenReturn(new int[0]);
+        when(mLog.forSubComponent(anyString())).thenReturn(mLog);
+
+        mEnMgr = new EntitlementManager(mContext, mLog, mSystemProperties);
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+    }
+
+    @After
+    public void tearDown() throws Exception {}
+
+    private void setupForRequiredProvisioning() {
+        // Produce some acceptable looking provision app setting if requested.
+        when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+                .thenReturn(PROVISIONING_APP_NAME);
+        // Don't disable tethering provisioning unless requested.
+        when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY),
+                anyBoolean())).thenReturn(false);
+        // Act like the CarrierConfigManager is present and ready unless told otherwise.
+        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+                .thenReturn(mCarrierConfigManager);
+        when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
+        mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+    }
+
+    @Test
+    public void canRequireProvisioning() {
+        setupForRequiredProvisioning();
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        assertTrue(mEnMgr.isTetherProvisioningRequired());
+    }
+
+    @Test
+    public void toleratesCarrierConfigManagerMissing() {
+        setupForRequiredProvisioning();
+        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+            .thenReturn(null);
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
+        // Therefore provisioning still be required.
+        assertTrue(mEnMgr.isTetherProvisioningRequired());
+    }
+
+    @Test
+    public void toleratesCarrierConfigMissing() {
+        setupForRequiredProvisioning();
+        when(mCarrierConfigManager.getConfig()).thenReturn(null);
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        // We still have a provisioning app configured, so still require provisioning.
+        assertTrue(mEnMgr.isTetherProvisioningRequired());
+    }
+
+    @Test
+    public void provisioningNotRequiredWhenAppNotFound() {
+        setupForRequiredProvisioning();
+        when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+            .thenReturn(null);
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        assertFalse(mEnMgr.isTetherProvisioningRequired());
+        when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+            .thenReturn(new String[] {"malformedApp"});
+        mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+        assertFalse(mEnMgr.isTetherProvisioningRequired());
+    }
+
+}
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 91cd1cb..cb8fef9 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -1285,10 +1285,19 @@
     if clazz.fullname == "android.os.UserManager": return
 
     for m in clazz.methods:
-        if m.name.endswith("AsUser") or m.name.endswith("ForUser"): continue
         if re.match("on[A-Z]+", m.name): continue
-        if "android.os.UserHandle" in m.args:
-            warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' or 'queryFooForUser'")
+
+        has_arg = "android.os.UserHandle" in m.args
+        has_name = m.name.endswith("AsUser") or m.name.endswith("ForUser")
+
+        if clazz.fullname.endswith("Manager") and has_arg:
+            warn(clazz, m, None, "When a method overload is needed to target a specific "
+                 "UserHandle, callers should be directed to use "
+                 "Context.createPackageContextAsUser() and re-obtain the relevant "
+                 "Manager, and no new API should be added")
+        elif has_arg and not has_name:
+            warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' "
+                 "or 'queryFooForUser'")
 
 
 def verify_params(clazz):
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
new file mode 100755
index 0000000..2291e5a
--- /dev/null
+++ b/tools/hiddenapi/exclude.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+set -e
+# Make sure that entries are not added for packages that are already fully handled using
+# annotations.
+LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
+# Each team should add a <team>_PACKAGES and <team>_EMAIL with the list of packages and
+# the team email to use in the event of this detecting an entry in a <team> package. Also
+# add <team> to the TEAMS list. 
+LIBCORE_PACKAGES="\
+  android.icu \
+  android.system \
+  com.android.bouncycastle \
+  com.android.conscrypt \
+  com.android.okhttp \
+  com.sun \
+  dalvik \
+  java \
+  javax \
+  libcore \
+  org.apache.harmony \
+  org.json \
+  org.w3c.dom \
+  org.xml.sax \
+  sun \
+  "
+LIBCORE_EMAIL=libcore-team@android.com
+
+# List of teams.
+TEAMS=LIBCORE
+
+# Generate the list of packages and convert to a regular expression.
+PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
+RE=$(echo ${PACKAGES} | sed "s/ /|/g")
+git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
+    ENTRIES=$(grep -E "^L(${RE})/" <(git show $1:$file))
+    if [[ -n "${ENTRIES}" ]]; then
+      echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m"
+      echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m"
+      echo -e "\e[1m\e[31mthese entries and add annotations instead.\e[0m"
+      # Partition the entries by team and provide contact details to aid in fixing the issue.
+      for t in ${TEAMS}
+      do
+        PACKAGES=$(eval echo \${${t}_PACKAGES})
+        RE=$(echo ${PACKAGES} | sed "s/ /|/g")
+        TEAM_ENTRIES=$(grep -E "^L(${RE})/" <(echo "${ENTRIES}"))
+        if [[ -n "${TEAM_ENTRIES}" ]]; then
+          EMAIL=$(eval echo \${${t}_EMAIL})
+          echo -e "\e[33mContact ${EMAIL} or compat- for help with the following:\e[0m"
+          for i in ${ENTRIES}
+          do
+            echo -e "\e[33m  ${i}\e[0m"
+          done
+        fi
+      done
+      exit 1
+    fi
+done
diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
index 03c0b9a..afe91cd 100644
--- a/tools/stringslint/stringslint.py
+++ b/tools/stringslint/stringslint.py
@@ -145,6 +145,13 @@
             if "translatable" in child.attrib and child.attrib["translatable"].lower() == "false":
                 continue
 
+            misspelled_attributes = [
+              ("translateable", "translatable"),
+            ]
+            for misspelling, expected in misspelled_attributes:
+                if misspelling in child.attrib:
+                    error(child, "Misspelled <string> attribute.", misspelling, expected)
+
             limit = re.search("CHAR[ _-]LIMIT=(\d+|NONE|none)", comment.text)
             if limit is None:
                 info(child, "Missing CHAR LIMIT to aid translation",
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
index aa8d325..87706b9 100644
--- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -60,14 +60,27 @@
      */
     private @Nullable Pair<MacAddress, MacAddress> mBssidPatternMatcher;
     /**
+     * Whether this is an OWE network or not.
+     */
+    private boolean mIsEnhancedOpen;
+    /**
      * Pre-shared key for use with WPA-PSK networks.
      */
-    private @Nullable String mPskPassphrase;
+    private @Nullable String mWpa2PskPassphrase;
+    /**
+     * Pre-shared key for use with WPA3-SAE networks.
+     */
+    private @Nullable String mWpa3SaePassphrase;
     /**
      * The enterprise configuration details specifying the EAP method,
-     * certificates and other settings associated with the EAP.
+     * certificates and other settings associated with the WPA-EAP networks.
      */
-    private @Nullable WifiEnterpriseConfig mEnterpriseConfig;
+    private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig;
+    /**
+     * The enterprise configuration details specifying the EAP method,
+     * certificates and other settings associated with the SuiteB networks.
+     */
+    private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig;
     /**
      * This is a network that does not broadcast its SSID, so an
      * SSID-specific probe request must be used for scans.
@@ -94,8 +107,11 @@
     public WifiNetworkConfigBuilder() {
         mSsidPatternMatcher = null;
         mBssidPatternMatcher = null;
-        mPskPassphrase = null;
-        mEnterpriseConfig = null;
+        mIsEnhancedOpen = false;
+        mWpa2PskPassphrase = null;
+        mWpa3SaePassphrase = null;
+        mWpa2EnterpriseConfig = null;
+        mWpa3EnterpriseConfig = null;
         mIsHiddenSSID = false;
         mIsAppInteractionRequired = false;
         mIsUserInteractionRequired = false;
@@ -188,36 +204,81 @@
     }
 
     /**
-     * Set the ASCII PSK passphrase for this network. Needed for authenticating to
-     * WPA_PSK networks.
+     * Specifies whether this represents an Enhanced Open (OWE) network.
      *
-     * @param pskPassphrase PSK passphrase of the network.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setIsEnhancedOpen() {
+        mIsEnhancedOpen = true;
+        return this;
+    }
+
+    /**
+     * Set the ASCII WPA2 passphrase for this network. Needed for authenticating to
+     * WPA2-PSK networks.
+     *
+     * @param passphrase passphrase of the network.
      * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
      * method.
      * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
      */
-    public WifiNetworkConfigBuilder setPskPassphrase(@NonNull String pskPassphrase) {
-        checkNotNull(pskPassphrase);
+    public WifiNetworkConfigBuilder setWpa2Passphrase(@NonNull String passphrase) {
+        checkNotNull(passphrase);
         final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
-        if (!asciiEncoder.canEncode(pskPassphrase)) {
+        if (!asciiEncoder.canEncode(passphrase)) {
             throw new IllegalArgumentException("passphrase not ASCII encodable");
         }
-        mPskPassphrase = pskPassphrase;
+        mWpa2PskPassphrase = passphrase;
+        return this;
+    }
+
+    /**
+     * Set the ASCII WPA3 passphrase for this network. Needed for authenticating to
+     * WPA3-SAE networks.
+     *
+     * @param passphrase passphrase of the network.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
+     */
+    public WifiNetworkConfigBuilder setWpa3Passphrase(@NonNull String passphrase) {
+        checkNotNull(passphrase);
+        final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
+        if (!asciiEncoder.canEncode(passphrase)) {
+            throw new IllegalArgumentException("passphrase not ASCII encodable");
+        }
+        mWpa3SaePassphrase = passphrase;
         return this;
     }
 
     /**
      * Set the associated enterprise configuration for this network. Needed for authenticating to
-     * WPA_EAP networks. See {@link WifiEnterpriseConfig} for description.
+     * WPA2-EAP networks. See {@link WifiEnterpriseConfig} for description.
      *
      * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
      * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
      * method.
      */
-    public WifiNetworkConfigBuilder setEnterpriseConfig(
+    public WifiNetworkConfigBuilder setWpa2EnterpriseConfig(
             @NonNull WifiEnterpriseConfig enterpriseConfig) {
         checkNotNull(enterpriseConfig);
-        mEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+        mWpa2EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+        return this;
+    }
+
+    /**
+     * Set the associated enterprise configuration for this network. Needed for authenticating to
+     * WPA3-SuiteB networks. See {@link WifiEnterpriseConfig} for description.
+     *
+     * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setWpa3EnterpriseConfig(
+            @NonNull WifiEnterpriseConfig enterpriseConfig) {
+        checkNotNull(enterpriseConfig);
+        mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
         return this;
     }
 
@@ -324,16 +385,38 @@
         configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
     }
 
-    private void setKeyMgmtInWifiConfiguration(@NonNull WifiConfiguration configuration) {
-        if (!TextUtils.isEmpty(mPskPassphrase)) {
-            // WPA_PSK network.
+    private void setSecurityParamsInWifiConfiguration(@NonNull WifiConfiguration configuration) {
+        if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network.
             configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
-        } else if (mEnterpriseConfig != null) {
-            // WPA_EAP network
+            // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+            configuration.preSharedKey = "\"" + mWpa2PskPassphrase + "\"";
+        } else if (!TextUtils.isEmpty(mWpa3SaePassphrase)) { // WPA3-SAE network.
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
+            // PMF mandatory for SAE.
+            configuration.requirePMF = true;
+            // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+            configuration.preSharedKey = "\"" + mWpa3SaePassphrase + "\"";
+        } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network
             configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
             configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
-        } else {
-            // Open network
+            configuration.enterpriseConfig = mWpa2EnterpriseConfig;
+        } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
+            configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
+            // TODO (b/113878056): Verify these params once we verify SuiteB configuration.
+            configuration.allowedGroupMgmtCiphers.set(
+                    WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
+            configuration.allowedSuiteBCiphers.set(
+                    WifiConfiguration.SuiteBCipher.ECDHE_ECDSA);
+            configuration.allowedSuiteBCiphers.set(
+                    WifiConfiguration.SuiteBCipher.ECDHE_RSA);
+            configuration.requirePMF = true;
+            configuration.enterpriseConfig = mWpa3EnterpriseConfig;
+        } else if (mIsEnhancedOpen) { // OWE network
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
+            // PMF mandatory.
+            configuration.requirePMF = true;
+        } else { // Open network
             configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         }
     }
@@ -349,12 +432,7 @@
         if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
             wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\"";
         }
-        setKeyMgmtInWifiConfiguration(wifiConfiguration);
-        // WifiConfiguration.preSharedKey needs quotes around ASCII password.
-        if (mPskPassphrase != null) {
-            wifiConfiguration.preSharedKey = "\"" + mPskPassphrase + "\"";
-        }
-        wifiConfiguration.enterpriseConfig = mEnterpriseConfig;
+        setSecurityParamsInWifiConfiguration(wifiConfiguration);
         wifiConfiguration.hiddenSSID = mIsHiddenSSID;
         wifiConfiguration.priority = mPriority;
         wifiConfiguration.meteredOverride =
@@ -396,6 +474,20 @@
         return false;
     }
 
+    private void validateSecurityParams() {
+        int numSecurityTypes = 0;
+        numSecurityTypes += mIsEnhancedOpen ? 1 : 0;
+        numSecurityTypes += !TextUtils.isEmpty(mWpa2PskPassphrase) ? 1 : 0;
+        numSecurityTypes += !TextUtils.isEmpty(mWpa3SaePassphrase) ? 1 : 0;
+        numSecurityTypes += mWpa2EnterpriseConfig != null ? 1 : 0;
+        numSecurityTypes += mWpa3EnterpriseConfig != null ? 1 : 0;
+        if (numSecurityTypes > 1) {
+            throw new IllegalStateException("only one of setIsEnhancedOpen, setWpa2Passphrase,"
+                    + "setWpa3Passphrase, setWpa2EnterpriseConfig or setWpa3EnterpriseConfig"
+                    + " can be invoked for network specifier");
+        }
+    }
+
     /**
      * Create a specifier object used to request a Wi-Fi network. The generated
      * {@link NetworkSpecifier} should be used in
@@ -464,10 +556,7 @@
                     + "setIsUserInteractionRequired/setPriority/setIsMetered are allowed for "
                     + "specifier");
         }
-        if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
-            throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
-                    + " be invoked for network specifier");
-        }
+        validateSecurityParams();
 
         return new WifiNetworkSpecifier(
                 mSsidPatternMatcher,
@@ -493,10 +582,7 @@
             throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are"
                     + " allowed for suggestion");
         }
-        if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
-            throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
-                    + "be invoked for suggestion");
-        }
+        validateSecurityParams();
 
         return new WifiNetworkSuggestion(
                 buildWifiConfiguration(),
diff --git a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
index 25dcdd8..893b19c 100644
--- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
+++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
@@ -49,7 +49,7 @@
     /**
      * SSID of the network to connect for service sign-up.
      */
-    private final WifiSsid mOsuSsid;
+    private WifiSsid mOsuSsid;
 
     /**
      * Friendly name of the OSU provider.
@@ -130,6 +130,10 @@
         return mOsuSsid;
     }
 
+    public void setOsuSsid(WifiSsid osuSsid) {
+        mOsuSsid = osuSsid;
+    }
+
     public String getFriendlyName() {
         return mFriendlyName;
     }
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index 5c9db53..a62d63c 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -150,6 +150,12 @@
     public static final int OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22;
 
     /**
+     * The reason code for provisioning failure when an {@link OsuProvider} is not found for
+     * provisioning.
+     */
+    public static final int OSU_FAILURE_OSU_PROVIDER_NOT_FOUND = 23;
+
+    /**
      * The status code for provisioning flow to indicate connecting to OSU AP
      */
     public static final int OSU_STATUS_AP_CONNECTING = 1;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index f58a006..d0efbcf 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi.p2p;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -25,6 +26,7 @@
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.net.NetworkInfo;
 import android.net.wifi.WpsInfo;
 import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo;
 import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceResponse;
@@ -49,6 +51,8 @@
 
 import dalvik.system.CloseGuard;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -158,6 +162,14 @@
      */
     public static final String EXTRA_WIFI_STATE = "wifi_p2p_state";
 
+    /** @hide */
+    @IntDef({
+            WIFI_P2P_STATE_DISABLED,
+            WIFI_P2P_STATE_ENABLED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WifiP2pState {
+    }
+
     /**
      * Wi-Fi p2p is disabled.
      *
@@ -250,6 +262,14 @@
      */
     public static final String EXTRA_DISCOVERY_STATE = "discoveryState";
 
+    /** @hide */
+    @IntDef({
+            WIFI_P2P_DISCOVERY_STOPPED,
+            WIFI_P2P_DISCOVERY_STARTED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WifiP2pDiscoveryState {
+    }
+
     /**
      * p2p discovery has stopped
      *
@@ -502,6 +522,21 @@
     /** @hide */
     public static final int SET_ONGOING_PEER_CONFIG_SUCCEEDED       = BASE + 89;
 
+    /** @hide */
+    public static final int REQUEST_P2P_STATE                       = BASE + 90;
+    /** @hide */
+    public static final int RESPONSE_P2P_STATE                      = BASE + 91;
+
+    /** @hide */
+    public static final int REQUEST_DISCOVERY_STATE                 = BASE + 92;
+    /** @hide */
+    public static final int RESPONSE_DISCOVERY_STATE                = BASE + 93;
+
+    /** @hide */
+    public static final int REQUEST_NETWORK_INFO                    = BASE + 94;
+    /** @hide */
+    public static final int RESPONSE_NETWORK_INFO                   = BASE + 95;
+
     /**
      * Create a new WifiP2pManager instance. Applications use
      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -690,6 +725,43 @@
         public void onHandoverMessageAvailable(String handoverMessage);
     }
 
+    /** Interface for callback invocation when p2p state is available
+     *  in response to {@link #requestP2pState}.
+     */
+    public interface P2pStateListener {
+        /**
+         * The requested p2p state is available.
+         * @param state Wi-Fi p2p state
+         *        @see #WIFI_P2P_STATE_DISABLED
+         *        @see #WIFI_P2P_STATE_ENABLED
+         */
+        void onP2pStateAvailable(@WifiP2pState int state);
+    }
+
+    /** Interface for callback invocation when p2p state is available
+     *  in response to {@link #requestDiscoveryState}.
+     */
+    public interface DiscoveryStateListener {
+        /**
+         * The requested p2p discovery state is available.
+         * @param state Wi-Fi p2p discovery state
+         *        @see #WIFI_P2P_DISCOVERY_STARTED
+         *        @see #WIFI_P2P_DISCOVERY_STOPPED
+         */
+        void onDiscoveryStateAvailable(@WifiP2pDiscoveryState int state);
+    }
+
+    /** Interface for callback invocation when {@link android.net.NetworkInfo} is available
+     *  in response to {@link #requestNetworkInfo}.
+     */
+    public interface NetworkInfoListener {
+        /**
+         * The requested {@link android.net.NetworkInfo} is available
+         * @param networkInfo Wi-Fi p2p {@link android.net.NetworkInfo}
+         */
+        void onNetworkInfoAvailable(NetworkInfo networkInfo);
+    }
+
     /**
      * Interface for callback invocation when ongoing peer info is available
      * @hide
@@ -889,6 +961,24 @@
                                     .onOngoingPeerAvailable(peerConfig);
                         }
                         break;
+                    case RESPONSE_P2P_STATE:
+                        if (listener != null) {
+                            ((P2pStateListener) listener)
+                                    .onP2pStateAvailable(message.arg1);
+                        }
+                        break;
+                    case RESPONSE_DISCOVERY_STATE:
+                        if (listener != null) {
+                            ((DiscoveryStateListener) listener)
+                                    .onDiscoveryStateAvailable(message.arg1);
+                        }
+                        break;
+                    case RESPONSE_NETWORK_INFO:
+                        if (listener != null) {
+                            ((NetworkInfoListener) listener)
+                                    .onNetworkInfoAvailable((NetworkInfo) message.obj);
+                        }
+                        break;
                     default:
                         Log.d(TAG, "Ignored " + message);
                         break;
@@ -1616,4 +1706,68 @@
         c.mAsyncChannel.sendMessage(SET_ONGOING_PEER_CONFIG, 0,
                 c.putListener(listener), config);
     }
+
+    /**
+     * Request p2p enabled state.
+     *
+     * <p> This state indicates whether Wi-Fi p2p is enabled or disabled.
+     * The valid value is one of {@link #WIFI_P2P_STATE_DISABLED} or
+     * {@link #WIFI_P2P_STATE_ENABLED}. The state is returned using the
+     * {@link P2pStateListener} listener.
+     *
+     * <p> This state is also included in the {@link #WIFI_P2P_STATE_CHANGED_ACTION}
+     * broadcast event with extra {@link #EXTRA_WIFI_STATE}.
+     *
+     * @param c is the channel created at {@link #initialize}.
+     * @param listener for callback when p2p state is available..
+     */
+    public void requestP2pState(@NonNull Channel c,
+            @NonNull P2pStateListener listener) {
+        checkChannel(c);
+        if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+        c.mAsyncChannel.sendMessage(REQUEST_P2P_STATE, 0, c.putListener(listener));
+    }
+
+    /**
+     * Request p2p discovery state.
+     *
+     * <p> This state indicates whether p2p discovery has started or stopped.
+     * The valid value is one of {@link #WIFI_P2P_DISCOVERY_STARTED} or
+     * {@link #WIFI_P2P_DISCOVERY_STOPPED}. The state is returned using the
+     * {@link DiscoveryStateListener} listener.
+     *
+     * <p> This state is also included in the {@link #WIFI_P2P_DISCOVERY_CHANGED_ACTION}
+     * broadcast event with extra {@link #EXTRA_DISCOVERY_STATE}.
+     *
+     * @param c is the channel created at {@link #initialize}.
+     * @param listener for callback when discovery state is available..
+     */
+    public void requestDiscoveryState(@NonNull Channel c,
+            @NonNull DiscoveryStateListener listener) {
+        checkChannel(c);
+        if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+        c.mAsyncChannel.sendMessage(REQUEST_DISCOVERY_STATE, 0, c.putListener(listener));
+    }
+
+    /**
+     * Request network info.
+     *
+     * <p> This method provides the network info in the form of a {@link android.net.NetworkInfo}.
+     * {@link android.net.NetworkInfo#isAvailable()} indicates the p2p availability and
+     * {@link android.net.NetworkInfo#getDetailedState()} reports the current fine-grained state
+     * of the network. This {@link android.net.NetworkInfo} is returned using the
+     * {@link NetworkInfoListener} listener.
+     *
+     * <p> This information is also included in the {@link #WIFI_P2P_CONNECTION_CHANGED_ACTION}
+     * broadcast event with extra {@link #EXTRA_NETWORK_INFO}.
+     *
+     * @param c is the channel created at {@link #initialize}.
+     * @param listener for callback when network info is available..
+     */
+    public void requestNetworkInfo(@NonNull Channel c,
+            @NonNull NetworkInfoListener listener) {
+        checkChannel(c);
+        if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+        c.mAsyncChannel.sendMessage(REQUEST_NETWORK_INFO, 0, c.putListener(listener));
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index 8980ddb..c455c6f 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -22,6 +22,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.net.MacAddress;
@@ -81,11 +82,11 @@
      * pattern.
      */
     @Test
-    public void testWifiNetworkSpecifierBuilderForWpaPskNetworkWithBssidPattern() {
+    public void testWifiNetworkSpecifierBuilderForWpa2PskNetworkWithBssidPattern() {
         NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
                 .setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
                         MacAddress.fromString(TEST_BSSID_OUI_MASK))
-                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setWpa2Passphrase(TEST_PRESHARED_KEY)
                 .buildNetworkSpecifier();
 
         assertTrue(specifier instanceof WifiNetworkSpecifier);
@@ -119,7 +120,7 @@
      * SSID and BSSID pattern.
      */
     @Test
-    public void testWifiNetworkSpecifierBuilderForEnterpriseHiddenNetworkWithSsidAndBssid() {
+    public void testWifiNetworkSpecifierBuilderForWpa2EapHiddenNetworkWithSsidAndBssid() {
         WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
         enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
         enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
@@ -127,7 +128,7 @@
         NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
                 .setSsid(TEST_SSID)
                 .setBssid(MacAddress.fromString(TEST_BSSID))
-                .setEnterpriseConfig(enterpriseConfig)
+                .setWpa2EnterpriseConfig(enterpriseConfig)
                 .setIsHiddenSsid()
                 .buildNetworkSpecifier();
 
@@ -174,14 +175,14 @@
     }
 
     /**
-     * Ensure {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} throws an exception
+     * Ensure {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} throws an exception
      * when the string is not ASCII encodable.
      */
     @Test(expected = IllegalArgumentException.class)
-    public void testSetPskPassphraseWithNonAsciiString() {
+    public void testSetWpa2PasphraseWithNonAsciiString() {
         new WifiNetworkConfigBuilder()
                 .setSsid(TEST_SSID)
-                .setPskPassphrase("salvē")
+                .setWpa2Passphrase("salvē")
                 .buildNetworkSpecifier();
     }
 
@@ -275,15 +276,15 @@
 
     /**
      * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
-     * when both {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} and
-     * {@link WifiNetworkConfigBuilder#setEnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+     * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and
+     * {@link WifiNetworkConfigBuilder#setWpa2EnterpriseConfig(WifiEnterpriseConfig)} are invoked.
      */
     @Test(expected = IllegalStateException.class)
-    public void testWifiNetworkSpecifierBuilderWithBothPskPassphraseAndEnterpriseConfig() {
+    public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndEnterpriseConfig() {
         new WifiNetworkConfigBuilder()
                 .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
-                .setPskPassphrase(TEST_PRESHARED_KEY)
-                .setEnterpriseConfig(new WifiEnterpriseConfig())
+                .setWpa2Passphrase(TEST_PRESHARED_KEY)
+                .setWpa2EnterpriseConfig(new WifiEnterpriseConfig())
                 .buildNetworkSpecifier();
     }
 
@@ -375,10 +376,11 @@
      * app interaction and has a priority of zero set.
      */
     @Test
-    public void testWifiNetworkSuggestionBuilderForWpaEapNetworkWithPriorityAndReqAppInteraction() {
+    public void
+            testWifiNetworkSuggestionBuilderForWpa2EapNetworkWithPriorityAndReqAppInteraction() {
         WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
                 .setSsid(TEST_SSID)
-                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setWpa2Passphrase(TEST_PRESHARED_KEY)
                 .setIsAppInteractionRequired()
                 .setPriority(0)
                 .buildNetworkSuggestion();
@@ -401,10 +403,11 @@
      * user interaction and is metered.
      */
     @Test
-    public void testWifiNetworkSuggestionBuilderForWpaPskNetworkWithMeteredAndReqUserInteraction() {
+    public void
+            testWifiNetworkSuggestionBuilderForWpa2PskNetworkWithMeteredAndReqUserInteraction() {
         WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
                 .setSsid(TEST_SSID)
-                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setWpa2Passphrase(TEST_PRESHARED_KEY)
                 .setIsUserInteractionRequired()
                 .setIsMetered()
                 .buildNetworkSuggestion();
@@ -422,6 +425,74 @@
     }
 
     /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for OWE network.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetwork() {
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setIsEnhancedOpen()
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.OWE));
+        assertNull(suggestion.wifiConfiguration.preSharedKey);
+        assertTrue(suggestion.wifiConfiguration.requirePMF);
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SAE network.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpa3PskNetwork() {
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setWpa3Passphrase(TEST_PRESHARED_KEY)
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.SAE));
+        assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
+                suggestion.wifiConfiguration.preSharedKey);
+        assertTrue(suggestion.wifiConfiguration.requirePMF);
+    }
+
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for SuiteB network.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpa3EapNetwork() {
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+        enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+        enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
+
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setWpa3EnterpriseConfig(enterpriseConfig)
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+        assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.GCMP_256));
+        assertTrue(suggestion.wifiConfiguration.allowedGroupMgmtCiphers
+                .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+        assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers
+                .get(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA));
+        assertTrue(suggestion.wifiConfiguration.allowedSuiteBCiphers
+                .get(WifiConfiguration.SuiteBCipher.ECDHE_RSA));
+        assertTrue(suggestion.wifiConfiguration.requirePMF);
+        assertNull(suggestion.wifiConfiguration.preSharedKey);
+    }
+
+    /**
      * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
      * when {@link WifiNetworkConfigBuilder#setSsidPattern(PatternMatcher)} is set.
      */
@@ -478,4 +549,46 @@
                 .setPriority(-1)
                 .buildNetworkSuggestion();
     }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when both {@link WifiNetworkConfigBuilder#setWpa2Passphrase(String)} and
+     * {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} are invoked.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithBothWpa2PasphraseAndWpa3Passphrase() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setWpa2Passphrase(TEST_PRESHARED_KEY)
+                .setWpa3Passphrase(TEST_PRESHARED_KEY)
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and
+     * {@link WifiNetworkConfigBuilder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnterprise() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setWpa3Passphrase(TEST_PRESHARED_KEY)
+                .setWpa3EnterpriseConfig(new WifiEnterpriseConfig())
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when both {@link WifiNetworkConfigBuilder#setWpa3Passphrase(String)} and
+     * {@link WifiNetworkConfigBuilder#setIsEnhancedOpen(} are invoked.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnhancedOpen() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setWpa3Passphrase(TEST_PRESHARED_KEY)
+                .setIsEnhancedOpen()
+                .buildNetworkSpecifier();
+    }
 }