Merge "Add description for android:canRetrieveWindowContent"
diff --git a/Android.bp b/Android.bp
index cd0720d..93c94e3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -269,7 +269,7 @@
         "core/java/android/os/storage/IStorageEventListener.aidl",
         "core/java/android/os/storage/IStorageShutdownObserver.aidl",
         "core/java/android/os/storage/IObbActionListener.aidl",
-        "core/java/android/permission/IRuntimePermissionPresenter.aidl",
+        "core/java/android/permission/IPermissionController.aidl",
         "core/java/android/rolecontrollerservice/IRoleControllerService.aidl",
         ":keystore_aidl",
         "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
@@ -872,6 +872,7 @@
     local_include_dir: "core/java",
     srcs: [
         "core/java/android/net/INetworkStackConnector.aidl",
+        "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
     ],
     api_dir: "aidl/networkstack",
 }
diff --git a/CleanSpec.mk b/CleanSpec.mk
index d01e183..30c2c69 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -252,6 +252,7 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUI)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio)
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
 # ******************************************************************
diff --git a/api/current.txt b/api/current.txt
index fad607c..faf7c24 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5385,11 +5385,11 @@
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput[] getRemoteInputs();
     method public int getSemanticAction();
+    method public boolean isContextual();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR;
     field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
     field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa
-    field public static final int SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION = 11; // 0xb
     field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
     field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
     field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
@@ -5414,6 +5414,7 @@
     method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Extender);
     method public android.os.Bundle getExtras();
     method public android.app.Notification.Action.Builder setAllowGeneratedReplies(boolean);
+    method public android.app.Notification.Action.Builder setContextual(boolean);
     method public android.app.Notification.Action.Builder setSemanticAction(int);
   }
 
@@ -10199,6 +10200,7 @@
     field public static final java.lang.String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
     field public static final java.lang.String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
     field public static final java.lang.String ACTION_PASTE = "android.intent.action.PASTE";
+    field public static final java.lang.String ACTION_PERMISSION_USAGE_DETAILS = "android.intent.action.PERMISSION_USAGE_DETAILS";
     field public static final java.lang.String ACTION_PICK = "android.intent.action.PICK";
     field public static final java.lang.String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY";
     field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
@@ -10323,6 +10325,7 @@
     field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
     field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
     field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_PERMISSION_USAGE_PERMISSIONS = "android.intent.extra.PERMISSION_USAGE_PERMISSIONS";
     field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
     field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
     field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
@@ -11598,8 +11601,11 @@
     field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone";
     field public static final java.lang.String FEATURE_MIDI = "android.software.midi";
     field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
+    field public static final java.lang.String FEATURE_NFC_BEAM = "android.sofware.nfc.beam";
     field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
     field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = "android.hardware.nfc.hcef";
+    field public static final java.lang.String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = "android.hardware.nfc.ese";
+    field public static final java.lang.String FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC = "android.hardware.nfc.uicc";
     field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
     field public static final java.lang.String FEATURE_PC = "android.hardware.type.pc";
     field public static final java.lang.String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture";
@@ -24939,6 +24945,25 @@
     field public static final int TYPE_STRING = 4; // 0x4
   }
 
+  public final class MediaItem2 implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getEndPosition();
+    method public android.media.MediaMetadata getMetadata();
+    method public long getStartPosition();
+    method public void setMetadata(android.media.MediaMetadata);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.MediaItem2> CREATOR;
+    field public static final long POSITION_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
+  }
+
+  public static class MediaItem2.Builder {
+    ctor public MediaItem2.Builder();
+    method public android.media.MediaItem2 build();
+    method public android.media.MediaItem2.Builder setEndPosition(long);
+    method public android.media.MediaItem2.Builder setMetadata(android.media.MediaMetadata);
+    method public android.media.MediaItem2.Builder setStartPosition(long);
+  }
+
   public final class MediaMetadata implements android.os.Parcelable {
     method public boolean containsKey(java.lang.String);
     method public int describeContents();
@@ -29751,7 +29776,10 @@
     method public void onIdentityChanged(byte[]);
   }
 
-  public class PeerHandle {
+  public final class PeerHandle 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.net.wifi.aware.PeerHandle> CREATOR;
   }
 
   public final class PublishConfig implements android.os.Parcelable {
@@ -30010,6 +30038,7 @@
     ctor public WifiP2pGroup(android.net.wifi.p2p.WifiP2pGroup);
     method public int describeContents();
     method public java.util.Collection<android.net.wifi.p2p.WifiP2pDevice> getClientList();
+    method public int getFrequency();
     method public java.lang.String getInterface();
     method public java.lang.String getNetworkName();
     method public android.net.wifi.p2p.WifiP2pDevice getOwner();
@@ -30286,15 +30315,16 @@
     method public deprecated void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
     method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
     method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
+    method public java.util.List<java.lang.String> getSupportedOffHostSecureElements();
     method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
-    method public boolean invokeBeam(android.app.Activity);
+    method public deprecated boolean invokeBeam(android.app.Activity);
     method public boolean isEnabled();
-    method public boolean isNdefPushEnabled();
-    method public void setBeamPushUris(android.net.Uri[], android.app.Activity);
-    method public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
-    method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
-    method public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
-    method public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
+    method public deprecated boolean isNdefPushEnabled();
+    method public deprecated void setBeamPushUris(android.net.Uri[], android.app.Activity);
+    method public deprecated void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
+    method public deprecated void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
+    method public deprecated void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
+    method public deprecated void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
     field public static final java.lang.String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
     field public static final java.lang.String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
     field public static final java.lang.String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
@@ -30321,15 +30351,15 @@
     field public static final int STATE_TURNING_ON = 2; // 0x2
   }
 
-  public static abstract interface NfcAdapter.CreateBeamUrisCallback {
+  public static abstract deprecated interface NfcAdapter.CreateBeamUrisCallback {
     method public abstract android.net.Uri[] createBeamUris(android.nfc.NfcEvent);
   }
 
-  public static abstract interface NfcAdapter.CreateNdefMessageCallback {
+  public static abstract deprecated interface NfcAdapter.CreateNdefMessageCallback {
     method public abstract android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
   }
 
-  public static abstract interface NfcAdapter.OnNdefPushCompleteCallback {
+  public static abstract deprecated interface NfcAdapter.OnNdefPushCompleteCallback {
     method public abstract void onNdefPushComplete(android.nfc.NfcEvent);
   }
 
@@ -30377,8 +30407,10 @@
     method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String);
     method public boolean registerAidsForService(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
     method public boolean removeAidsForService(android.content.ComponentName, java.lang.String);
+    method public boolean setOffHostForService(android.content.ComponentName, java.lang.String);
     method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
     method public boolean supportsAidPrefixRegistration();
+    method public boolean unsetOffHostForService(android.content.ComponentName);
     method public boolean unsetPreferredService(android.app.Activity);
     field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
     field public static final java.lang.String CATEGORY_OTHER = "other";
@@ -37855,7 +37887,11 @@
     method public static java.lang.String getVolumeName(android.net.Uri);
     method public static android.provider.MediaStore.PendingSession openPending(android.content.Context, android.net.Uri);
     method public static android.net.Uri setIncludePending(android.net.Uri);
+    method public static android.net.Uri setIncludeTrashed(android.net.Uri);
     method public static android.net.Uri setRequireOriginal(android.net.Uri);
+    method public static void trash(android.content.Context, android.net.Uri);
+    method public static void trash(android.content.Context, android.net.Uri, long);
+    method public static void untrash(android.content.Context, android.net.Uri);
     field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
     field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
     field public static final java.lang.String ACTION_REVIEW = "android.provider.action.REVIEW";
@@ -38034,6 +38070,7 @@
   }
 
   public static abstract interface MediaStore.DownloadColumns implements android.provider.MediaStore.MediaColumns {
+    field public static final java.lang.String DESCRIPTION = "description";
     field public static final java.lang.String DOWNLOAD_URI = "download_uri";
     field public static final java.lang.String REFERER_URI = "referer_uri";
   }
@@ -38078,6 +38115,7 @@
     field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final java.lang.String ORIENTATION = "orientation";
     field public static final deprecated java.lang.String PICASA_ID = "picasa_id";
+    field public static final java.lang.String SECONDARY_BUCKET_ID = "secondary_bucket_id";
   }
 
   public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns {
@@ -38122,11 +38160,13 @@
   public static abstract interface MediaStore.MediaColumns implements android.provider.BaseColumns {
     field public static final deprecated java.lang.String DATA = "_data";
     field public static final java.lang.String DATE_ADDED = "date_added";
+    field public static final java.lang.String DATE_EXPIRES = "date_expires";
     field public static final java.lang.String DATE_MODIFIED = "date_modified";
     field public static final java.lang.String DISPLAY_NAME = "_display_name";
     field public static final java.lang.String HASH = "_hash";
     field public static final java.lang.String HEIGHT = "height";
     field public static final java.lang.String IS_PENDING = "is_pending";
+    field public static final java.lang.String IS_TRASHED = "is_trashed";
     field public static final java.lang.String MIME_TYPE = "mime_type";
     field public static final java.lang.String OWNER_PACKAGE_NAME = "owner_package_name";
     field public static final java.lang.String SIZE = "_size";
@@ -38202,6 +38242,7 @@
     field public static final deprecated java.lang.String LONGITUDE = "longitude";
     field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final java.lang.String RESOLUTION = "resolution";
+    field public static final java.lang.String SECONDARY_BUCKET_ID = "secondary_bucket_id";
     field public static final java.lang.String TAGS = "tags";
   }
 
@@ -42720,10 +42761,10 @@
     ctor public CallRedirectionService();
     method public final void cancelCall();
     method public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+    method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean);
     method public final boolean onUnbind(android.content.Intent);
     method public final void placeCallUnmodified();
-    method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+    method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 6c97c36..43bd8dd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -62,6 +62,7 @@
     field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
     field public static final java.lang.String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
+    field public static final java.lang.String GET_RUNTIME_PERMISSIONS = "android.permission.GET_RUNTIME_PERMISSIONS";
     field public static final java.lang.String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO";
     field public static final java.lang.String GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS = "android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS";
     field public static final java.lang.String GRANT_RUNTIME_PERMISSIONS = "android.permission.GRANT_RUNTIME_PERMISSIONS";
@@ -94,6 +95,7 @@
     field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
     field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
+    field public static final java.lang.String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
     field public static final java.lang.String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
     field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
@@ -925,6 +927,9 @@
     method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
     method public void registerAppUsageObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent);
     method public void registerUsageSessionObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit, android.app.PendingIntent, android.app.PendingIntent);
+    method public void reportUsageStart(android.app.Activity, java.lang.String);
+    method public void reportUsageStart(android.app.Activity, java.lang.String, long);
+    method public void reportUsageStop(android.app.Activity, java.lang.String);
     method public void setAppStandbyBucket(java.lang.String, int);
     method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>);
     method public void unregisterAppUsageObserver(int);
@@ -1038,6 +1043,7 @@
     field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
     field public static final java.lang.String PERMISSION_SERVICE = "permission";
     field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
+    field public static final java.lang.String ROLLBACK_SERVICE = "rollback";
     field public static final java.lang.String SECURE_ELEMENT_SERVICE = "secure_element";
     field public static final java.lang.String STATS_MANAGER = "stats";
     field public static final java.lang.String SYSTEM_UPDATE_SERVICE = "system_update";
@@ -1069,6 +1075,7 @@
     field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
     field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
     field public static final java.lang.String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION";
+    field public static final java.lang.String ACTION_PACKAGE_ROLLBACK_EXECUTED = "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
     field public static final java.lang.String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
     field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
     field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
@@ -1204,6 +1211,7 @@
     method public void setAllocateAggressive(boolean);
     method public void setAllowDowngrade(boolean);
     method public void setDontKillApp(boolean);
+    method public void setEnableRollback();
     method public void setGrantedRuntimePermissions(java.lang.String[]);
     method public void setInstallAsInstantApp(boolean);
     method public void setInstallAsVirtualPreload();
@@ -1398,6 +1406,41 @@
 
 }
 
+package android.content.rollback {
+
+  public final class PackageRollbackInfo implements android.os.Parcelable {
+    ctor public PackageRollbackInfo(java.lang.String, android.content.rollback.PackageRollbackInfo.PackageVersion, android.content.rollback.PackageRollbackInfo.PackageVersion);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR;
+    field public final android.content.rollback.PackageRollbackInfo.PackageVersion higherVersion;
+    field public final android.content.rollback.PackageRollbackInfo.PackageVersion lowerVersion;
+    field public final java.lang.String packageName;
+  }
+
+  public static class PackageRollbackInfo.PackageVersion {
+    ctor public PackageRollbackInfo.PackageVersion(long);
+    field public final long versionCode;
+  }
+
+  public final class RollbackInfo 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.content.rollback.RollbackInfo> CREATOR;
+    field public final android.content.rollback.PackageRollbackInfo targetPackage;
+  }
+
+  public final class RollbackManager {
+    method public void executeRollback(android.content.rollback.RollbackInfo, android.content.IntentSender);
+    method public void expireRollbackForPackage(java.lang.String);
+    method public android.content.rollback.RollbackInfo getAvailableRollback(java.lang.String);
+    method public java.util.List<java.lang.String> getPackagesWithAvailableRollbacks();
+    method public java.util.List<android.content.rollback.RollbackInfo> getRecentlyExecutedRollbacks();
+    method public void reloadPersistedData();
+  }
+
+}
+
 package android.hardware {
 
   public final class Sensor {
@@ -1472,8 +1515,6 @@
 
   public final class BrightnessConfiguration implements android.os.Parcelable {
     method public int describeContents();
-    method public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
-    method public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(java.lang.String);
     method public android.util.Pair<float[], float[]> getCurve();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -1481,22 +1522,10 @@
 
   public static class BrightnessConfiguration.Builder {
     ctor public BrightnessConfiguration.Builder(float[], float[]);
-    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
-    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(java.lang.String, android.hardware.display.BrightnessCorrection);
     method public android.hardware.display.BrightnessConfiguration build();
-    method public int getMaxCorrectionsByCategory();
-    method public int getMaxCorrectionsByPackageName();
     method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String);
   }
 
-  public final class BrightnessCorrection implements android.os.Parcelable {
-    method public float apply(float);
-    method public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR;
-  }
-
   public final class DisplayManager {
     method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
     method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
@@ -3869,7 +3898,6 @@
     method public void disable(int, android.net.wifi.WifiManager.ActionListener);
     method public void disableEphemeralNetwork(java.lang.String);
     method public void forget(int, android.net.wifi.WifiManager.ActionListener);
-    method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
     method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
     method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method public int getWifiApState();
@@ -4562,6 +4590,7 @@
     method public void allocateBytes(java.util.UUID, long, int) throws java.io.IOException;
     method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException;
     method public long getAllocatableBytes(java.util.UUID, int) throws java.io.IOException;
+    method public static boolean hasIsolatedStorage();
     field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1
   }
 
@@ -4569,6 +4598,16 @@
 
 package android.permission {
 
+  public abstract class PermissionControllerService extends android.app.Service {
+    ctor public PermissionControllerService();
+    method public final void attachBaseContext(android.content.Context);
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
+    method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
+    method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
+  }
+
   public final class PermissionManager {
     method public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
   }
@@ -4589,16 +4628,6 @@
     field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionPresentationInfo> CREATOR;
   }
 
-  public abstract class RuntimePermissionPresenterService extends android.app.Service {
-    ctor public RuntimePermissionPresenterService();
-    method public final void attachBaseContext(android.content.Context);
-    method public final android.os.IBinder onBind(android.content.Intent);
-    method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
-    method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
-    method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
-    field public static final java.lang.String SERVICE_INTERFACE = "android.permission.RuntimePermissionPresenterService";
-  }
-
 }
 
 package android.permissionpresenterservice {
diff --git a/api/test-current.txt b/api/test-current.txt
index 9c51c9f..1401cbb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -330,11 +330,6 @@
     method public android.os.UserHandle getUser();
     method public int getUserId();
     method public void setAutofillCompatibilityEnabled(boolean);
-    field public static final java.lang.String ROLLBACK_SERVICE = "rollback";
-  }
-
-  public class Intent implements java.lang.Cloneable android.os.Parcelable {
-    field public static final java.lang.String ACTION_PACKAGE_ROLLBACK_EXECUTED = "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
   }
 
 }
@@ -357,10 +352,6 @@
     ctor public LauncherApps(android.content.Context);
   }
 
-  public static class PackageInstaller.SessionParams implements android.os.Parcelable {
-    method public void setEnableRollback();
-  }
-
   public abstract class PackageManager {
     method public abstract boolean arePermissionsIndividuallyControlled();
     method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
@@ -408,42 +399,6 @@
 
 }
 
-package android.content.rollback {
-
-  public final class PackageRollbackInfo implements android.os.Parcelable {
-    ctor public PackageRollbackInfo(java.lang.String, android.content.rollback.PackageRollbackInfo.PackageVersion, android.content.rollback.PackageRollbackInfo.PackageVersion);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR;
-    field public final android.content.rollback.PackageRollbackInfo.PackageVersion higherVersion;
-    field public final android.content.rollback.PackageRollbackInfo.PackageVersion lowerVersion;
-    field public final java.lang.String packageName;
-  }
-
-  public static class PackageRollbackInfo.PackageVersion {
-    ctor public PackageRollbackInfo.PackageVersion(long);
-    field public final long versionCode;
-  }
-
-  public final class RollbackInfo implements android.os.Parcelable {
-    ctor public RollbackInfo(android.content.rollback.PackageRollbackInfo);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
-    field public final android.content.rollback.PackageRollbackInfo targetPackage;
-  }
-
-  public final class RollbackManager {
-    method public void executeRollback(android.content.rollback.RollbackInfo, android.content.IntentSender);
-    method public void expireRollbackForPackage(java.lang.String);
-    method public android.content.rollback.RollbackInfo getAvailableRollback(java.lang.String);
-    method public java.util.List<java.lang.String> getPackagesWithAvailableRollbacks();
-    method public java.util.List<android.content.rollback.RollbackInfo> getRecentlyExecutedRollbacks();
-    method public void reloadPersistedData();
-  }
-
-}
-
 package android.database.sqlite {
 
   public class SQLiteCompatibilityWalFlags {
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index 74edffb..b905e94 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -16,15 +16,20 @@
 
 package com.android.commands.input;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.hardware.input.InputManager;
 import android.os.SystemClock;
-import android.util.Log;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 
+import com.android.internal.os.BaseCommand;
+
+import java.io.PrintStream;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -33,9 +38,11 @@
  * desired character output.
  */
 
-public class Input {
+public class Input extends BaseCommand {
     private static final String TAG = "Input";
     private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: ";
+    private static final String INVALID_DISPLAY_ARGUMENTS =
+            "Error: Invalid arguments for display ID.";
 
     private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
         put("keyboard", InputDevice.SOURCE_KEYBOARD);
@@ -50,6 +57,7 @@
         put("joystick", InputDevice.SOURCE_JOYSTICK);
     }};
 
+    private static final Map<String, InputCmd> COMMANDS = new HashMap<String, InputCmd>();
 
     /**
      * Command-line entry point.
@@ -60,217 +68,256 @@
         (new Input()).run(args);
     }
 
-    private void run(String[] args) {
-        if (args.length < 1) {
-            showUsage();
-            return;
-        }
+    Input() {
+        COMMANDS.put("text", new InputText());
+        COMMANDS.put("keyevent", new InputKeyEvent());
+        COMMANDS.put("tap", new InputTap());
+        COMMANDS.put("swipe", new InputSwipe());
+        COMMANDS.put("draganddrop", new InputDragAndDrop());
+        COMMANDS.put("press", new InputPress());
+        COMMANDS.put("roll", new InputRoll());
+    }
 
-        int index = 0;
-        String command = args[index];
+    @Override
+    public void onRun() throws Exception {
+        String arg = nextArgRequired();
         int inputSource = InputDevice.SOURCE_UNKNOWN;
-        if (SOURCES.containsKey(command)) {
-            inputSource = SOURCES.get(command);
-            index++;
-            command = args[index];
-        }
-        final int length = args.length - index;
 
-        try {
-            if (command.equals("text")) {
-                if (length == 2) {
-                    inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
-                    sendText(inputSource, args[index+1]);
-                    return;
+        // Get source (optional).
+        if (SOURCES.containsKey(arg)) {
+            inputSource = SOURCES.get(arg);
+            arg = nextArgRequired();
+        }
+
+        // Get displayId (optional).
+        int displayId = INVALID_DISPLAY;
+        if ("-d".equals(arg)) {
+            displayId = getDisplayId();
+            arg = nextArgRequired();
+        }
+
+        // Get command and run.
+        InputCmd cmd = COMMANDS.get(arg);
+        if (cmd != null) {
+            try {
+                cmd.run(inputSource, displayId);
+                return;
+            } catch (NumberFormatException ex) {
+                throw new IllegalArgumentException(INVALID_ARGUMENTS + arg);
+            }
+        }
+
+        throw new IllegalArgumentException("Error: Unknown command: " + arg);
+    }
+
+    private int getDisplayId() {
+        String displayArg = nextArgRequired();
+        if ("INVALID_DISPLAY".equalsIgnoreCase(displayArg)) {
+            return INVALID_DISPLAY;
+        } else if ("DEFAULT_DISPLAY".equalsIgnoreCase(displayArg)) {
+            return DEFAULT_DISPLAY;
+        } else {
+            try {
+                final int displayId = Integer.parseInt(displayArg);
+                if (displayId == INVALID_DISPLAY) {
+                    return INVALID_DISPLAY;
                 }
-            } else if (command.equals("keyevent")) {
-                if (length >= 2) {
-                    final boolean longpress = "--longpress".equals(args[index + 1]);
-                    final int start = longpress ? index + 2 : index + 1;
-                    inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
-                    if (args.length > start) {
-                        for (int i = start; i < args.length; i++) {
-                            int keyCode = KeyEvent.keyCodeFromString(args[i]);
-                            sendKeyEvent(inputSource, keyCode, longpress);
-                        }
-                        return;
+                return Math.max(displayId, 0);
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException(INVALID_DISPLAY_ARGUMENTS);
+            }
+        }
+    }
+
+    class InputText implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
+            sendText(inputSource, nextArgRequired(), displayId);
+        }
+
+        /**
+         * Convert the characters of string text into key event's and send to
+         * device.
+         *
+         * @param text is a string of characters you want to input to the device.
+         */
+        private void sendText(int source, final String text, int displayId) {
+            final StringBuffer buff = new StringBuffer(text);
+            boolean escapeFlag = false;
+            for (int i = 0; i < buff.length(); i++) {
+                if (escapeFlag) {
+                    escapeFlag = false;
+                    if (buff.charAt(i) == 's') {
+                        buff.setCharAt(i, ' ');
+                        buff.deleteCharAt(--i);
                     }
                 }
-            } else if (command.equals("tap")) {
-                if (length == 3) {
-                    inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
-                    sendTap(inputSource, Float.parseFloat(args[index+1]),
-                            Float.parseFloat(args[index+2]));
-                    return;
+                if (buff.charAt(i) == '%') {
+                    escapeFlag = true;
                 }
-            } else if (command.equals("swipe")) {
-                int duration = -1;
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
-                switch (length) {
-                    case 6:
-                        duration = Integer.parseInt(args[index+5]);
-                    case 5:
-                        sendSwipe(inputSource,
-                                Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
-                                Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
-                                duration);
-                        return;
-                }
-            } else if (command.equals("draganddrop")) {
-                int duration = -1;
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
-                switch (length) {
-                    case 6:
-                        duration = Integer.parseInt(args[index+5]);
-                    case 5:
-                        sendDragAndDrop(inputSource,
-                                Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
-                                Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
-                                duration);
-                        return;
-                }
-            } else if (command.equals("press")) {
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
-                if (length == 1) {
-                    sendTap(inputSource, 0.0f, 0.0f);
-                    return;
-                }
-            } else if (command.equals("roll")) {
-                inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
-                if (length == 3) {
-                    sendMove(inputSource, Float.parseFloat(args[index+1]),
-                            Float.parseFloat(args[index+2]));
-                    return;
-                }
-            } else {
-                System.err.println("Error: Unknown command: " + command);
-                showUsage();
-                return;
             }
-        } catch (NumberFormatException ex) {
+
+            final char[] chars = buff.toString().toCharArray();
+            final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+            final KeyEvent[] events = kcm.getEvents(chars);
+            for (int i = 0; i < events.length; i++) {
+                KeyEvent e = events[i];
+                if (source != e.getSource()) {
+                    e.setSource(source);
+                }
+                e.setDisplayId(displayId);
+                injectKeyEvent(e);
+            }
         }
-        System.err.println(INVALID_ARGUMENTS + command);
-        showUsage();
+    }
+
+    class InputKeyEvent implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            String arg = nextArgRequired();
+            final boolean longpress = "--longpress".equals(arg);
+            if (longpress) {
+                arg = nextArgRequired();
+            }
+
+            do {
+                final int keycode = KeyEvent.keyCodeFromString(arg);
+                sendKeyEvent(inputSource, keycode, longpress, displayId);
+            } while ((arg = nextArg()) != null);
+        }
+
+        private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) {
+            final long now = SystemClock.uptimeMillis();
+            int repeatCount = 0;
+
+            KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount,
+                    0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0/*flags*/,
+                    inputSource);
+            event.setDisplayId(displayId);
+
+            injectKeyEvent(event);
+            if (longpress) {
+                repeatCount++;
+                injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount,
+                        KeyEvent.FLAG_LONG_PRESS));
+            }
+            injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+        }
+    }
+
+    class InputTap implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+            sendTap(inputSource, Float.parseFloat(nextArgRequired()),
+                    Float.parseFloat(nextArgRequired()), displayId);
+        }
+
+        void sendTap(int inputSource, float x, float y, int displayId) {
+            final long now = SystemClock.uptimeMillis();
+            injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f,
+                    displayId);
+            injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId);
+        }
+    }
+
+    class InputPress extends InputTap {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+            sendTap(inputSource, 0.0f, 0.0f, displayId);
+        }
+    }
+
+    class InputSwipe implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+            sendSwipe(inputSource, displayId, false);
+        }
+
+        void sendSwipe(int inputSource, int displayId, boolean isDragDrop) {
+            // Parse two points and duration.
+            final float x1 = Float.parseFloat(nextArgRequired());
+            final float y1 = Float.parseFloat(nextArgRequired());
+            final float x2 = Float.parseFloat(nextArgRequired());
+            final float y2 = Float.parseFloat(nextArgRequired());
+            String durationArg = nextArg();
+            int duration = durationArg != null ? Integer.parseInt(durationArg) : -1;
+            if (duration < 0) {
+                duration = 300;
+            }
+
+            final long down = SystemClock.uptimeMillis();
+            injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, down, down, x1, y1, 1.0f,
+                    displayId);
+            if (isDragDrop) {
+                // long press until drag start.
+                try {
+                    Thread.sleep(ViewConfiguration.getLongPressTimeout());
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            long now = SystemClock.uptimeMillis();
+            final long endTime = down + duration;
+            while (now < endTime) {
+                final long elapsedTime = now - down;
+                final float alpha = (float) elapsedTime / duration;
+                injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
+                        lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
+                now = SystemClock.uptimeMillis();
+            }
+            injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
+                    displayId);
+        }
+    }
+
+    class InputDragAndDrop extends InputSwipe {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+            sendSwipe(inputSource, displayId, true);
+        }
+    }
+
+    class InputRoll implements InputCmd {
+        @Override
+        public void run(int inputSource, int displayId) {
+            inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+            sendMove(inputSource, Float.parseFloat(nextArgRequired()),
+                    Float.parseFloat(nextArgRequired()), displayId);
+        }
+
+        /**
+         * Sends a simple zero-pressure move event.
+         *
+         * @param inputSource the InputDevice.SOURCE_* sending the input event
+         * @param dx change in x coordinate due to move
+         * @param dy change in y coordinate due to move
+         */
+        private void sendMove(int inputSource, float dx, float dy, int displayId) {
+            final long now = SystemClock.uptimeMillis();
+            injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, now, dx, dy, 0.0f,
+                    displayId);
+        }
     }
 
     /**
-     * Convert the characters of string text into key event's and send to
-     * device.
-     *
-     * @param text is a string of characters you want to input to the device.
+     * Abstract class for command
+     * use nextArgRequired or nextArg to check next argument if necessary.
      */
-    private void sendText(int source, String text) {
-
-        StringBuffer buff = new StringBuffer(text);
-
-        boolean escapeFlag = false;
-        for (int i=0; i<buff.length(); i++) {
-            if (escapeFlag) {
-                escapeFlag = false;
-                if (buff.charAt(i) == 's') {
-                    buff.setCharAt(i, ' ');
-                    buff.deleteCharAt(--i);
-                }
-            }
-            if (buff.charAt(i) == '%') {
-                escapeFlag = true;
-            }
-        }
-
-        char[] chars = buff.toString().toCharArray();
-
-        KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
-        KeyEvent[] events = kcm.getEvents(chars);
-        for(int i = 0; i < events.length; i++) {
-            KeyEvent e = events[i];
-            if (source != e.getSource()) {
-                e.setSource(source);
-            }
-            injectKeyEvent(e);
-        }
+    private interface InputCmd {
+        void run(int inputSource, int displayId);
     }
 
-    private void sendKeyEvent(int inputSource, int keyCode, boolean longpress) {
-        long now = SystemClock.uptimeMillis();
-        injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0,
-                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
-        if (longpress) {
-            injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0,
-                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS,
-                    inputSource));
-        }
-        injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0,
-                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
-    }
-
-    private void sendTap(int inputSource, float x, float y) {
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x, y, 1.0f);
-        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x, y, 0.0f);
-    }
-
-    private void sendSwipe(int inputSource, float x1, float y1, float x2, float y2, int duration) {
-        if (duration < 0) {
-            duration = 300;
-        }
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
-        long startTime = now;
-        long endTime = startTime + duration;
-        while (now < endTime) {
-            long elapsedTime = now - startTime;
-            float alpha = (float) elapsedTime / duration;
-            injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
-                    lerp(y1, y2, alpha), 1.0f);
-            now = SystemClock.uptimeMillis();
-        }
-        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
-    }
-
-    private void sendDragAndDrop(int inputSource, float x1, float y1, float x2, float y2,
-            int dragDuration) {
-        if (dragDuration < 0) {
-            dragDuration = 300;
-        }
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
-        try {
-            Thread.sleep(ViewConfiguration.getLongPressTimeout());
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        }
-        now = SystemClock.uptimeMillis();
-        long startTime = now;
-        long endTime = startTime + dragDuration;
-        while (now < endTime) {
-            long elapsedTime = now - startTime;
-            float alpha = (float) elapsedTime / dragDuration;
-            injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
-                    lerp(y1, y2, alpha), 1.0f);
-            now = SystemClock.uptimeMillis();
-        }
-        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
-    }
-
-    /**
-     * Sends a simple zero-pressure move event.
-     *
-     * @param inputSource the InputDevice.SOURCE_* sending the input event
-     * @param dx change in x coordinate due to move
-     * @param dy change in y coordinate due to move
-     */
-    private void sendMove(int inputSource, float dx, float dy) {
-        long now = SystemClock.uptimeMillis();
-        injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, dx, dy, 0.0f);
-    }
-
-    private void injectKeyEvent(KeyEvent event) {
-        Log.i(TAG, "injectKeyEvent: " + event);
+    private static void injectKeyEvent(KeyEvent event) {
         InputManager.getInstance().injectInputEvent(event,
                 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
     }
 
-    private int getInputDeviceId(int inputSource) {
+    private static int getInputDeviceId(int inputSource) {
         final int DEFAULT_DEVICE_ID = 0;
         int[] devIds = InputDevice.getDeviceIds();
         for (int devId : devIds) {
@@ -287,22 +334,27 @@
      *
      * @param inputSource the InputDevice.SOURCE_* sending the input event
      * @param action the MotionEvent.ACTION_* for the event
+     * @param downTime the value of the ACTION_DOWN event happened
      * @param when the value of SystemClock.uptimeMillis() at which the event happened
      * @param x x coordinate of event
      * @param y y coordinate of event
      * @param pressure pressure of event
      */
-    private void injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure) {
+    private static void injectMotionEvent(int inputSource, int action, long downTime, long when,
+            float x, float y, float pressure, int displayId) {
         final float DEFAULT_SIZE = 1.0f;
         final int DEFAULT_META_STATE = 0;
         final float DEFAULT_PRECISION_X = 1.0f;
         final float DEFAULT_PRECISION_Y = 1.0f;
         final int DEFAULT_EDGE_FLAGS = 0;
-        MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, DEFAULT_SIZE,
+        MotionEvent event = MotionEvent.obtain(downTime, when, action, x, y, pressure, DEFAULT_SIZE,
                 DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y,
                 getInputDeviceId(inputSource), DEFAULT_EDGE_FLAGS);
         event.setSource(inputSource);
-        Log.i(TAG, "injectMotionEvent: " + event);
+        if (displayId == INVALID_DISPLAY && (inputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+            displayId = DEFAULT_DISPLAY;
+        }
+        event.setDisplayId(displayId);
         InputManager.getInstance().injectInputEvent(event,
                 InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
     }
@@ -315,24 +367,29 @@
         return inputSource == InputDevice.SOURCE_UNKNOWN ? defaultSource : inputSource;
     }
 
-    private void showUsage() {
-        System.err.println("Usage: input [<source>] <command> [<arg>...]");
-        System.err.println();
-        System.err.println("The sources are: ");
+    @Override
+    public void onShowUsage(PrintStream out) {
+        out.println("Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]");
+        out.println();
+        out.println("The sources are: ");
         for (String src : SOURCES.keySet()) {
-            System.err.println("      " + src);
+            out.println("      " + src);
         }
-        System.err.println();
-        System.err.println("The commands and default sources are:");
-        System.err.println("      text <string> (Default: touchscreen)");
-        System.err.println("      keyevent [--longpress] <key code number or name> ..."
+        out.println();
+        out.printf("-d: specify the display ID.\n"
+                + "      (Default: %d for key event, %d for motion event if not specified.)",
+                INVALID_DISPLAY, DEFAULT_DISPLAY);
+        out.println();
+        out.println("The commands and default sources are:");
+        out.println("      text <string> (Default: touchscreen)");
+        out.println("      keyevent [--longpress] <key code number or name> ..."
                 + " (Default: keyboard)");
-        System.err.println("      tap <x> <y> (Default: touchscreen)");
-        System.err.println("      swipe <x1> <y1> <x2> <y2> [duration(ms)]"
+        out.println("      tap <x> <y> (Default: touchscreen)");
+        out.println("      swipe <x1> <y1> <x2> <y2> [duration(ms)]"
                 + " (Default: touchscreen)");
-        System.err.println("      draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
+        out.println("      draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
                 + " (Default: touchscreen)");
-        System.err.println("      press (Default: trackball)");
-        System.err.println("      roll <dx> <dy> (Default: trackball)");
+        out.println("      press (Default: trackball)");
+        out.println("      roll <dx> <dy> (Default: trackball)");
     }
 }
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index d610f66..59b2aa6 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -332,6 +332,8 @@
         "src/stats_log.proto",
         "src/statsd_config.proto",
         "src/atoms.proto",
+        "src/shell/shell_config.proto",
+        "src/shell/shell_data.proto",
     ],
 
     static_libs: [
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index f2a4663..3107b4d 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -360,7 +360,11 @@
             if (mShellSubscriber == nullptr) {
                 mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
             }
-            mShellSubscriber->startNewSubscription(in, out, resultReceiver);
+            int timeoutSec = -1;
+            if (argCount >= 2) {
+                timeoutSec = atoi(args[1].c_str());
+            }
+            mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec);
             return NO_ERROR;
         }
     }
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index 22883f3..52d5ffc 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -30,7 +30,8 @@
 
 const static int FIELD_ID_ATOM = 1;
 
-void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver) {
+void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver,
+                                           int timeoutSec) {
     VLOG("start new shell subscription");
     {
         std::lock_guard<std::mutex> lock(mMutex);
@@ -50,11 +51,18 @@
     // Read config forever until EOF is reached. Clients may send multiple configs -- each new
     // config replace the previous one.
     readConfig(in);
+    VLOG("timeout : %d", timeoutSec);
 
     // Now we have read an EOF we now wait for the semaphore until the client exits.
     VLOG("Now wait for client to exit");
     std::unique_lock<std::mutex> lk(mMutex);
-    mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+
+    if (timeoutSec > 0) {
+        mShellDied.wait_for(lk, timeoutSec * 1s,
+                            [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+    } else {
+        mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+    }
 }
 
 void ShellSubscriber::updateConfig(const ShellSubscription& config) {
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
index 5401f31..8e54a8b 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -65,7 +65,8 @@
     /**
      * Start a new subscription.
      */
-    void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver);
+    void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver,
+                              int timeoutSec);
 
     void binderDied(const wp<IBinder>& who);
 
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index a184f56..73d1fd7 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -83,7 +83,7 @@
 
     // mimic a binder thread that a shell subscriber runs on. it would block.
     std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] {
-        shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver);
+        shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver, -1);
     });
     reader.detach();
 
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index 0775afe..de818a8 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -54,7 +54,8 @@
         "AID_SYSTEM",
         "AID_ROOT",
         "AID_BLUETOOTH",
-        "AID_LMKD"
+        "AID_LMKD",
+        "com.android.managedprovisioning"
     };
     private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
 
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 7a993fd..97dcee6 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -2768,94 +2768,6 @@
 Lcom/android/internal/telephony/Connection;->mIsIncoming:Z
 Lcom/android/internal/telephony/Connection;->mNumberPresentation:I
 Lcom/android/internal/telephony/Connection;->setVideoState(I)V
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getApnType()Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getReason()Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getState()Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isConnectable()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isDisconnected()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isEnabled()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isReady()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mApnType:Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCount:I
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCountLock:Ljava/lang/Object;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->setState(Lcom/android/internal/telephony/DctConstants$State;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;->mApnContext:Lcom/android/internal/telephony/dataconnection/ApnContext;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->clearSettings()V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->dumpToLog()V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->initConnection(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)Z
-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;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingErrorCreatingConnection:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectionErrorCreatingConnection;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectingState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectParams:Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mId:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mInactiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcInactiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mLinkProperties:Landroid/net/LinkProperties;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mNetworkInfo:Landroid/net/NetworkInfo;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mPhone:Lcom/android/internal/telephony/Phone;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mRilRat:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfConnected(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;Z)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->onConnect(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->tearDownData(Ljava/lang/Object;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->updateTcpBufferSizes(I)V
-Lcom/android/internal/telephony/dataconnection/DcController;->lr(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcController;->mDcListActiveByCid:Ljava/util/HashMap;
-Lcom/android/internal/telephony/dataconnection/DcController;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
-Lcom/android/internal/telephony/dataconnection/DcController;->mDcTesterDeactivateAll:Lcom/android/internal/telephony/dataconnection/DcTesterDeactivateAll;
-Lcom/android/internal/telephony/dataconnection/DcTracker$RecoveryAction;->isAggressiveRecovery(I)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->cancelReconnectAlarm(Lcom/android/internal/telephony/dataconnection/ApnContext;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpAllConnections(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->createAllApnList()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getActiveApnTypes()[Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getOverallState()Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getUiccRecords(I)Lcom/android/internal/telephony/uicc/IccRecords;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isConnected()Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isDisconnected()Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isOnlySingleDcAllowed(I)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->loge(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAllApnSettings:Ljava/util/ArrayList;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mApnContexts:Ljava/util/concurrent/ConcurrentHashMap;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAttached:Ljava/util/concurrent/atomic/AtomicBoolean;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAutoAttachOnCreation:Ljava/util/concurrent/atomic/AtomicBoolean;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mDataConnectionTracker:Landroid/os/Handler;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mDisconnectPendingCount:I
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsPsRestricted:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsScreenOn:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollEnabled:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollPeriod:I
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mPhone:Lcom/android/internal/telephony/Phone;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mPrioritySortedApnContexts:Ljava/util/PriorityQueue;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mProvisioningSpinner:Landroid/app/ProgressDialog;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mResolver:Landroid/content/ContentResolver;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mState:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mSubscriptionManager:Landroid/telephony/SubscriptionManager;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->notifyDataConnection(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->notifyOffApnsOfAvailability(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentDataStallAlarm(Landroid/content/Intent;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentProvisioningApnAlarm(Landroid/content/Intent;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onRecordsLoadedOrSubIdChanged()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Lcom/android/internal/telephony/dataconnection/ApnContext;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->registerSettingsObserver()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->resetPollStats()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->restartDataStallAlarm()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setInitialAttachApn()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setPreferredApn(I)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setRadio(Z)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setupDataOnConnectableApns(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->startDataStallAlarm(Z)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->startNetStatPoll()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->stopDataStallAlarm()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->stopNetStatPoll()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->unregisterForAllDataDisconnected(Landroid/os/Handler;)V
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity;
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity;
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity;
@@ -3290,7 +3202,7 @@
 Lcom/android/internal/telephony/ITelephonyRegistry;->listen(Ljava/lang/String;Lcom/android/internal/telephony/IPhoneStateListener;IZ)V
 Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCallState(ILjava/lang/String;)V
 Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCellInfo(Ljava/util/List;)V
-Lcom/android/internal/telephony/ITelephonyRegistry;->notifyDataConnectionFailed(Ljava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/ITelephonyRegistry;->notifyDataConnectionFailed(Ljava/lang/String;)V
 Lcom/android/internal/telephony/IWapPushManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IWapPushManager;
 Lcom/android/internal/telephony/IWapPushManager;->addPackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZZ)Z
 Lcom/android/internal/telephony/IWapPushManager;->deletePackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 997ed25..e86fa89 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -794,7 +795,7 @@
      *
      * @see android.R.styleable#AccessibilityService_nonInteractiveUiTimeout
      */
-    public void setNonInteractiveUiTimeoutMillis(int timeout) {
+    public void setNonInteractiveUiTimeoutMillis(@IntRange(from = 0) int timeout) {
         mNonInteractiveUiTimeout = timeout;
     }
 
@@ -821,7 +822,7 @@
      *
      * @see android.R.styleable#AccessibilityService_interactiveUiTimeout
      */
-    public void setInteractiveUiTimeoutMillis(int timeout) {
+    public void setInteractiveUiTimeoutMillis(@IntRange(from = 0) int timeout) {
         mInteractiveUiTimeout = timeout;
     }
 
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index b42d53a..0b50916 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -73,6 +73,12 @@
             IBinder whitelistToken, long duration);
 
     /**
+     * Allows for a {@link PendingIntent} to be whitelisted to start activities from background.
+     */
+    public abstract void setPendingIntentAllowBgActivityStarts(
+            IIntentSender target, IBinder whitelistToken, int flags);
+
+    /**
      * Allow DeviceIdleController to tell us about what apps are whitelisted.
      */
     public abstract void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids);
@@ -250,7 +256,7 @@
     public abstract int broadcastIntentInPackage(String packageName, int uid, Intent intent,
             String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized,
-            boolean sticky, int userId);
+            boolean sticky, int userId, boolean allowBackgroundActivityStarts);
     public abstract ComponentName startServiceInPackage(int uid, Intent service,
             String resolvedType, boolean fgRequired, String callingPackage, int userId)
             throws TransactionTooLargeException;
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 0b5776e..57132a7 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -37,6 +37,7 @@
 import android.view.SurfaceHolder;
 import android.view.SurfaceSession;
 import android.view.SurfaceView;
+import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -320,7 +321,7 @@
         public void surfaceCreated(SurfaceHolder surfaceHolder) {
             mTmpSurface = new Surface();
             if (mVirtualDisplay == null) {
-                initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface()));
+                initVirtualDisplay(new SurfaceSession());
                 if (mVirtualDisplay != null && mActivityViewCallback != null) {
                     mActivityViewCallback.onActivityViewReady(ActivityView.this);
                 }
@@ -354,6 +355,12 @@
         }
     }
 
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        mSurfaceView.setVisibility(visibility);
+    }
+
     private void initVirtualDisplay(SurfaceSession surfaceSession) {
         if (mVirtualDisplay != null) {
             throw new IllegalStateException("Trying to initialize for the second time.");
@@ -382,6 +389,7 @@
 
         mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
                 .setContainerLayer(true)
+                .setParent(mSurfaceView.getSurfaceControl())
                 .setName(DISPLAY_NAME)
                 .build();
 
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 3a2038d..666f721 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -142,19 +142,20 @@
      *
      * @param which either {@link WallpaperManager#FLAG_LOCK}
      * or {@link WallpaperManager#FLAG_SYSTEM}
+     * @param displayId Which display is interested
      * @return colors of chosen wallpaper
      */
-    WallpaperColors getWallpaperColors(int which, int userId);
+    WallpaperColors getWallpaperColors(int which, int userId, int displayId);
 
     /**
-     * Register a callback to receive color updates
+     * Register a callback to receive color updates from a display
      */
-    void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId);
+    void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId);
 
     /**
-     * Unregister a callback that was receiving color updates
+     * Unregister a callback that was receiving color updates from a display
      */
-    void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId);
+    void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId);
 
     /**
      * Called from SystemUI when it shows the AoD UI.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e066f06..b657a91 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1427,18 +1427,13 @@
          */
         public static final int SEMANTIC_ACTION_CALL = 10;
 
-        /**
-         * {@code SemanticAction}: Contextual action - dependent on the current notification. E.g.
-         * open a Map application with an address shown in the notification.
-         */
-        public static final int SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION = 11;
-
         private final Bundle mExtras;
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         private Icon mIcon;
         private final RemoteInput[] mRemoteInputs;
         private boolean mAllowGeneratedReplies = true;
         private final @SemanticAction int mSemanticAction;
+        private final boolean mIsContextual;
 
         /**
          * Small icon representing the action.
@@ -1474,6 +1469,7 @@
             mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
             mAllowGeneratedReplies = in.readInt() == 1;
             mSemanticAction = in.readInt();
+            mIsContextual = in.readInt() == 1;
         }
 
         /**
@@ -1482,13 +1478,13 @@
         @Deprecated
         public Action(int icon, CharSequence title, PendingIntent intent) {
             this(Icon.createWithResource("", icon), title, intent, new Bundle(), null, true,
-                    SEMANTIC_ACTION_NONE);
+                    SEMANTIC_ACTION_NONE, false /* isContextual */);
         }
 
         /** Keep in sync with {@link Notification.Action.Builder#Builder(Action)}! */
         private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras,
                 RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
-                       @SemanticAction int semanticAction) {
+                       @SemanticAction int semanticAction, boolean isContextual) {
             this.mIcon = icon;
             if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
                 this.icon = icon.getResId();
@@ -1499,6 +1495,7 @@
             this.mRemoteInputs = remoteInputs;
             this.mAllowGeneratedReplies = allowGeneratedReplies;
             this.mSemanticAction = semanticAction;
+            this.mIsContextual = isContextual;
         }
 
         /**
@@ -1546,6 +1543,15 @@
         }
 
         /**
+         * Returns whether this is a contextual Action, i.e. whether the action is dependent on the
+         * notification message body. An example of a contextual action could be an action opening a
+         * map application with an address shown in the notification.
+         */
+        public boolean isContextual() {
+            return mIsContextual;
+        }
+
+        /**
          * Get the list of inputs to be collected from the user that ONLY accept data when this
          * action is sent. These remote inputs are guaranteed to return true on a call to
          * {@link RemoteInput#isDataOnly}.
@@ -1570,6 +1576,7 @@
             private final Bundle mExtras;
             private ArrayList<RemoteInput> mRemoteInputs;
             private @SemanticAction int mSemanticAction;
+            private boolean mIsContextual;
 
             /**
              * Construct a new builder for {@link Action} object.
@@ -1684,6 +1691,16 @@
             }
 
             /**
+             * Sets whether this {@link Action} is a contextual action, i.e. whether the action is
+             * dependent on the notification message body. An example of a contextual action could
+             * be an action opening a map application with an address shown in the notification.
+             */
+            public Builder setContextual(boolean isContextual) {
+                mIsContextual = isContextual;
+                return this;
+            }
+
+            /**
              * Apply an extender to this action builder. Extenders may be used to add
              * metadata or change options on this builder.
              */
@@ -1697,7 +1714,7 @@
              * necessary to display the action.
              */
             private void checkContextualActionNullFields() {
-                if (mSemanticAction != SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) return;
+                if (!mIsContextual) return;
 
                 if (mIcon == null) {
                     throw new NullPointerException("Contextual Actions must contain a valid icon");
@@ -1743,7 +1760,7 @@
                 RemoteInput[] textInputsArr = textInputs.isEmpty()
                         ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
                 return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
-                        mAllowGeneratedReplies, mSemanticAction);
+                        mAllowGeneratedReplies, mSemanticAction, mIsContextual);
             }
         }
 
@@ -1756,7 +1773,8 @@
                     mExtras == null ? new Bundle() : new Bundle(mExtras),
                     getRemoteInputs(),
                     getAllowGeneratedReplies(),
-                    getSemanticAction());
+                    getSemanticAction(),
+                    isContextual());
         }
 
         @Override
@@ -1784,6 +1802,7 @@
             out.writeTypedArray(mRemoteInputs, flags);
             out.writeInt(mAllowGeneratedReplies ? 1 : 0);
             out.writeInt(mSemanticAction);
+            out.writeInt(mIsContextual ? 1 : 0);
         }
 
         public static final Parcelable.Creator<Action> CREATOR =
@@ -2073,8 +2092,7 @@
                 SEMANTIC_ACTION_UNMUTE,
                 SEMANTIC_ACTION_THUMBS_UP,
                 SEMANTIC_ACTION_THUMBS_DOWN,
-                SEMANTIC_ACTION_CALL,
-                SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION
+                SEMANTIC_ACTION_CALL
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface SemanticAction {}
@@ -3230,8 +3248,7 @@
     }
 
     /**
-     * Returns the actions that are contextual (marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) out
-     * of the actions in this notification.
+     * Returns the actions that are contextual out of the actions in this notification.
      *
      * @hide
      */
@@ -3240,8 +3257,7 @@
 
         List<Notification.Action> contextualActions = new ArrayList<>();
         for (Notification.Action action : actions) {
-            if (action.getSemanticAction()
-                    == Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+            if (action.isContextual()) {
                 contextualActions.add(action);
             }
         }
@@ -5062,8 +5078,7 @@
                 List<Notification.Action> actions) {
             List<Notification.Action> nonContextualActions = new ArrayList<>();
             for (Notification.Action action : actions) {
-                if (action.getSemanticAction()
-                        != Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+                if (!action.isContextual()) {
                     nonContextualActions.add(action);
                 }
             }
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index bf3d885..bbe5b8b 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -69,6 +69,22 @@
             | DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK
             | DISABLE_SEARCH;
 
+    @IntDef(flag = true, prefix = {"DISABLE_"}, value = {
+            DISABLE_NONE,
+            DISABLE_EXPAND,
+            DISABLE_NOTIFICATION_ICONS,
+            DISABLE_NOTIFICATION_ALERTS,
+            DISABLE_NOTIFICATION_TICKER,
+            DISABLE_SYSTEM_INFO,
+            DISABLE_HOME,
+            DISABLE_RECENT,
+            DISABLE_BACK,
+            DISABLE_CLOCK,
+            DISABLE_SEARCH
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DisableFlags {}
+
     /**
      * Flag to disable quick settings.
      *
@@ -104,10 +120,25 @@
     public static final int WINDOW_STATUS_BAR = 1;
     public static final int WINDOW_NAVIGATION_BAR = 2;
 
+    @IntDef(flag = true, prefix = { "WINDOW_" }, value = {
+        WINDOW_STATUS_BAR,
+        WINDOW_NAVIGATION_BAR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WindowType {}
+
     public static final int WINDOW_STATE_SHOWING = 0;
     public static final int WINDOW_STATE_HIDING = 1;
     public static final int WINDOW_STATE_HIDDEN = 2;
 
+    @IntDef(flag = true, prefix = { "WINDOW_STATE_" }, value = {
+            WINDOW_STATE_SHOWING,
+            WINDOW_STATE_HIDING,
+            WINDOW_STATE_HIDDEN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WindowVisibleState {}
+
     public static final int CAMERA_LAUNCH_SOURCE_WIGGLE = 0;
     public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1;
     public static final int CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER = 2;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 3f6ebc8..9ddf4bd 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -144,6 +144,7 @@
 import android.os.Vibrator;
 import android.os.health.SystemHealthManager;
 import android.os.storage.StorageManager;
+import android.permission.PermissionControllerManager;
 import android.permission.PermissionManager;
 import android.print.IPrintManager;
 import android.print.PrintManager;
@@ -1164,6 +1165,13 @@
                         return new PermissionManager(ctx.getOuterContext());
                     }});
 
+        registerService(Context.PERMISSION_CONTROLLER_SERVICE, PermissionControllerManager.class,
+                new CachedServiceFetcher<PermissionControllerManager>() {
+                    @Override
+                    public PermissionControllerManager createService(ContextImpl ctx) {
+                        return new PermissionControllerManager(ctx.getOuterContext());
+                    }});
+
         registerService(Context.ROLE_SERVICE, RoleManager.class,
                 new CachedServiceFetcher<RoleManager>() {
                     @Override
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 27ae0b0..a929fe0 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -307,13 +307,14 @@
          * @param callback Listener
          * @param handler Thread to call it from. Main thread if null.
          * @param userId Owner of the wallpaper or UserHandle.USER_ALL
+         * @param displayId Caller comes from which display
          */
         public void addOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
-                @Nullable Handler handler, int userId) {
+                @Nullable Handler handler, int userId, int displayId) {
             synchronized (this) {
                 if (!mColorCallbackRegistered) {
                     try {
-                        mService.registerWallpaperColorsCallback(this, userId);
+                        mService.registerWallpaperColorsCallback(this, userId, displayId);
                         mColorCallbackRegistered = true;
                     } catch (RemoteException e) {
                         // Failed, service is gone
@@ -329,16 +330,17 @@
          *
          * @param callback listener
          * @param userId Owner of the wallpaper or UserHandle.USER_ALL
+         * @param displayId Which display is interested
          */
         public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
-                int userId) {
+                int userId, int displayId) {
             synchronized (this) {
                 mColorListeners.removeIf(pair -> pair.first == callback);
 
                 if (mColorListeners.size() == 0 && mColorCallbackRegistered) {
                     mColorCallbackRegistered = false;
                     try {
-                        mService.unregisterWallpaperColorsCallback(this, userId);
+                        mService.unregisterWallpaperColorsCallback(this, userId, displayId);
                     } catch (RemoteException e) {
                         // Failed, service is gone
                         Log.w(TAG, "Can't unregister color updates", e);
@@ -370,14 +372,14 @@
             }
         }
 
-        WallpaperColors getWallpaperColors(int which, int userId) {
+        WallpaperColors getWallpaperColors(int which, int userId, int displayId) {
             if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
                 throw new IllegalArgumentException(
                         "Must request colors for exactly one kind of wallpaper");
             }
 
             try {
-                return mService.getWallpaperColors(which, userId);
+                return mService.getWallpaperColors(which, userId, displayId);
             } catch (RemoteException e) {
                 // Can't get colors, connection lost.
             }
@@ -894,7 +896,7 @@
     @UnsupportedAppUsage
     public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener,
             @NonNull Handler handler, int userId) {
-        sGlobals.addOnColorsChangedListener(listener, handler, userId);
+        sGlobals.addOnColorsChangedListener(listener, handler, userId, mContext.getDisplayId());
     }
 
     /**
@@ -913,7 +915,7 @@
      */
     public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
             int userId) {
-        sGlobals.removeOnColorsChangedListener(callback, userId);
+        sGlobals.removeOnColorsChangedListener(callback, userId, mContext.getDisplayId());
     }
 
     /**
@@ -947,7 +949,7 @@
      */
     @UnsupportedAppUsage
     public @Nullable WallpaperColors getWallpaperColors(int which, int userId) {
-        return sGlobals.getWallpaperColors(which, userId);
+        return sGlobals.getWallpaperColors(which, userId, mContext.getDisplayId());
     }
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f843262..7da67d9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -907,9 +907,8 @@
         = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
 
     /**
-     * A String extra holding the URL-safe base64 encoded SHA-256 or SHA-1 hash (see notes below) of
-     * the file at download location specified in
-     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
+     * A String extra holding the URL-safe base64 encoded SHA-256 hash of the file at download
+     * location specified in {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
      *
      * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM} must be
      * present. The provided checksum must match the checksum of the file at the download
@@ -922,7 +921,8 @@
      * <p><strong>Note:</strong> for devices running {@link android.os.Build.VERSION_CODES#LOLLIPOP}
      * and {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} only SHA-1 hash is supported.
      * Starting from {@link android.os.Build.VERSION_CODES#M}, this parameter accepts SHA-256 in
-     * addition to SHA-1. Support for SHA-1 is likely to be removed in future OS releases.
+     * addition to SHA-1. From {@link android.os.Build.VERSION_CODES#Q}, only SHA-256 hash is
+     * supported.
      */
     public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM
         = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 4d52263..bbae7d3 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -55,4 +55,8 @@
             long sessionThresholdTimeMs, in PendingIntent limitReachedCallbackIntent,
             in PendingIntent sessionEndCallbackIntent, String callingPackage);
     void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
+    void reportUsageStart(in IBinder activity, String token, String callingPackage);
+    void reportPastUsageStart(in IBinder activity, String token, long timeAgoMs,
+            String callingPackage);
+    void reportUsageStop(in IBinder activity, String token, String callingPackage);
 }
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 26beb45..605deac 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
+import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
@@ -579,15 +580,18 @@
     /**
      * @hide
      * Register an app usage limit observer that receives a callback on the provided intent when
-     * the sum of usages of apps in the packages array exceeds the {@code timeLimit} specified. The
-     * observer will automatically be unregistered when the time limit is reached and the intent
-     * is delivered. Registering an {@code observerId} that was already registered will override
-     * the previous one. No more than 1000 unique {@code observerId} may be registered by a single
-     * uid at any one time.
+     * the sum of usages of apps and tokens in the {@code observed} array exceeds the
+     * {@code timeLimit} specified. The structure of a token is a String with the reporting
+     * package's name and a token the reporting app will use, separated by the forward slash
+     * character. Example: com.reporting.package/5OM3*0P4QU3-7OK3N
+     * The observer will automatically be unregistered when the time limit is reached and the
+     * intent is delivered. Registering an {@code observerId} that was already registered will
+     * override the previous one. No more than 1000 unique {@code observerId} may be registered by
+     * a single uid at any one time.
      * @param observerId A unique id associated with the group of apps to be monitored. There can
      *                  be multiple groups with common packages and different time limits.
-     * @param packages The list of packages to observe for foreground activity time. Cannot be null
-     *                 and must include at least one package.
+     * @param observedEntities The list of packages and token to observe for usage time. Cannot be
+     *                         null and must include at least one package or token.
      * @param timeLimit The total time the set of apps can be in the foreground before the
      *                  callbackIntent is delivered. Must be at least one minute.
      * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
@@ -600,11 +604,11 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
-    public void registerAppUsageObserver(int observerId, @NonNull String[] packages, long timeLimit,
-            @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
+    public void registerAppUsageObserver(int observerId, @NonNull String[] observedEntities,
+            long timeLimit, @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
         try {
-            mService.registerAppUsageObserver(observerId, packages, timeUnit.toMillis(timeLimit),
-                    callbackIntent, mContext.getOpPackageName());
+            mService.registerAppUsageObserver(observerId, observedEntities,
+                    timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -631,18 +635,21 @@
 
     /**
      * Register a usage session observer that receives a callback on the provided {@code
-     * limitReachedCallbackIntent} when the sum of usages of apps in the packages array exceeds
-     * the {@code timeLimit} specified within a usage session. After the {@code timeLimit} has
-     * been reached, the usage session observer will receive a callback on the provided {@code
-     * sessionEndCallbackIntent} when the usage session ends. Registering another session
-     * observer against a {@code sessionObserverId} that has already been registered will
-     * override the previous session observer.
+     * limitReachedCallbackIntent} when the sum of usages of apps and tokens in the {@code
+     * observed} array exceeds the {@code timeLimit} specified within a usage session. The
+     * structure of a token is a String with the reporting packages' name and a token the
+     * reporting app will use, separated by the forward slash character.
+     * Example: com.reporting.package/5OM3*0P4QU3-7OK3N
+     * After the {@code timeLimit} has been reached, the usage session observer will receive a
+     * callback on the provided {@code sessionEndCallbackIntent} when the usage session ends.
+     * Registering another session observer against a {@code sessionObserverId} that has already
+     * been registered will override the previous session observer.
      *
      * @param sessionObserverId A unique id associated with the group of apps to be
      *                          monitored. There can be multiple groups with common
      *                          packages and different time limits.
-     * @param packages The list of packages to observe for foreground activity time. Cannot be null
-     *                 and must include at least one package.
+     * @param observedEntities The list of packages and token to observe for usage time. Cannot be
+     *                         null and must include at least one package or token.
      * @param timeLimit The total time the set of apps can be used continuously before the {@code
      *                  limitReachedCallbackIntent} is delivered. Must be at least one minute.
      * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
@@ -668,13 +675,13 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
-    public void registerUsageSessionObserver(int sessionObserverId, @NonNull String[] packages,
-            long timeLimit, @NonNull TimeUnit timeUnit, long sessionThresholdTime,
-            @NonNull TimeUnit sessionThresholdTimeUnit,
+    public void registerUsageSessionObserver(int sessionObserverId,
+            @NonNull String[] observedEntities, long timeLimit, @NonNull TimeUnit timeUnit,
+            long sessionThresholdTime,  @NonNull TimeUnit sessionThresholdTimeUnit,
             @NonNull PendingIntent limitReachedCallbackIntent,
             @Nullable PendingIntent sessionEndCallbackIntent) {
         try {
-            mService.registerUsageSessionObserver(sessionObserverId, packages,
+            mService.registerUsageSessionObserver(sessionObserverId, observedEntities,
                     timeUnit.toMillis(timeLimit),
                     sessionThresholdTimeUnit.toMillis(sessionThresholdTime),
                     limitReachedCallbackIntent, sessionEndCallbackIntent,
@@ -704,6 +711,71 @@
         }
     }
 
+    /**
+     * Report usage associated with a particular {@code token} has started. Tokens are app defined
+     * strings used to represent usage of in-app features. Apps with the {@link
+     * android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time limit observers
+     * to monitor the usage of a token. In app usage can only associated with an {@code activity}
+     * and usage will be considered stopped if the activity stops or crashes.
+     * @see #registerAppUsageObserver
+     * @see #registerUsageSessionObserver
+     *
+     * @param activity The activity {@code token} is associated with.
+     * @param token The token to report usage against.
+     * @hide
+     */
+    @SystemApi
+    public void reportUsageStart(@NonNull Activity activity, @NonNull String token) {
+        try {
+            mService.reportUsageStart(activity.getActivityToken(), token,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Report usage associated with a particular {@code token} had started some amount of time in
+     * the past. Tokens are app defined strings used to represent usage of in-app features. Apps
+     * with the {@link android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time
+     * limit observers to monitor the usage of a token. In app usage can only associated with an
+     * {@code activity} and usage will be considered stopped if the activity stops or crashes.
+     * @see #registerAppUsageObserver
+     * @see #registerUsageSessionObserver
+     *
+     * @param activity The activity {@code token} is associated with.
+     * @param token The token to report usage against.
+     * @param timeAgoMs How long ago the start of usage took place
+     * @hide
+     */
+    @SystemApi
+    public void reportUsageStart(@NonNull Activity activity, @NonNull String token,
+                                 long timeAgoMs) {
+        try {
+            mService.reportPastUsageStart(activity.getActivityToken(), token, timeAgoMs,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Report the usage associated with a particular {@code token} has stopped.
+     *
+     * @param activity The activity {@code token} is associated with.
+     * @param token The token to report usage against.
+     * @hide
+     */
+    @SystemApi
+    public void reportUsageStop(@NonNull Activity activity, @NonNull String token) {
+        try {
+            mService.reportUsageStop(activity.getActivityToken(), token,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     public static String reasonToString(int standbyReason) {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 125c4c6..eb7be6f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3993,6 +3993,14 @@
     public static final String PERMISSION_SERVICE = "permission";
 
     /**
+     * Official published name of the (internal) permission controller service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve an
      * {@link android.app.backup.IBackupManager IBackupManager} for communicating
      * with the backup mechanism.
@@ -4009,9 +4017,9 @@
      * with the rollback manager
      *
      * @see #getSystemService(String)
-     * @hide TODO(ruhler): hidden, @TestApi until we decide on public vs. @SystemApi.
+     * @hide
      */
-    @TestApi
+    @SystemApi
     public static final String ROLLBACK_SERVICE = "rollback";
 
     /**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3492200..ab60b84 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -27,7 +27,6 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -817,6 +816,28 @@
             = "android.intent.action.SHOW_APP_INFO";
 
     /**
+     * Activity Action: Start an activity to show the app's detailed usage information for
+     * permission protected data.
+     *
+     * The Intent contains an extra {@link #EXTRA_PERMISSION_USAGE_PERMISSIONS} that is of
+     * type {@code String[]} and contains the specific permissions to show information for.
+     *
+     * Apps should handle this intent if they want to provide more information about permission
+     * usage to users beyond the information provided in the manifest.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PERMISSION_USAGE_DETAILS =
+            "android.intent.action.PERMISSION_USAGE_DETAILS";
+
+    /**
+     * The name of the extra used to contain the permissions in
+     * {@link #ACTION_PERMISSION_USAGE_DETAILS}.
+     * @see #ACTION_PERMISSION_USAGE_DETAILS
+     */
+    public static final String EXTRA_PERMISSION_USAGE_PERMISSIONS =
+            "android.intent.extra.PERMISSION_USAGE_PERMISSIONS";
+
+    /**
      * Represents a shortcut/live folder icon resource.
      *
      * @see Intent#ACTION_CREATE_SHORTCUT
@@ -2268,9 +2289,9 @@
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
      *
-     * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+     * @hide
      */
-    @TestApi
+    @SystemApi
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_ROLLBACK_EXECUTED =
             "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index a2fd83f..94b7c45 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -24,7 +24,6 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -1428,9 +1427,9 @@
 
         /**
          * Request that rollbacks be enabled for the given upgrade.
-         * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+         * @hide
          */
-        @TestApi
+        @SystemApi
         public void setEnableRollback() {
             installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6110557..2aeb68d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1368,6 +1368,14 @@
      */
     public static final int INSTALL_FAILED_BAD_DEX_METADATA = -117;
 
+    /**
+     * Installation parse return code: this is passed in the
+     * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if there is any signature problem.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FAILED_BAD_SIGNATURE = -118;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DELETE_" }, value = {
             DELETE_KEEP_DATA,
@@ -1931,6 +1939,30 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports uicc-
+     * based NFC card emulation.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC =
+                                                                       "android.hardware.nfc.uicc";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports eSE-
+     * based NFC card emulation.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = "android.hardware.nfc.ese";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The Beam API is enabled on the device.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_BEAM = "android.sofware.nfc.beam";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports any
      * one of the {@link #FEATURE_NFC}, {@link #FEATURE_NFC_HOST_CARD_EMULATION},
      * or {@link #FEATURE_NFC_HOST_CARD_EMULATION_NFCF} features.
@@ -6243,6 +6275,7 @@
             case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED";
             case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA";
             case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
+            case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
             default: return Integer.toString(status);
         }
     }
@@ -6288,6 +6321,7 @@
             case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return PackageInstaller.STATUS_FAILURE_INVALID;
             case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return PackageInstaller.STATUS_FAILURE_INVALID;
             case INSTALL_FAILED_BAD_DEX_METADATA: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_BAD_SIGNATURE: return PackageInstaller.STATUS_FAILURE_INVALID;
             case INSTALL_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE;
             case INSTALL_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
             case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT;
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index 0c05765..2040024 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -16,7 +16,7 @@
 
 package android.content.rollback;
 
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -25,9 +25,9 @@
 /**
  * Information about a rollback available for a particular package.
  *
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
  */
-@TestApi
+@SystemApi
 public final class PackageRollbackInfo implements Parcelable {
     /**
      * The name of a package being rolled back.
diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
index 5fa4e57..66df4fe 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -16,7 +16,7 @@
 
 package android.content.rollback;
 
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -24,9 +24,9 @@
  * Information about a set of packages that can be, or already have been
  * rolled back together.
  *
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
  */
-@TestApi
+@SystemApi
 public final class RollbackInfo implements Parcelable {
 
     /**
@@ -39,6 +39,7 @@
     // TODO: Add a flag to indicate if reboot is required, when rollback of
     // staged installs is supported.
 
+    /** @hide */
     public RollbackInfo(PackageRollbackInfo targetPackage) {
         this.targetPackage = targetPackage;
     }
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 294151a..c1c0bc1 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -18,8 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.content.IntentSender;
 import android.os.RemoteException;
@@ -33,12 +34,10 @@
  * used to initiate rollback of those packages for a limited time period after
  * upgrade.
  *
- * TODO: Require an appropriate permission for apps to use these APIs.
- *
  * @see PackageInstaller.SessionParams#setEnableRollback()
- * @hide TODO: hidden, @TestApi until we decide on public vs. @SystemApi.
+ * @hide
  */
-@TestApi
+@SystemApi
 @SystemService(Context.ROLLBACK_SERVICE)
 public final class RollbackManager {
     private final String mCallerPackageName;
@@ -63,7 +62,10 @@
      * @param packageName name of the package to get the availble RollbackInfo for.
      * @return the rollback available for the package, or null if no rollback
      *         is available for the package.
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
     public @Nullable RollbackInfo getAvailableRollback(@NonNull String packageName) {
         try {
             return mBinder.getAvailableRollback(packageName);
@@ -78,7 +80,10 @@
      * about the rollback available for a particular package.
      *
      * @return the names of packages that are available for rollback.
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
     public @NonNull List<String> getPackagesWithAvailableRollbacks() {
         try {
             return mBinder.getPackagesWithAvailableRollbacks().getList();
@@ -103,7 +108,10 @@
      * rolled back from.
      *
      * @return the recently executed rollbacks
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
     public @NonNull List<RollbackInfo> getRecentlyExecutedRollbacks() {
         try {
             return mBinder.getRecentlyExecutedRollbacks().getList();
@@ -127,7 +135,10 @@
      *
      * @param rollback to execute
      * @param statusReceiver where to deliver the results
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
     public void executeRollback(@NonNull RollbackInfo rollback,
             @NonNull IntentSender statusReceiver) {
         try {
@@ -143,9 +154,10 @@
      * across device reboot, by simulating what happens on reboot without
      * actually rebooting the device.
      *
-     * @hide
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
      */
-    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
     public void reloadPersistedData() {
         try {
             mBinder.reloadPersistedData();
@@ -160,10 +172,10 @@
      * expiring rollback data.
      *
      * @param packageName the name of the package to expire data for.
-     *
-     * @hide
+     * @throws SecurityException if the caller does not have the
+     *            MANAGE_ROLLBACKS permission.
      */
-    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
     public void expireRollbackForPackage(@NonNull String packageName) {
         try {
             mBinder.expireRollbackForPackage(packageName);
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index be054297..7e52ca3 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -19,54 +19,26 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.content.pm.ApplicationInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pair;
 
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Objects;
 
 /** @hide */
 @SystemApi
 @TestApi
 public final class BrightnessConfiguration implements Parcelable {
-    private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
-    private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
-    private static final String TAG_BRIGHTNESS_CORRECTIONS = "brightness-corrections";
-    private static final String TAG_BRIGHTNESS_CORRECTION = "brightness-correction";
-    private static final String ATTR_LUX = "lux";
-    private static final String ATTR_NITS = "nits";
-    private static final String ATTR_DESCRIPTION = "description";
-    private static final String ATTR_PACKAGE_NAME = "package-name";
-    private static final String ATTR_CATEGORY = "category";
-
     private final float[] mLux;
     private final float[] mNits;
-    private final Map<String, BrightnessCorrection> mCorrectionsByPackageName;
-    private final Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
     private final String mDescription;
 
-    private BrightnessConfiguration(float[] lux, float[] nits,
-            Map<String, BrightnessCorrection> correctionsByPackageName,
-            Map<Integer, BrightnessCorrection> correctionsByCategory, String description) {
+    private BrightnessConfiguration(float[] lux, float[] nits, String description) {
         mLux = lux;
         mNits = nits;
-        mCorrectionsByPackageName = correctionsByPackageName;
-        mCorrectionsByCategory = correctionsByCategory;
         mDescription = description;
     }
 
@@ -84,38 +56,6 @@
     }
 
     /**
-     * Returns a brightness correction by app, or null.
-     *
-     * @param packageName
-     *      The app's package name.
-     *
-     * @return The matching brightness correction, or null.
-     *
-     * @hide
-     */
-    @SystemApi
-    @Nullable
-    public BrightnessCorrection getCorrectionByPackageName(String packageName) {
-        return mCorrectionsByPackageName.get(packageName);
-    }
-
-    /**
-     * Returns a brightness correction by app category, or null.
-     *
-     * @param category
-     *      The app category.
-     *
-     * @return The matching brightness correction, or null.
-     *
-     * @hide
-     */
-    @SystemApi
-    @Nullable
-    public BrightnessCorrection getCorrectionByCategory(int category) {
-        return mCorrectionsByCategory.get(category);
-    }
-
-    /**
      * Returns description string.
      * @hide
      */
@@ -127,20 +67,6 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeFloatArray(mLux);
         dest.writeFloatArray(mNits);
-        dest.writeInt(mCorrectionsByPackageName.size());
-        for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
-            final String packageName = entry.getKey();
-            final BrightnessCorrection correction = entry.getValue();
-            dest.writeString(packageName);
-            correction.writeToParcel(dest, flags);
-        }
-        dest.writeInt(mCorrectionsByCategory.size());
-        for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
-            final int category = entry.getKey();
-            final BrightnessCorrection correction = entry.getValue();
-            dest.writeInt(category);
-            correction.writeToParcel(dest, flags);
-        }
         dest.writeString(mDescription);
     }
 
@@ -159,14 +85,7 @@
             }
             sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
         }
-        sb.append("], {");
-        for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
-            sb.append("'" + entry.getKey() + "': " + entry.getValue() + ", ");
-        }
-        for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
-            sb.append(entry.getKey() + ": " + entry.getValue() + ", ");
-        }
-        sb.append("}, '");
+        sb.append("], '");
         if (mDescription != null) {
             sb.append(mDescription);
         }
@@ -179,8 +98,6 @@
         int result = 1;
         result = result * 31 + Arrays.hashCode(mLux);
         result = result * 31 + Arrays.hashCode(mNits);
-        result = result * 31 + mCorrectionsByPackageName.hashCode();
-        result = result * 31 + mCorrectionsByCategory.hashCode();
         if (mDescription != null) {
             result = result * 31 + mDescription.hashCode();
         }
@@ -197,8 +114,6 @@
         }
         final BrightnessConfiguration other = (BrightnessConfiguration) o;
         return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits)
-                && mCorrectionsByPackageName.equals(other.mCorrectionsByPackageName)
-                && mCorrectionsByCategory.equals(other.mCorrectionsByCategory)
                 && Objects.equals(mDescription, other.mDescription);
     }
 
@@ -208,25 +123,7 @@
             float[] lux = in.createFloatArray();
             float[] nits = in.createFloatArray();
             Builder builder = new Builder(lux, nits);
-
-            int n = in.readInt();
-            for (int i = 0; i < n; i++) {
-                final String packageName = in.readString();
-                final BrightnessCorrection correction =
-                        BrightnessCorrection.CREATOR.createFromParcel(in);
-                builder.addCorrectionByPackageName(packageName, correction);
-            }
-
-            n = in.readInt();
-            for (int i = 0; i < n; i++) {
-                final int category = in.readInt();
-                final BrightnessCorrection correction =
-                        BrightnessCorrection.CREATOR.createFromParcel(in);
-                builder.addCorrectionByCategory(category, correction);
-            }
-
-            final String description = in.readString();
-            builder.setDescription(description);
+            builder.setDescription(in.readString());
             return builder.build();
         }
 
@@ -236,146 +133,11 @@
     };
 
     /**
-     * Writes the configuration to an XML serializer.
-     *
-     * @param serializer
-     *      The XML serializer.
-     *
-     * @hide
-     */
-    public void saveToXml(XmlSerializer serializer) throws IOException {
-        serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
-        if (mDescription != null) {
-            serializer.attribute(null, ATTR_DESCRIPTION, mDescription);
-        }
-        for (int i = 0; i < mLux.length; i++) {
-            serializer.startTag(null, TAG_BRIGHTNESS_POINT);
-            serializer.attribute(null, ATTR_LUX, Float.toString(mLux[i]));
-            serializer.attribute(null, ATTR_NITS, Float.toString(mNits[i]));
-            serializer.endTag(null, TAG_BRIGHTNESS_POINT);
-        }
-        serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
-        serializer.startTag(null, TAG_BRIGHTNESS_CORRECTIONS);
-        for (Map.Entry<String, BrightnessCorrection> entry :
-                mCorrectionsByPackageName.entrySet()) {
-            final String packageName = entry.getKey();
-            final BrightnessCorrection correction = entry.getValue();
-            serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
-            serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
-            correction.saveToXml(serializer);
-            serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
-        }
-        for (Map.Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
-            final int category = entry.getKey();
-            final BrightnessCorrection correction = entry.getValue();
-            serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
-            serializer.attribute(null, ATTR_CATEGORY, Integer.toString(category));
-            correction.saveToXml(serializer);
-            serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
-        }
-        serializer.endTag(null, TAG_BRIGHTNESS_CORRECTIONS);
-    }
-
-    /**
-     * Read a configuration from an XML parser.
-     *
-     * @param parser
-     *      The XML parser.
-     *
-     * @throws IOException
-     *      The parser failed to read the XML file.
-     * @throws XmlPullParserException
-     *      The parser failed to parse the XML file.
-     *
-     * @hide
-     */
-    public static BrightnessConfiguration loadFromXml(XmlPullParser parser)
-            throws IOException, XmlPullParserException {
-        String description = null;
-        List<Float> luxList = new ArrayList<>();
-        List<Float> nitsList = new ArrayList<>();
-        Map<String, BrightnessCorrection> correctionsByPackageName = new HashMap<>();
-        Map<Integer, BrightnessCorrection> correctionsByCategory = new HashMap<>();
-        final int configDepth = parser.getDepth();
-        while (XmlUtils.nextElementWithin(parser, configDepth)) {
-            if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
-                description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
-                final int curveDepth = parser.getDepth();
-                while (XmlUtils.nextElementWithin(parser, curveDepth)) {
-                    if (!TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
-                        continue;
-                    }
-                    final float lux = loadFloatFromXml(parser, ATTR_LUX);
-                    final float nits = loadFloatFromXml(parser, ATTR_NITS);
-                    luxList.add(lux);
-                    nitsList.add(nits);
-                }
-            }
-            if (TAG_BRIGHTNESS_CORRECTIONS.equals(parser.getName())) {
-                final int correctionsDepth = parser.getDepth();
-                while (XmlUtils.nextElementWithin(parser, correctionsDepth)) {
-                    if (!TAG_BRIGHTNESS_CORRECTION.equals(parser.getName())) {
-                        continue;
-                    }
-                    final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
-                    final String categoryText = parser.getAttributeValue(null, ATTR_CATEGORY);
-                    BrightnessCorrection correction = BrightnessCorrection.loadFromXml(parser);
-                    if (packageName != null) {
-                        correctionsByPackageName.put(packageName, correction);
-                    } else if (categoryText != null) {
-                        try {
-                            final int category = Integer.parseInt(categoryText);
-                            correctionsByCategory.put(category, correction);
-                        } catch (NullPointerException | NumberFormatException e) {
-                            continue;
-                        }
-                    }
-                }
-            }
-        }
-        final int n = luxList.size();
-        float[] lux = new float[n];
-        float[] nits = new float[n];
-        for (int i = 0; i < n; i++) {
-            lux[i] = luxList.get(i);
-            nits[i] = nitsList.get(i);
-        }
-        final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(lux,
-                nits);
-        builder.setDescription(description);
-        for (Map.Entry<String, BrightnessCorrection> entry : correctionsByPackageName.entrySet()) {
-            final String packageName = entry.getKey();
-            final BrightnessCorrection correction = entry.getValue();
-            builder.addCorrectionByPackageName(packageName, correction);
-        }
-        for (Map.Entry<Integer, BrightnessCorrection> entry : correctionsByCategory.entrySet()) {
-            final int category = entry.getKey();
-            final BrightnessCorrection correction = entry.getValue();
-            builder.addCorrectionByCategory(category, correction);
-        }
-        return builder.build();
-    }
-
-    private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
-        final String string = parser.getAttributeValue(null, attribute);
-        try {
-            return Float.parseFloat(string);
-        } catch (NullPointerException | NumberFormatException e) {
-            return Float.NaN;
-        }
-    }
-
-    /**
      * A builder class for {@link BrightnessConfiguration}s.
      */
     public static class Builder {
-        private static final int MAX_CORRECTIONS_BY_PACKAGE_NAME = 20;
-        private static final int MAX_CORRECTIONS_BY_CATEGORY = 20;
-
         private float[] mCurveLux;
         private float[] mCurveNits;
-        private Map<String, BrightnessCorrection> mCorrectionsByPackageName;
-        private Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
         private String mDescription;
 
         /**
@@ -407,88 +169,6 @@
             checkMonotonic(nits, false /*strictly increasing*/, "nits");
             mCurveLux = lux;
             mCurveNits = nits;
-            mCorrectionsByPackageName = new HashMap<>();
-            mCorrectionsByCategory = new HashMap<>();
-        }
-
-        /**
-         * Returns the maximum number of corrections by package name allowed.
-         *
-         * @return The maximum number of corrections by package name allowed.
-         *
-         * @hide
-         */
-        @SystemApi
-        public int getMaxCorrectionsByPackageName() {
-            return MAX_CORRECTIONS_BY_PACKAGE_NAME;
-        }
-
-        /**
-         * Returns the maximum number of corrections by category allowed.
-         *
-         * @return The maximum number of corrections by category allowed.
-         *
-         * @hide
-         */
-        @SystemApi
-        public int getMaxCorrectionsByCategory() {
-            return MAX_CORRECTIONS_BY_CATEGORY;
-        }
-
-        /**
-         * Add a brightness correction by app package name.
-         * This correction is applied whenever an app with this package name has the top activity
-         * of the focused stack.
-         *
-         * @param packageName
-         *      The app's package name.
-         * @param correction
-         *      The brightness correction.
-         *
-         * @return The builder.
-         *
-         * @throws IllegalArgumentExceptions
-         *      Maximum number of corrections by package name exceeded (see
-         *      {@link #getMaxCorrectionsByPackageName}).
-         *
-         * @hide
-         */
-        @SystemApi
-        public Builder addCorrectionByPackageName(String packageName,
-                BrightnessCorrection correction) {
-            if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) {
-                throw new IllegalArgumentException("Too many corrections by package name");
-            }
-            mCorrectionsByPackageName.put(packageName, correction);
-            return this;
-        }
-
-        /**
-         * Add a brightness correction by app category.
-         * This correction is applied whenever an app with this category has the top activity of
-         * the focused stack, and only if a correction by package name has not been applied.
-         *
-         * @param category
-         *      The {@link android.content.pm.ApplicationInfo#category app category}.
-         * @param correction
-         *      The brightness correction.
-         *
-         * @return The builder.
-         *
-         * @throws IllegalArgumentException
-         *      Maximum number of corrections by category exceeded (see
-         *      {@link #getMaxCorrectionsByCategory}).
-         *
-         * @hide
-         */
-        @SystemApi
-        public Builder addCorrectionByCategory(@ApplicationInfo.Category int category,
-                BrightnessCorrection correction) {
-            if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) {
-                throw new IllegalArgumentException("Too many corrections by category");
-            }
-            mCorrectionsByCategory.put(category, correction);
-            return this;
         }
 
         /**
@@ -511,8 +191,7 @@
             if (mCurveLux == null || mCurveNits == null) {
                 throw new IllegalStateException("A curve must be set!");
             }
-            return new BrightnessConfiguration(mCurveLux, mCurveNits, mCorrectionsByPackageName,
-                    mCorrectionsByCategory, mDescription);
+            return new BrightnessConfiguration(mCurveLux, mCurveNits, mDescription);
         }
 
         private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) {
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
deleted file mode 100644
index c4e0e3b..0000000
--- a/core/java/android/hardware/display/BrightnessCorrection.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.display;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.MathUtils;
-
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-
-/**
- * BrightnessCorrection encapsulates a correction to the brightness, without comitting to the
- * actual correction scheme.
- * It is used by the BrightnessConfiguration, which maps context (e.g. the foreground app's package
- * name and category) to corrections that need to be applied to the brightness within that context.
- * Corrections are currently done by the app that has the top activity of the focused stack, either
- * by its package name, or (if its package name is not mapped to any correction) by its category.
- *
- * @hide
- */
-@SystemApi
-public final class BrightnessCorrection implements Parcelable {
-
-    private static final int SCALE_AND_TRANSLATE_LOG = 1;
-
-    private static final String TAG_SCALE_AND_TRANSLATE_LOG = "scale-and-translate-log";
-
-    private BrightnessCorrectionImplementation mImplementation;
-
-    // Parcelable classes must be final, and protected methods are not allowed in APIs, so we can't
-    // make this class abstract and use composition instead of inheritence.
-    private BrightnessCorrection(BrightnessCorrectionImplementation implementation) {
-        mImplementation = implementation;
-    }
-
-    /**
-     * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be
-     * {@code exp(scale * log(brightness) + translate)}.
-     *
-     * @param scale
-     *      How much to scale the log brightness.
-     * @param translate
-     *      How much to translate the log brightness.
-     *
-     * @return A BrightnessCorrection that given {@code brightness}, corrects it to be
-     * {@code exp(scale * log(brightness) + translate)}.
-     *
-     * @throws IllegalArgumentException
-     *      - scale or translate are NaN.
-     */
-    @NonNull
-    public static BrightnessCorrection createScaleAndTranslateLog(float scale, float translate) {
-        BrightnessCorrectionImplementation implementation =
-                new ScaleAndTranslateLog(scale, translate);
-        return new BrightnessCorrection(implementation);
-    }
-
-    /**
-     * Applies the brightness correction to a given brightness.
-     *
-     * @param brightness
-     *      The brightness.
-     *
-     * @return The corrected brightness.
-     */
-    public float apply(float brightness) {
-        return mImplementation.apply(brightness);
-    }
-
-    /**
-     * Returns a string representation.
-     *
-     * @return A string representation.
-     */
-    public String toString() {
-        return mImplementation.toString();
-    }
-
-    public static final Creator<BrightnessCorrection> CREATOR =
-            new Creator<BrightnessCorrection>() {
-                public BrightnessCorrection createFromParcel(Parcel in) {
-                    final int type = in.readInt();
-                    switch (type) {
-                        case SCALE_AND_TRANSLATE_LOG:
-                            return ScaleAndTranslateLog.readFromParcel(in);
-                    }
-                    return null;
-                }
-
-                public BrightnessCorrection[] newArray(int size) {
-                    return new BrightnessCorrection[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        mImplementation.writeToParcel(dest);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Writes the correction to an XML serializer.
-     *
-     * @param serializer
-     *      The XML serializer.
-     *
-     * @hide
-     */
-    public void saveToXml(XmlSerializer serializer) throws IOException {
-        mImplementation.saveToXml(serializer);
-    }
-
-    /**
-     * Read a correction from an XML parser.
-     *
-     * @param parser
-     *      The XML parser.
-     *
-     * @throws IOException
-     *      The parser failed to read the XML file.
-     * @throws XmlPullParserException
-     *      The parser failed to parse the XML file.
-     *
-     * @hide
-     */
-    public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
-            XmlPullParserException {
-        final int depth = parser.getDepth();
-        while (XmlUtils.nextElementWithin(parser, depth)) {
-            if (TAG_SCALE_AND_TRANSLATE_LOG.equals(parser.getName())) {
-                return ScaleAndTranslateLog.loadFromXml(parser);
-            }
-        }
-        return null;
-    }
-
-    private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
-        final String string = parser.getAttributeValue(null, attribute);
-        try {
-            return Float.parseFloat(string);
-        } catch (NullPointerException | NumberFormatException e) {
-            return Float.NaN;
-        }
-    }
-
-    private interface BrightnessCorrectionImplementation {
-        float apply(float brightness);
-        String toString();
-        void writeToParcel(Parcel dest);
-        void saveToXml(XmlSerializer serializer) throws IOException;
-        // Package-private static methods:
-        // static BrightnessCorrection readFromParcel(Parcel in);
-        // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
-        //      XmlPullParserException;
-    }
-
-    /**
-     * A BrightnessCorrection that given {@code brightness}, corrects it to be
-     * {@code exp(scale * log(brightness) + translate)}.
-     */
-    private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation {
-        private static final float MIN_SCALE = 0.5f;
-        private static final float MAX_SCALE = 2.0f;
-        private static final float MIN_TRANSLATE = -0.6f;
-        private static final float MAX_TRANSLATE = 0.7f;
-
-        private static final String ATTR_SCALE = "scale";
-        private static final String ATTR_TRANSLATE = "translate";
-
-        private final float mScale;
-        private final float mTranslate;
-
-        ScaleAndTranslateLog(float scale, float translate) {
-            if (Float.isNaN(scale) || Float.isNaN(translate)) {
-                throw new IllegalArgumentException("scale and translate must be numbers");
-            }
-            mScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
-            mTranslate = MathUtils.constrain(translate, MIN_TRANSLATE, MAX_TRANSLATE);
-        }
-
-        @Override
-        public float apply(float brightness) {
-            return MathUtils.exp(mScale * MathUtils.log(brightness) + mTranslate);
-        }
-
-        @Override
-        public String toString() {
-            return "ScaleAndTranslateLog(" + mScale + ", " + mTranslate + ")";
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest) {
-            dest.writeInt(SCALE_AND_TRANSLATE_LOG);
-            dest.writeFloat(mScale);
-            dest.writeFloat(mTranslate);
-        }
-
-        @Override
-        public void saveToXml(XmlSerializer serializer) throws IOException {
-            serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
-            serializer.attribute(null, ATTR_SCALE, Float.toString(mScale));
-            serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate));
-            serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
-        }
-
-        static BrightnessCorrection readFromParcel(Parcel in) {
-            float scale = in.readFloat();
-            float translate = in.readFloat();
-            return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
-        }
-
-        static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
-                XmlPullParserException {
-            final float scale = loadFloatFromXml(parser, ATTR_SCALE);
-            final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE);
-            return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
-        }
-    }
-}
diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
index 9bebbd2..d382eb9 100644
--- a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
+++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
@@ -17,6 +17,7 @@
 
 import android.annotation.Nullable;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.RemoteException;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -52,7 +53,7 @@
     @VisibleForTesting(visibility = Visibility.PACKAGE)
     public HdmiAudioSystemClient(IHdmiControlService service, @Nullable Handler handler) {
         super(service);
-        mHandler = handler == null ? new Handler() : handler;
+        mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler;
     }
 
     /** @hide */
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 72a6ffe..b520d2c 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -388,6 +388,19 @@
     }
 
     /**
+     * Gets whether the system is in system audio mode.
+     *
+     * @hide
+     */
+    public boolean getSystemAudioMode() {
+        try {
+            return mService.getSystemAudioMode();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Listener used to get hotplug event from HDMI port.
      */
     public interface HotplugEventListener {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 61d5a91..23e4ec0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -16,6 +16,7 @@
 package android.net;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
@@ -909,6 +910,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public NetworkInfo getActiveNetworkInfo() {
         try {
             return mService.getActiveNetworkInfo();
@@ -928,6 +930,7 @@
      *        {@code null} if no default network is currently active
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public Network getActiveNetwork() {
         try {
             return mService.getActiveNetwork();
@@ -949,6 +952,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @Nullable
     public Network getActiveNetworkForUid(int uid) {
         return getActiveNetworkForUid(uid, false);
     }
@@ -1074,6 +1078,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @Nullable
     public NetworkInfo getNetworkInfo(int networkType) {
         try {
             return mService.getNetworkInfo(networkType);
@@ -1095,7 +1100,8 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public NetworkInfo getNetworkInfo(Network network) {
+    @Nullable
+    public NetworkInfo getNetworkInfo(@Nullable Network network) {
         return getNetworkInfoForUid(network, Process.myUid(), false);
     }
 
@@ -1121,6 +1127,7 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @NonNull
     public NetworkInfo[] getAllNetworkInfo() {
         try {
             return mService.getAllNetworkInfo();
@@ -1156,6 +1163,7 @@
      * @return an array of {@link Network} objects.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    @NonNull
     public Network[] getAllNetworks() {
         try {
             return mService.getAllNetworks();
@@ -1230,7 +1238,8 @@
      * @return The {@link LinkProperties} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public LinkProperties getLinkProperties(Network network) {
+    @Nullable
+    public LinkProperties getLinkProperties(@Nullable Network network) {
         try {
             return mService.getLinkProperties(network);
         } catch (RemoteException e) {
@@ -1246,7 +1255,8 @@
      * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public NetworkCapabilities getNetworkCapabilities(Network network) {
+    @Nullable
+    public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
         try {
             return mService.getNetworkCapabilities(network);
         } catch (RemoteException e) {
@@ -2000,7 +2010,7 @@
      *
      * @param l Previously registered listener.
      */
-    public void removeDefaultNetworkActiveListener(OnNetworkActiveListener l) {
+    public void removeDefaultNetworkActiveListener(@NonNull OnNetworkActiveListener l) {
         INetworkActivityListener rl = mNetworkActivityListeners.get(l);
         Preconditions.checkArgument(rl != null, "Listener was not registered.");
         try {
@@ -2528,7 +2538,7 @@
      *             working and non-working connectivity.
      */
     @Deprecated
-    public void reportBadNetwork(Network network) {
+    public void reportBadNetwork(@Nullable Network network) {
         printStackTrace();
         try {
             // One of these will be ignored because it matches system's current state.
@@ -2551,7 +2561,7 @@
      * @param hasConnectivity {@code true} if the application was able to successfully access the
      *                        Internet using {@code network} or {@code false} if not.
      */
-    public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
+    public void reportNetworkConnectivity(@Nullable Network network, boolean hasConnectivity) {
         printStackTrace();
         try {
             mService.reportNetworkConnectivity(network, hasConnectivity);
@@ -2625,6 +2635,7 @@
      * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no
      *        HTTP proxy is active.
      */
+    @Nullable
     public ProxyInfo getDefaultProxy() {
         return getProxyForNetwork(getBoundNetworkForProcess());
     }
@@ -3156,8 +3167,9 @@
      *
      * @hide
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            int timeoutMs, int legacyType, Handler handler) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType,
+            @NonNull Handler handler) {
         CallbackHandler cbHandler = new CallbackHandler(handler);
         NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler);
@@ -3194,7 +3206,8 @@
      * @throws IllegalArgumentException if {@code request} specifies any mutable
      *         {@code NetworkCapabilities}.
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback) {
         requestNetwork(request, networkCallback, getDefaultHandler());
     }
 
@@ -3229,8 +3242,8 @@
      * @throws IllegalArgumentException if {@code request} specifies any mutable
      *         {@code NetworkCapabilities}.
      */
-    public void requestNetwork(
-            NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
         requestNetwork(request, networkCallback, 0, legacyType, cbHandler);
@@ -3264,8 +3277,8 @@
      *                  before {@link NetworkCallback#onUnavailable()} is called. The timeout must
      *                  be a positive value (i.e. >0).
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            int timeoutMs) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, int timeoutMs) {
         checkTimeout(timeoutMs);
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
@@ -3298,8 +3311,8 @@
      * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
      *                  before {@link NetworkCallback#onUnavailable} is called.
      */
-    public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
-            Handler handler, int timeoutMs) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
         checkTimeout(timeoutMs);
         int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
@@ -3371,7 +3384,8 @@
      *         {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
      *         {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}.
      */
-    public void requestNetwork(NetworkRequest request, PendingIntent operation) {
+    public void requestNetwork(@NonNull NetworkRequest request,
+            @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
@@ -3395,7 +3409,7 @@
      *                  {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} with the
      *                  corresponding NetworkRequest you'd like to remove. Cannot be null.
      */
-    public void releaseNetworkRequest(PendingIntent operation) {
+    public void releaseNetworkRequest(@NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
@@ -3428,7 +3442,8 @@
      *                        The callback is invoked on the default internal Handler.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback) {
         registerNetworkCallback(request, networkCallback, getDefaultHandler());
     }
 
@@ -3443,8 +3458,8 @@
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(
-            NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
         CallbackHandler cbHandler = new CallbackHandler(handler);
         NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, 0, LISTEN, TYPE_NONE, cbHandler);
@@ -3480,7 +3495,8 @@
      *                  comes from {@link PendingIntent#getBroadcast}. Cannot be null.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerNetworkCallback(NetworkRequest request, PendingIntent operation) {
+    public void registerNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
         try {
@@ -3502,7 +3518,7 @@
      *                        The callback is invoked on the default internal Handler.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerDefaultNetworkCallback(NetworkCallback networkCallback) {
+    public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback) {
         registerDefaultNetworkCallback(networkCallback, getDefaultHandler());
     }
 
@@ -3516,7 +3532,8 @@
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public void registerDefaultNetworkCallback(NetworkCallback networkCallback, Handler handler) {
+    public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
+            @NonNull Handler handler) {
         // This works because if the NetworkCapabilities are null,
         // ConnectivityService takes them from the default request.
         //
@@ -3541,7 +3558,7 @@
      * @param network {@link Network} specifying which network you're interested.
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
      */
-    public boolean requestBandwidthUpdate(Network network) {
+    public boolean requestBandwidthUpdate(@NonNull Network network) {
         try {
             return mService.requestBandwidthUpdate(network);
         } catch (RemoteException e) {
@@ -3562,7 +3579,7 @@
      *
      * @param networkCallback The {@link NetworkCallback} used when making the request.
      */
-    public void unregisterNetworkCallback(NetworkCallback networkCallback) {
+    public void unregisterNetworkCallback(@NonNull NetworkCallback networkCallback) {
         printStackTrace();
         checkCallbackNotNull(networkCallback);
         final List<NetworkRequest> reqs = new ArrayList<>();
@@ -3601,7 +3618,7 @@
      *                  {@link #registerNetworkCallback(NetworkRequest, android.app.PendingIntent)}.
      *                  Cannot be null.
      */
-    public void unregisterNetworkCallback(PendingIntent operation) {
+    public void unregisterNetworkCallback(@NonNull PendingIntent operation) {
         checkPendingIntentNotNull(operation);
         releaseNetworkRequest(operation);
     }
@@ -3723,7 +3740,7 @@
      * @return a bitwise OR of zero or more of the  {@code MULTIPATH_PREFERENCE_*} constants.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public @MultipathPreference int getMultipathPreference(Network network) {
+    public @MultipathPreference int getMultipathPreference(@Nullable Network network) {
         try {
             return mService.getMultipathPreference(network);
         } catch (RemoteException e) {
@@ -3761,7 +3778,7 @@
      *                the current binding.
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
      */
-    public boolean bindProcessToNetwork(Network network) {
+    public boolean bindProcessToNetwork(@Nullable Network network) {
         // Forcing callers to call through non-static function ensures ConnectivityManager
         // instantiated.
         return setProcessDefaultNetwork(network);
@@ -3789,7 +3806,7 @@
      *             is a direct replacement.
      */
     @Deprecated
-    public static boolean setProcessDefaultNetwork(Network network) {
+    public static boolean setProcessDefaultNetwork(@Nullable Network network) {
         int netId = (network == null) ? NETID_UNSET : network.netId;
         if (netId == NetworkUtils.getBoundNetworkForProcess()) {
             return true;
@@ -3820,6 +3837,7 @@
      *
      * @return {@code Network} to which this process is bound, or {@code null}.
      */
+    @Nullable
     public Network getBoundNetworkForProcess() {
         // Forcing callers to call thru non-static function ensures ConnectivityManager
         // instantiated.
@@ -3836,6 +3854,7 @@
      *             {@code getBoundNetworkForProcess} is a direct replacement.
      */
     @Deprecated
+    @Nullable
     public static Network getProcessDefaultNetwork() {
         int netId = NetworkUtils.getBoundNetworkForProcess();
         if (netId == NETID_UNSET) return null;
@@ -3962,6 +3981,7 @@
      *
      * @return Hash of network watchlist config file. Null if config does not exist.
      */
+    @Nullable
     public byte[] getNetworkWatchlistConfigHash() {
         try {
             return mService.getNetworkWatchlistConfigHash();
@@ -3983,8 +4003,8 @@
      * (e.g., if it is associated with the calling VPN app's tunnel) or
      * {@link android.os.Process#INVALID_UID} if the connection is not found.
      */
-    public int getConnectionOwnerUid(int protocol, InetSocketAddress local,
-                                     InetSocketAddress remote) {
+    public int getConnectionOwnerUid(int protocol, @NonNull InetSocketAddress local,
+            @NonNull InetSocketAddress remote) {
         ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote);
         try {
             return mService.getConnectionOwnerUid(connectionInfo);
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index a00b9a3..9cf582b 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import static android.os.Process.CLAT_UID;
+
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -828,13 +830,15 @@
      *
      * <p>For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4
      * packet on the stacked interface, and once as translated to an IPv6 packet on the
-     * base interface. For correct stats accounting on the base interface, every 464xlat
-     * packet needs to be subtracted from the root UID on the base interface both for tx
-     * and rx traffic (http://b/12249687, http:/b/33681750).
+     * base interface. For correct stats accounting on the base interface, if using xt_qtaguid,
+     * every rx 464xlat packet needs to be subtracted from the root UID on the base interface
+     * (http://b/12249687, http:/b/33681750), and every tx 464xlat packet which was counted onto
+     * clat uid should be ignored.
      *
      * As for eBPF, the per uid stats is collected by different hook, the rx packets on base
-     * interface will not be counted. Thus, the adjustment on root uid is only needed in tx
-     * direction.
+     * interface will not be counted. Thus, the adjustment on root uid is not needed. However, the
+     * tx traffic counted in the same way xt_qtaguid does, so the traffic on clat uid still
+     * needs to be ignored.
      *
      * <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
      * {@code ConcurrentHashMap}
@@ -862,17 +866,14 @@
             if (baseIface == null) {
                 continue;
             }
-            // Subtract any 464lat traffic seen for the root UID on the current base interface.
-            // However, for eBPF, the per uid stats is collected by different hook, the rx packets
-            // on base interface will not be counted. Thus, the adjustment on root uid is only
-            // needed in tx direction.
+            // Subtract xt_qtaguid 464lat rx traffic seen for the root UID on the current base
+            // interface. As for eBPF, the per uid stats is collected by different hook, the rx
+            // packets on base interface will not be counted.
             adjust.iface = baseIface;
             if (!useBpfStats) {
                 adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
                 adjust.rxPackets = -entry.rxPackets;
             }
-            adjust.txBytes = -(entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
-            adjust.txPackets = -entry.txPackets;
             adjustments.combineValues(adjust);
 
             // For 464xlat traffic, per uid stats only counts the bytes of the native IPv4 packet
@@ -884,6 +885,9 @@
             stackedTraffic.setValues(i, entry);
         }
 
+        // Traffic on clat uid is v6 tx traffic that is already counted with app uid on the stacked
+        // v4 interface, so it needs to be removed to avoid double-counting.
+        baseTraffic.removeUids(new int[] {CLAT_UID});
         baseTraffic.combineAllValues(adjustments);
     }
 
diff --git a/core/java/android/hardware/display/BrightnessCorrection.aidl b/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl
similarity index 63%
copy from core/java/android/hardware/display/BrightnessCorrection.aidl
copy to core/java/android/net/dhcp/DhcpServingParamsParcel.aidl
index 3abe29c..7b8b9ee 100644
--- a/core/java/android/hardware/display/BrightnessCorrection.aidl
+++ b/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -1,11 +1,12 @@
-/*
+/**
+ *
  * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +15,16 @@
  * limitations under the License.
  */
 
-package android.hardware.display;
+package android.net.dhcp;
 
-parcelable BrightnessCorrection;
+parcelable DhcpServingParamsParcel {
+    int serverAddr;
+    int serverAddrPrefixLength;
+    int[] defaultRouters;
+    int[] dnsServers;
+    int[] excludedAddrs;
+    long dhcpLeaseTimeSecs;
+    int linkMtu;
+    boolean metered;
+}
+
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index d1b132c..dd2c0d4 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -31,6 +31,8 @@
     boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
     boolean setDefaultForNextTap(int userHandle, in ComponentName service);
     boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
+    boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
+    boolean unsetOffHostForService(int userHandle, in ComponentName service);
     AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category);
     boolean removeAidGroupForService(int userHandle, in ComponentName service, String category);
     List<ApduServiceInfo> getServices(int userHandle, in String category);
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 21fed48..e55e036 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -42,7 +43,9 @@
 import android.util.Log;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 
 /**
  * Represents the local NFC adapter.
@@ -322,6 +325,7 @@
     // Guarded by NfcAdapter.class
     static boolean sIsInitialized = false;
     static boolean sHasNfcFeature;
+    static boolean sHasBeamFeature;
 
     // Final after first constructor, except for
     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
@@ -369,7 +373,9 @@
      * A callback to be invoked when the system successfully delivers your {@link NdefMessage}
      * to another device.
      * @see #setOnNdefPushCompleteCallback
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public interface OnNdefPushCompleteCallback {
         /**
          * Called on successful NDEF push.
@@ -392,7 +398,9 @@
      * content currently visible to the user. Alternatively, you can call {@link
      * #setNdefPushMessage setNdefPushMessage()} if the {@link NdefMessage} always contains the
      * same data.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public interface CreateNdefMessageCallback {
         /**
          * Called to provide a {@link NdefMessage} to push.
@@ -418,7 +426,10 @@
     }
 
 
-    // TODO javadoc
+     /**
+     * @deprecated this feature is deprecated.
+     */
+    @java.lang.Deprecated
     public interface CreateBeamUrisCallback {
         public Uri[] createBeamUris(NfcEvent event);
     }
@@ -446,6 +457,25 @@
         public boolean onUnlockAttempted(Tag tag);
     }
 
+    /**
+     * Helper to check if this device has FEATURE_NFC_BEAM, but without using
+     * a context.
+     * Equivalent to
+     * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_BEAM)
+     */
+    private static boolean hasBeamFeature() {
+        IPackageManager pm = ActivityThread.getPackageManager();
+        if (pm == null) {
+            Log.e(TAG, "Cannot get package manager, assuming no Android Beam feature");
+            return false;
+        }
+        try {
+            return pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM, 0);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Package manager query failed, assuming no Android Beam feature", e);
+            return false;
+        }
+    }
 
     /**
      * Helper to check if this device has FEATURE_NFC, but without using
@@ -488,6 +518,35 @@
     }
 
     /**
+     * Return list of Secure Elements which support off host card emulation.
+     *
+     * @return List<String> containing secure elements on the device which supports
+     *                      off host card emulation. eSE for Embedded secure element,
+     *                      SIM for UICC and so on.
+     */
+    public @NonNull List<String> getSupportedOffHostSecureElements() {
+        List<String> offHostSE = new ArrayList<String>();
+        IPackageManager pm = ActivityThread.getPackageManager();
+        if (pm == null) {
+            Log.e(TAG, "Cannot get package manager, assuming no off-host CE feature");
+            return offHostSE;
+        }
+        try {
+            if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC, 0)) {
+                offHostSE.add("SIM");
+            }
+            if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE, 0)) {
+                offHostSE.add("eSE");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Package manager query failed, assuming no off-host CE feature", e);
+            offHostSE.clear();
+            return offHostSE;
+        }
+        return offHostSE;
+    }
+
+    /**
      * Returns the NfcAdapter for application context,
      * or throws if NFC is not available.
      * @hide
@@ -496,6 +555,7 @@
     public static synchronized NfcAdapter getNfcAdapter(Context context) {
         if (!sIsInitialized) {
             sHasNfcFeature = hasNfcFeature();
+            sHasBeamFeature = hasBeamFeature();
             boolean hasHceFeature = hasNfcHceFeature();
             /* is this device meant to have NFC */
             if (!sHasNfcFeature && !hasHceFeature) {
@@ -921,12 +981,17 @@
      * @param uris an array of Uri(s) to push over Android Beam
      * @param activity activity for which the Uri(s) will be pushed
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public void setBeamPushUris(Uri[] uris, Activity activity) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         if (activity == null) {
             throw new NullPointerException("activity cannot be null");
@@ -1003,12 +1068,17 @@
      * @param callback callback, or null to disable
      * @param activity activity for which the Uri(s) will be pushed
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         if (activity == null) {
             throw new NullPointerException("activity cannot be null");
@@ -1087,13 +1157,18 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public void setNdefPushMessage(NdefMessage message, Activity activity,
             Activity ... activities) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         int targetSdkVersion = getSdkVersion();
         try {
@@ -1200,13 +1275,18 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
             Activity ... activities) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         int targetSdkVersion = getSdkVersion();
         try {
@@ -1281,13 +1361,18 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
             Activity activity, Activity ... activities) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         int targetSdkVersion = getSdkVersion();
         try {
@@ -1492,12 +1577,17 @@
      * @param activity the current foreground Activity that has registered data to share
      * @return whether the Beam animation was successfully invoked
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
     public boolean invokeBeam(Activity activity) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return false;
+            }
         }
         if (activity == null) {
             throw new NullPointerException("activity may not be null.");
@@ -1561,6 +1651,9 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         if (activity == null || message == null) {
             throw new NullPointerException();
@@ -1595,6 +1688,9 @@
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return;
+            }
         }
         if (activity == null) {
             throw new NullPointerException();
@@ -1668,12 +1764,18 @@
      * @see android.provider.Settings#ACTION_NFCSHARING_SETTINGS
      * @return true if NDEF Push feature is enabled
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+     * @deprecated this feature is deprecated.
      */
+    @java.lang.Deprecated
+
     public boolean isNdefPushEnabled() {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
+            if (!sHasBeamFeature) {
+                return false;
+            }
         }
         try {
             return sService.isNdefPushEnabled();
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index e8d801c..911ec84 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -18,11 +18,10 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.TypedArray;
@@ -30,7 +29,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.ResultReceiver;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
@@ -69,6 +67,18 @@
     final boolean mOnHost;
 
     /**
+     * Offhost reader name.
+     * eg: SIM, eSE etc
+     */
+    String mOffHostName;
+
+    /**
+     * Offhost reader name from manifest file.
+     * Used for unsetOffHostSecureElement()
+     */
+    final String mStaticOffHostName;
+
+    /**
      * Mapping from category to static AID group
      */
     @UnsupportedAppUsage
@@ -104,15 +114,17 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
+    public ApduServiceInfo(ResolveInfo info, String description,
             ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
             boolean requiresUnlock, int bannerResource, int uid,
-            String settingsActivityName) {
+            String settingsActivityName, String offHost, String staticOffHost) {
         this.mService = info;
         this.mDescription = description;
         this.mStaticAidGroups = new HashMap<String, AidGroup>();
         this.mDynamicAidGroups = new HashMap<String, AidGroup>();
-        this.mOnHost = onHost;
+        this.mOffHostName = offHost;
+        this.mStaticOffHostName = staticOffHost;
+        this.mOnHost = (offHost == null);
         this.mRequiresDeviceUnlock = requiresUnlock;
         for (AidGroup aidGroup : staticAidGroups) {
             this.mStaticAidGroups.put(aidGroup.category, aidGroup);
@@ -174,6 +186,8 @@
                         com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1);
                 mSettingsActivityName = sa.getString(
                         com.android.internal.R.styleable.HostApduService_settingsActivity);
+                mOffHostName = null;
+                mStaticOffHostName = mOffHostName;
                 sa.recycle();
             } else {
                 TypedArray sa = res.obtainAttributes(attrs,
@@ -186,6 +200,16 @@
                         com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1);
                 mSettingsActivityName = sa.getString(
                         com.android.internal.R.styleable.HostApduService_settingsActivity);
+                mOffHostName = sa.getString(
+                        com.android.internal.R.styleable.OffHostApduService_secureElementName);
+                if (mOffHostName != null) {
+                    if (mOffHostName.equals("eSE")) {
+                        mOffHostName = "eSE1";
+                    } else if (mOffHostName.equals("SIM")) {
+                        mOffHostName = "SIM1";
+                    }
+                }
+                mStaticOffHostName = mOffHostName;
                 sa.recycle();
             }
 
@@ -289,6 +313,10 @@
                 mService.serviceInfo.name);
     }
 
+    public String getOffHostSecureElement() {
+        return mOffHostName;
+    }
+
     /**
      * Returns a consolidated list of AIDs from the AID groups
      * registered by this service. Note that if a service has both
@@ -404,6 +432,20 @@
         mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup);
     }
 
+    @UnsupportedAppUsage
+    public void setOffHostSecureElement(String offHost) {
+        mOffHostName = offHost;
+    }
+
+    /**
+     * Resets the off host Secure Element to statically defined
+     * by the service in the manifest file.
+     */
+    @UnsupportedAppUsage
+    public void unsetOffHostSecureElement() {
+        mOffHostName = mStaticOffHostName;
+    }
+
     public CharSequence loadLabel(PackageManager pm) {
         return mService.loadLabel(pm);
     }
@@ -481,6 +523,8 @@
         mService.writeToParcel(dest, flags);
         dest.writeString(mDescription);
         dest.writeInt(mOnHost ? 1 : 0);
+        dest.writeString(mOffHostName);
+        dest.writeString(mStaticOffHostName);
         dest.writeInt(mStaticAidGroups.size());
         if (mStaticAidGroups.size() > 0) {
             dest.writeTypedList(new ArrayList<AidGroup>(mStaticAidGroups.values()));
@@ -503,6 +547,8 @@
             ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
             String description = source.readString();
             boolean onHost = source.readInt() != 0;
+            String offHostName = source.readString();
+            String staticOffHostName = source.readString();
             ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>();
             int numStaticGroups = source.readInt();
             if (numStaticGroups > 0) {
@@ -517,9 +563,9 @@
             int bannerResource = source.readInt();
             int uid = source.readInt();
             String settingsActivityName = source.readString();
-            return new ApduServiceInfo(info, onHost, description, staticAidGroups,
+            return new ApduServiceInfo(info, description, staticAidGroups,
                     dynamicAidGroups, requiresUnlock, bannerResource, uid,
-                    settingsActivityName);
+                    settingsActivityName, offHostName, staticOffHostName);
         }
 
         @Override
@@ -531,6 +577,14 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("    " + getComponent() +
                 " (Description: " + getDescription() + ")");
+        if (mOnHost) {
+            pw.println("    On Host Service");
+        } else {
+            pw.println("    Off-host Service");
+            pw.println("        " + "Current off-host SE" + mOffHostName
+                    + " static off-host: " + mOffHostName);
+        }
+        pw.println("    Static off-host Secure Element:");
         pw.println("    Static AID groups:");
         for (AidGroup group : mStaticAidGroups.values()) {
             pw.println("        Category: " + group.category);
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 15d02f2..01932ab 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -27,7 +27,6 @@
 import android.nfc.INfcCardEmulation;
 import android.nfc.NfcAdapter;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Log;
@@ -345,6 +344,108 @@
     }
 
     /**
+     * Unsets the off-host Secure Element for the given service.
+     *
+     * <p>Note that this will only remove Secure Element that was dynamically
+     * set using the {@link #setOffHostForService(ComponentName, String)}
+     * and resets it to a value that was statically assigned using manifest.
+     *
+     * <p>Note that you can only unset off-host SE for a service that
+     * is running under the same UID as the caller of this API. Typically
+     * this means you need to call this from the same
+     * package as the service itself, though UIDs can also
+     * be shared between packages using shared UIDs.
+     *
+     * @param service The component name of the service
+     * @return whether the registration was successful.
+     */
+    public boolean unsetOffHostForService(ComponentName service) {
+        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+        if (adapter == null) {
+            return false;
+        }
+
+        try {
+            return sService.unsetOffHostForService(mContext.getUserId(), service);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.unsetOffHostForService(mContext.getUserId(), service);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Sets the off-host Secure Element for the given service.
+     *
+     * <p>If off-host SE was initially set (either statically
+     * through the manifest, or dynamically by using this API),
+     * it will be replaced with this one. All AIDs registered by
+     * this service will be re-routed to this Secure Element if
+     * successful.
+     *
+     * <p>Note that you can only set off-host SE for a service that
+     * is running under the same UID as the caller of this API. Typically
+     * this means you need to call this from the same
+     * package as the service itself, though UIDs can also
+     * be shared between packages using shared UIDs.
+     *
+     * <p>Registeration will be successful only if the Secure Element
+     * exists on the device.
+     *
+     * @param service The component name of the service
+     * @param offHostSecureElement Secure Element to register the AID to
+     * @return whether the registration was successful.
+     */
+    public boolean setOffHostForService(ComponentName service, String offHostSecureElement) {
+        boolean validSecureElement = false;
+
+        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+        if (adapter == null || offHostSecureElement == null) {
+            return false;
+        }
+
+        List<String> validSE = adapter.getSupportedOffHostSecureElements();
+        if ((offHostSecureElement.startsWith("eSE") && !validSE.contains("eSE"))
+                || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))) {
+            return false;
+        }
+
+        if (offHostSecureElement.equals("eSE")) {
+            offHostSecureElement = "eSE1";
+        } else if (offHostSecureElement.equals("SIM")) {
+            offHostSecureElement = "SIM1";
+        }
+
+        try {
+            return sService.setOffHostForService(mContext.getUserId(), service,
+                offHostSecureElement);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.setOffHostForService(mContext.getUserId(), service,
+                        offHostSecureElement);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
      * Retrieves the currently registered AIDs for the specified
      * category for a service.
      *
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
new file mode 100644
index 0000000..40cbaf7
--- /dev/null
+++ b/core/java/android/os/AppZygote.java
@@ -0,0 +1,119 @@
+/*
+ * 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.content.pm.ApplicationInfo;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * AppZygote is responsible for interfacing with an application-specific zygote.
+ *
+ * Application zygotes can pre-load app-specific code and data, and this interface can
+ * be used to spawn isolated services from such an application zygote.
+ *
+ * Note that we'll have only one instance of this per application / uid combination.
+ *
+ * @hide
+ */
+public class AppZygote {
+    private static final String LOG_TAG = "AppZygote";
+
+    private final int mZygoteUid;
+
+    private final Object mLock = new Object();
+
+    /**
+     * Instance that maintains the socket connection to the zygote. This is {@code null} if the
+     * zygote is not running or is not connected.
+     */
+    @GuardedBy("mLock")
+    private ChildZygoteProcess mZygote;
+
+    private final ApplicationInfo mAppInfo;
+
+    public AppZygote(ApplicationInfo appInfo, int zygoteUid) {
+        mAppInfo = appInfo;
+        mZygoteUid = zygoteUid;
+    }
+
+    /**
+     * Returns the zygote process associated with this app zygote.
+     * Creates the process if it's not already running.
+     */
+    public ChildZygoteProcess getProcess() {
+        synchronized (mLock) {
+            if (mZygote != null) return mZygote;
+
+            connectToZygoteIfNeededLocked();
+            return mZygote;
+        }
+    }
+
+    /**
+     * Stops the Zygote and kills the zygote process.
+     */
+    public void stopZygote() {
+        synchronized (mLock) {
+            stopZygoteLocked();
+        }
+    }
+
+    public ApplicationInfo getAppInfo() {
+        return mAppInfo;
+    }
+
+    @GuardedBy("mLock")
+    private void stopZygoteLocked() {
+        if (mZygote != null) {
+            // Close the connection and kill the zygote process. This will not cause
+            // child processes to be killed by itself.
+            mZygote.close();
+            Process.killProcess(mZygote.getPid());
+            mZygote = null;
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void connectToZygoteIfNeededLocked() {
+        String abi = mAppInfo.primaryCpuAbi != null ? mAppInfo.primaryCpuAbi :
+                Build.SUPPORTED_ABIS[0];
+        try {
+            mZygote = Process.zygoteProcess.startChildZygote(
+                    "com.android.internal.os.AppZygoteInit",
+                    mAppInfo.processName + "_zygote",
+                    mZygoteUid,
+                    mZygoteUid,
+                    null,  // gids
+                    0,  // runtimeFlags
+                    "app_zygote",  // seInfo
+                    abi,  // abi
+                    abi, // acceptedAbiList
+                    null);  // instructionSet
+
+            ZygoteProcess.waitForConnectionToZygote(mZygote.getPrimarySocketAddress());
+            // preload application code in the zygote
+            Log.i(LOG_TAG, "Starting application preload.");
+            mZygote.preloadApp(mAppInfo, abi);
+            Log.i(LOG_TAG, "Application preload done.");
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Error connecting to app zygote", e);
+            stopZygoteLocked();
+        }
+    }
+}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 8904ee6..a236300 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -382,6 +382,11 @@
     }
 
     /** {@hide} */
+    public static File getDataStagingDirectory(String volumeUuid) {
+        return new File(getDataDirectory(volumeUuid), "staging");
+    }
+
+    /** {@hide} */
     public static File getDataUserCeDirectory(String volumeUuid) {
         return new File(getDataDirectory(volumeUuid), "user");
     }
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index d979309..4c0ee6f 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -353,26 +353,6 @@
     }
 
     /**
-     * Attempt to setup ANGLE with a (temporary) default rules file: b/121153494
-     * True: Rules file was loaded.
-     * False: Rules file was *not* loaded.
-     */
-    private boolean setupAngleRulesDebug(String packageName, String paths, String devOptIn) {
-        // b/121153494
-        // Skip APK rules file checking.
-        if (!DEBUG) {
-            Log.v(TAG, "Skipping loading the rules file.");
-            // Fill in some default values for now, so the loader can get an answer when it asks.
-            // Most importantly, we need to indicate which app we are init'ing and what the
-            // developer options for it are so we can turn on ANGLE if needed.
-            setAngleInfo(paths, packageName, devOptIn, null, 0, 0);
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
      * Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
      * True: APK rules file was loaded.
      * False: APK rules file was *not* loaded.
@@ -450,12 +430,6 @@
             return;
         }
 
-        // b/121153494
-        if (setupAngleRulesDebug(packageName, paths, devOptIn)) {
-            // We setup ANGLE with defaults, so we're done here.
-            return;
-        }
-
         if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) {
             // We setup ANGLE with rules from the APK, so we're done here.
             return;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 505aec2..ee56e3d 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -111,6 +111,12 @@
     public static final int NFC_UID = 1027;
 
     /**
+     * Defines the UID/GID for the clatd process.
+     * @hide
+     * */
+    public static final int CLAT_UID = 1029;
+
+    /**
      * Defines the UID/GID for the Bluetooth service process.
      * @hide
      */
@@ -199,6 +205,24 @@
     public static final int LAST_APPLICATION_UID = 19999;
 
     /**
+     * First uid used for fully isolated sandboxed processes spawned from an app zygote
+     * @hide
+     */
+    public static final int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000;
+
+    /**
+     * Number of UIDs we allocate per application zygote
+     * @hide
+     */
+    public static final int NUM_UIDS_PER_APP_ZYGOTE = 100;
+
+    /**
+     * Last uid used for fully isolated sandboxed processes spawned from an app zygote
+     * @hide
+     */
+    public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999;
+
+    /**
      * First uid used for fully isolated sandboxed processes (with no permissions of their own)
      * @hide
      */
@@ -644,7 +668,8 @@
     /** {@hide} */
     public static final boolean isIsolated(int uid) {
         uid = UserHandle.getAppId(uid);
-        return uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID;
+        return (uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID)
+                || (uid >= FIRST_APP_ZYGOTE_ISOLATED_UID && uid <= LAST_APP_ZYGOTE_ISOLATED_UID);
     }
 
     /**
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index f8feb7b..ad8a4d5 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -138,8 +138,7 @@
      */
     public static boolean isIsolated(int uid) {
         if (uid > 0) {
-            final int appId = getAppId(uid);
-            return appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID;
+            return Process.isIsolated(uid);
         } else {
             return false;
         }
@@ -294,9 +293,14 @@
             sb.append('u');
             sb.append(getUserId(uid));
             final int appId = getAppId(uid);
-            if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
-                sb.append('i');
-                sb.append(appId - Process.FIRST_ISOLATED_UID);
+            if (isIsolated(appId)) {
+                if (appId > Process.FIRST_ISOLATED_UID) {
+                    sb.append('i');
+                    sb.append(appId - Process.FIRST_ISOLATED_UID);
+                } else {
+                    sb.append("ai");
+                    sb.append(appId - Process.FIRST_APP_ZYGOTE_ISOLATED_UID);
+                }
             } else if (appId >= Process.FIRST_APPLICATION_UID) {
                 sb.append('a');
                 sb.append(appId - Process.FIRST_APPLICATION_UID);
@@ -330,9 +334,14 @@
             pw.print('u');
             pw.print(getUserId(uid));
             final int appId = getAppId(uid);
-            if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
-                pw.print('i');
-                pw.print(appId - Process.FIRST_ISOLATED_UID);
+            if (isIsolated(appId)) {
+                if (appId > Process.FIRST_ISOLATED_UID) {
+                    pw.print('i');
+                    pw.print(appId - Process.FIRST_ISOLATED_UID);
+                } else {
+                    pw.print("ai");
+                    pw.print(appId - Process.FIRST_APP_ZYGOTE_ISOLATED_UID);
+                }
             } else if (appId >= Process.FIRST_APPLICATION_UID) {
                 pw.print('a');
                 pw.print(appId - Process.FIRST_APPLICATION_UID);
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index f136cd6..251c5ee 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.util.Log;
@@ -34,6 +35,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Base64;
 import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
@@ -673,6 +675,36 @@
     }
 
     /**
+     * Instructs the zygote to pre-load the application code for the given Application.
+     * Only the app zygote supports this function.
+     * TODO preloadPackageForAbi() can probably be removed and the callers an use this instead.
+     */
+    public boolean preloadApp(ApplicationInfo appInfo, String abi) throws ZygoteStartFailedEx,
+                                                                          IOException {
+        synchronized (mLock) {
+            ZygoteState state = openZygoteSocketIfNeeded(abi);
+            state.writer.write("2");
+            state.writer.newLine();
+
+            state.writer.write("--preload-app");
+            state.writer.newLine();
+
+            // Zygote args needs to be strings, so in order to pass ApplicationInfo,
+            // write it to a Parcel, and base64 the raw Parcel bytes to the other side.
+            Parcel parcel = Parcel.obtain();
+            appInfo.writeToParcel(parcel, 0 /* flags */);
+            String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall());
+            parcel.recycle();
+            state.writer.write(encodedParcelData);
+            state.writer.newLine();
+
+            state.writer.flush();
+
+            return (state.inputStream.readInt() == 0);
+        }
+    }
+
+    /**
      * Instructs the zygote to pre-load the classes and native libraries at the given paths
      * for the specified abi. Not all zygotes support this function.
      */
@@ -763,6 +795,20 @@
      * secondary zygotes that inherit data from the zygote that this object
      * communicates with. This returns a new ZygoteProcess representing a connection
      * to the newly created zygote. Throws an exception if the zygote cannot be started.
+     *
+     * @param processClass The class to use as the child zygote's main entry
+     *                     point.
+     * @param niceName A more readable name to use for the process.
+     * @param uid The user-id under which the child zygote will run.
+     * @param gid The group-id under which the child zygote will run.
+     * @param gids Additional group-ids associated with the child zygote process.
+     * @param runtimeFlags Additional flags.
+     * @param seInfo null-ok SELinux information for the child zygote process.
+     * @param abi non-null the ABI of the child zygote
+     * @param acceptedAbiList ABIs this child zygote will accept connections for; this
+     *                        may be different from <code>abi</code> in case the children
+     *                        spawned from this Zygote only communicate using ABI-safe methods.
+     * @param instructionSet null-ok the instruction set to use.
      */
     public ChildZygoteProcess startChildZygote(final String processClass,
                                                final String niceName,
@@ -770,12 +816,14 @@
                                                int runtimeFlags,
                                                String seInfo,
                                                String abi,
+                                               String acceptedAbiList,
                                                String instructionSet) {
         // Create an unguessable address in the global abstract namespace.
         final LocalSocketAddress serverAddress = new LocalSocketAddress(
                 processClass + "/" + UUID.randomUUID().toString());
 
-        final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName()};
+        final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName(),
+                                    Zygote.CHILD_ZYGOTE_ABI_LIST_ARG + acceptedAbiList};
 
         Process.ProcessStartResult result;
         try {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 9594a71..735f4f2 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1540,6 +1540,7 @@
     }
 
     /** {@hide} */
+    @SystemApi
     @TestApi
     public static boolean hasIsolatedStorage() {
         // Prefer to use snapshot for current boot when available
diff --git a/core/java/android/permission/IRuntimePermissionPresenter.aidl b/core/java/android/permission/IPermissionController.aidl
similarity index 89%
rename from core/java/android/permission/IRuntimePermissionPresenter.aidl
rename to core/java/android/permission/IPermissionController.aidl
index e95428a..38951d5 100644
--- a/core/java/android/permission/IRuntimePermissionPresenter.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -19,11 +19,11 @@
 import android.os.RemoteCallback;
 
 /**
- * Interface for communication with the permission presenter service.
+ * Interface for system apps to communication with the permission controller.
  *
  * @hide
  */
-oneway interface IRuntimePermissionPresenter {
+oneway interface IPermissionController {
     void getAppPermissions(String packageName, in RemoteCallback callback);
     void revokeRuntimePermission(String packageName, String permissionName);
     void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
diff --git a/core/java/android/permission/RuntimePermissionPresenter.java b/core/java/android/permission/PermissionControllerManager.java
similarity index 82%
rename from core/java/android/permission/RuntimePermissionPresenter.java
rename to core/java/android/permission/PermissionControllerManager.java
index c607e3f..66e8666 100644
--- a/core/java/android/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -16,13 +16,16 @@
 
 package android.permission;
 
-import static android.permission.RuntimePermissionPresenterService.SERVICE_INTERFACE;
+import static android.permission.PermissionControllerService.SERVICE_INTERFACE;
 
 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
 import static com.android.internal.util.Preconditions.checkNotNull;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -34,7 +37,6 @@
 import android.os.UserHandle;
 import android.util.Log;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
 import com.android.internal.infra.AbstractRemoteService;
 
@@ -42,29 +44,24 @@
 import java.util.List;
 
 /**
- * This class provides information about runtime permissions for a specific
- * app or all apps. This information is dedicated for presentation purposes
- * and does not necessarily reflect the individual permissions requested/
- * granted to an app as the platform may be grouping permissions to improve
- * presentation and help the user make an informed choice. For example, all
- * runtime permissions in the same permission group may be presented as a
- * single permission in the UI.
+ * Interface for communicating with the permission controller from system apps. All UI operations
+ * regarding permissions and any changes to the permission state should flow through this
+ * interface.
  *
  * @hide
  */
-public final class RuntimePermissionPresenter {
-    private static final String TAG = "RuntimePermPresenter";
+@SystemService(Context.PERMISSION_CONTROLLER_SERVICE)
+public final class PermissionControllerManager {
+    private static final String TAG = PermissionControllerManager.class.getSimpleName();
 
     /**
      * The key for retrieving the result from the returned bundle.
-     *
-     * @hide
      */
     public static final String KEY_RESULT =
-            "android.permission.RuntimePermissionPresenter.key.result";
+            "android.permission.PermissionControllerManager.key.result";
 
     /**
-     * Listener for delivering the result of {@link #getAppPermissions}.
+     * Callback for delivering the result of {@link #getAppPermissions}.
      */
     public interface OnGetAppPermissionResultCallback {
         /**
@@ -77,7 +74,7 @@
     }
 
     /**
-     * Listener for delivering the result of {@link #countPermissionApps}.
+     * Callback for delivering the result of {@link #countPermissionApps}.
      */
     public interface OnCountPermissionAppsResultCallback {
         /**
@@ -89,29 +86,9 @@
         void onCountPermissionApps(int numApps);
     }
 
-    private static final Object sLock = new Object();
-
-    @GuardedBy("sLock")
-    private static RuntimePermissionPresenter sInstance;
-
     private final RemoteService mRemoteService;
 
-    /**
-     * Gets the singleton runtime permission presenter.
-     *
-     * @param context Context for accessing resources.
-     * @return The singleton instance.
-     */
-    public static RuntimePermissionPresenter getInstance(@NonNull Context context) {
-        synchronized (sLock) {
-            if (sInstance == null) {
-                sInstance = new RuntimePermissionPresenter(context.getApplicationContext());
-            }
-            return sInstance;
-        }
-    }
-
-    private RuntimePermissionPresenter(@NonNull Context context) {
+    public PermissionControllerManager(@NonNull Context context) {
         Intent intent = new Intent(SERVICE_INTERFACE);
         intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
         ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
@@ -127,6 +104,7 @@
      * @param callback Callback to receive the result.
      * @param handler Handler on which to invoke the callback.
      */
+    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
     public void getAppPermissions(@NonNull String packageName,
             @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
         checkNotNull(packageName);
@@ -142,6 +120,7 @@
      * @param packageName The package for which to revoke
      * @param permissionName The permission to revoke
      */
+    @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
     public void revokeRuntimePermission(@NonNull String packageName,
             @NonNull String permissionName) {
         checkNotNull(packageName);
@@ -160,6 +139,7 @@
      * @param callback Callback to receive the result
      * @param handler Handler on which to invoke the callback
      */
+    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
     public void countPermissionApps(@NonNull List<String> permissionNames,
             boolean countOnlyGranted, boolean countSystem,
             @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
@@ -175,8 +155,7 @@
      * A connection to the remote service
      */
     static final class RemoteService extends
-            AbstractMultiplePendingRequestsRemoteService<RemoteService,
-                    IRuntimePermissionPresenter>  {
+            AbstractMultiplePendingRequestsRemoteService<RemoteService, IPermissionController> {
         private static final long UNBIND_TIMEOUT_MILLIS = 10000;
         private static final long MESSAGE_TIMEOUT_MILLIS = 30000;
 
@@ -200,9 +179,8 @@
         }
 
         @Override
-        protected @NonNull IRuntimePermissionPresenter getServiceInterface(
-                @NonNull IBinder binder) {
-            return IRuntimePermissionPresenter.Stub.asInterface(binder);
+        protected @NonNull IPermissionController getServiceInterface(@NonNull IBinder binder) {
+            return IPermissionController.Stub.asInterface(binder);
         }
 
         @Override
@@ -217,13 +195,12 @@
 
         @Override
         public void scheduleRequest(@NonNull PendingRequest<RemoteService,
-                IRuntimePermissionPresenter> pendingRequest) {
+                IPermissionController> pendingRequest) {
             super.scheduleRequest(pendingRequest);
         }
 
         @Override
-        public void scheduleAsyncRequest(
-                @NonNull AsyncRequest<IRuntimePermissionPresenter> request) {
+        public void scheduleAsyncRequest(@NonNull AsyncRequest<IPermissionController> request) {
             super.scheduleAsyncRequest(request);
         }
     }
@@ -232,7 +209,7 @@
      * Request for {@link #getAppPermissions}
      */
     private static final class PendingGetAppPermissionRequest extends
-            AbstractRemoteService.PendingRequest<RemoteService, IRuntimePermissionPresenter> {
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
         private final @NonNull String mPackageName;
         private final @NonNull OnGetAppPermissionResultCallback mCallback;
 
@@ -282,7 +259,7 @@
      * Request for {@link #revokeRuntimePermission}
      */
     private static final class PendingRevokeAppPermissionRequest
-            implements AbstractRemoteService.AsyncRequest<IRuntimePermissionPresenter> {
+            implements AbstractRemoteService.AsyncRequest<IPermissionController> {
         private final @NonNull String mPackageName;
         private final @NonNull String mPermissionName;
 
@@ -293,7 +270,7 @@
         }
 
         @Override
-        public void run(IRuntimePermissionPresenter remoteInterface) {
+        public void run(IPermissionController remoteInterface) {
             try {
                 remoteInterface.revokeRuntimePermission(mPackageName, mPermissionName);
             } catch (RemoteException e) {
@@ -306,7 +283,7 @@
      * Request for {@link #countPermissionApps}
      */
     private static final class PendingCountPermissionAppsRequest extends
-            AbstractRemoteService.PendingRequest<RemoteService, IRuntimePermissionPresenter> {
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
         private final @NonNull List<String> mPermissionNames;
         private final @NonNull OnCountPermissionAppsResultCallback mCallback;
         private final boolean mCountOnlyGranted;
diff --git a/core/java/android/permission/RuntimePermissionPresenterService.java b/core/java/android/permission/PermissionControllerService.java
similarity index 76%
rename from core/java/android/permission/RuntimePermissionPresenterService.java
rename to core/java/android/permission/PermissionControllerService.java
index 81ec7be..5dad071 100644
--- a/core/java/android/permission/RuntimePermissionPresenterService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -20,6 +20,7 @@
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.Service;
@@ -33,26 +34,21 @@
 import java.util.List;
 
 /**
- * This service presents information regarding runtime permissions that is
- * used for presenting them in the UI. Runtime permissions are presented as
- * a single permission in the UI but may be composed of several individual
- * permissions.
+ * This service is meant to be implemented by the app controlling permissions.
  *
- * @see RuntimePermissionPresenter
- * @see RuntimePermissionPresentationInfo
+ * @see PermissionController
  *
  * @hide
  */
 @SystemApi
-public abstract class RuntimePermissionPresenterService extends Service {
+public abstract class PermissionControllerService extends Service {
 
     /**
      * The {@link Intent} action that must be declared as handled by a service
      * in its manifest for the system to recognize it as a runtime permission
      * presenter service.
      */
-    public static final String SERVICE_INTERFACE =
-            "android.permission.RuntimePermissionPresenterService";
+    public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
 
     // No need for locking - always set first and never modified
     private Handler mHandler;
@@ -96,16 +92,17 @@
 
     @Override
     public final IBinder onBind(Intent intent) {
-        return new IRuntimePermissionPresenter.Stub() {
+        return new IPermissionController.Stub() {
             @Override
             public void getAppPermissions(String packageName, RemoteCallback callback) {
                 checkNotNull(packageName, "packageName");
                 checkNotNull(callback, "callback");
 
+                enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+
                 mHandler.sendMessage(
-                        obtainMessage(
-                                RuntimePermissionPresenterService::getAppPermissions,
-                                RuntimePermissionPresenterService.this, packageName, callback));
+                        obtainMessage(PermissionControllerService::getAppPermissions,
+                                PermissionControllerService.this, packageName, callback));
             }
 
             @Override
@@ -113,11 +110,11 @@
                 checkNotNull(packageName, "packageName");
                 checkNotNull(permissionName, "permissionName");
 
+                enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
+
                 mHandler.sendMessage(
-                        obtainMessage(
-                                RuntimePermissionPresenterService::onRevokeRuntimePermission,
-                                RuntimePermissionPresenterService.this, packageName,
-                                permissionName));
+                        obtainMessage(PermissionControllerService::onRevokeRuntimePermission,
+                                PermissionControllerService.this, packageName, permissionName));
             }
 
             @Override
@@ -126,11 +123,12 @@
                 checkCollectionElementsNotNull(permissionNames, "permissionNames");
                 checkNotNull(callback, "callback");
 
+                enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+
                 mHandler.sendMessage(
-                        obtainMessage(
-                                RuntimePermissionPresenterService::countPermissionApps,
-                                RuntimePermissionPresenterService.this, permissionNames,
-                                countOnlyGranted, countSystem, callback));
+                        obtainMessage(PermissionControllerService::countPermissionApps,
+                                PermissionControllerService.this, permissionNames, countOnlyGranted,
+                                countSystem, callback));
             }
         };
     }
@@ -139,7 +137,7 @@
         List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
         if (permissions != null && !permissions.isEmpty()) {
             Bundle result = new Bundle();
-            result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, permissions);
+            result.putParcelableList(PermissionControllerManager.KEY_RESULT, permissions);
             callback.sendResult(result);
         } else {
             callback.sendResult(null);
@@ -151,7 +149,7 @@
         int numApps = onCountPermissionApps(permissionNames, countOnlyGranted, countSystem);
 
         Bundle result = new Bundle();
-        result.putInt(RuntimePermissionPresenter.KEY_RESULT, numApps);
+        result.putInt(PermissionControllerManager.KEY_RESULT, numApps);
         callback.sendResult(result);
     }
 }
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index 2b3f0f5..8d568c8 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -30,6 +30,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteCallback;
+import android.permission.PermissionControllerService;
 
 import java.util.List;
 
@@ -43,7 +44,7 @@
  *
  * @hide
  *
- * @deprecated use {@link android.permission.RuntimePermissionPresenterService} instead
+ * @deprecated use {@link PermissionControllerService} instead
  */
 @Deprecated
 @SystemApi
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index e5fd29c..b348da4 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -409,6 +409,15 @@
         public static final String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
 
         /**
+         * Similar to {@link #COLUMN_MEDIAPROVIDER_URI}, except this cannot be updated/queried
+         * by apps and will be the source of truth when updating/deleting download entries in
+         * MediaProvider database.
+         *
+         * <P>Type: TEXT</P>
+         */
+        public static final String COLUMN_MEDIASTORE_URI = "mediastore_uri";
+
+        /**
          * The column that is used to remember whether the media scanner was invoked.
          * It can take the values: null or 0(not scanned), 1(scanned), 2 (not scannable).
          * <P>Type: TEXT</P>
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index c4e2b12..cdbc979 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -17,6 +17,7 @@
 package android.provider;
 
 import android.annotation.BytesLong;
+import android.annotation.DurationMillisLong;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -55,6 +56,7 @@
 import android.os.storage.VolumeInfo;
 import android.service.media.CameraPrewarmService;
 import android.text.TextUtils;
+import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -131,6 +133,8 @@
     /** {@hide} */
     public static final String PARAM_INCLUDE_PENDING = "includePending";
     /** {@hide} */
+    public static final String PARAM_INCLUDE_TRASHED = "includeTrashed";
+    /** {@hide} */
     public static final String PARAM_PROGRESS = "progress";
     /** {@hide} */
     public static final String PARAM_REQUIRE_ORIGINAL = "requireOriginal";
@@ -485,12 +489,29 @@
      * By default no pending items are returned.
      *
      * @see MediaColumns#IS_PENDING
+     * @see MediaStore#setIncludePending(Uri)
+     * @see MediaStore#createPending(Context, PendingParams)
      */
     public static @NonNull Uri setIncludePending(@NonNull Uri uri) {
         return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_PENDING, "1").build();
     }
 
     /**
+     * Update the given {@link Uri} to also include any trashed media items from
+     * calls such as
+     * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
+     * By default no trashed items are returned.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static @NonNull Uri setIncludeTrashed(@NonNull Uri uri) {
+        return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_TRASHED, "1").build();
+    }
+
+    /**
      * Update the given {@link Uri} to indicate that the caller requires the
      * original file contents when calling
      * {@link ContentResolver#openFileDescriptor(Uri, String)}.
@@ -516,6 +537,9 @@
      *
      * @return token which can be passed to {@link #openPending(Context, Uri)}
      *         to work with this pending item.
+     * @see MediaColumns#IS_PENDING
+     * @see MediaStore#setIncludePending(Uri)
+     * @see MediaStore#createPending(Context, PendingParams)
      */
     public static @NonNull Uri createPending(@NonNull Context context,
             @NonNull PendingParams params) {
@@ -572,6 +596,8 @@
             this.insertValues.put(MediaColumns.DATE_ADDED, now);
             this.insertValues.put(MediaColumns.DATE_MODIFIED, now);
             this.insertValues.put(MediaColumns.IS_PENDING, 1);
+            this.insertValues.put(MediaColumns.DATE_EXPIRES,
+                    (System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS) / 1000);
         }
 
         /**
@@ -696,6 +722,7 @@
         public @NonNull Uri publish() {
             final ContentValues values = new ContentValues();
             values.put(MediaColumns.IS_PENDING, 0);
+            values.putNull(MediaColumns.DATE_EXPIRES);
             mContext.getContentResolver().update(mUri, values, null, null);
             return mUri;
         }
@@ -717,6 +744,67 @@
     }
 
     /**
+     * Mark the given item as being "trashed", meaning it should be deleted at
+     * some point in the future. This is a more gentle operation than simply
+     * calling {@link ContentResolver#delete(Uri, String, String[])}, which
+     * would take effect immediately.
+     * <p>
+     * This method preserves trashed items for at least 48 hours before erasing
+     * them, giving the user a chance to untrash the item.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static void trash(@NonNull Context context, @NonNull Uri uri) {
+        trash(context, uri, 48 * DateUtils.HOUR_IN_MILLIS);
+    }
+
+    /**
+     * Mark the given item as being "trashed", meaning it should be deleted at
+     * some point in the future. This is a more gentle operation than simply
+     * calling {@link ContentResolver#delete(Uri, String, String[])}, which
+     * would take effect immediately.
+     * <p>
+     * This method preserves trashed items for at least the given timeout before
+     * erasing them, giving the user a chance to untrash the item.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static void trash(@NonNull Context context, @NonNull Uri uri,
+            @DurationMillisLong long timeoutMillis) {
+        if (timeoutMillis < 0) {
+            throw new IllegalArgumentException();
+        }
+
+        final ContentValues values = new ContentValues();
+        values.put(MediaColumns.IS_TRASHED, 1);
+        values.put(MediaColumns.DATE_EXPIRES,
+                (System.currentTimeMillis() + timeoutMillis) / 1000);
+        context.getContentResolver().update(uri, values, null, null);
+    }
+
+    /**
+     * Mark the given item as being "untrashed", meaning it should no longer be
+     * deleted as previously requested through {@link #trash(Context, Uri)}.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#setIncludeTrashed(Uri)
+     * @see MediaStore#trash(Context, Uri)
+     * @see MediaStore#untrash(Context, Uri)
+     */
+    public static void untrash(@NonNull Context context, @NonNull Uri uri) {
+        final ContentValues values = new ContentValues();
+        values.put(MediaColumns.IS_TRASHED, 0);
+        values.putNull(MediaColumns.DATE_EXPIRES);
+        context.getContentResolver().update(uri, values, null, null);
+    }
+
+    /**
      * Common fields for most MediaProvider tables
      */
     public interface MediaColumns extends BaseColumns {
@@ -821,12 +909,34 @@
          * <p>
          * Type: BOOLEAN
          *
+         * @see MediaColumns#IS_PENDING
+         * @see MediaStore#setIncludePending(Uri)
          * @see MediaStore#createPending(Context, PendingParams)
-         * @see MediaStore#PARAM_INCLUDE_PENDING
          */
         public static final String IS_PENDING = "is_pending";
 
         /**
+         * Flag indicating if a media item is trashed.
+         * <p>
+         * Type: BOOLEAN
+         *
+         * @see MediaColumns#IS_TRASHED
+         * @see MediaStore#setIncludeTrashed(Uri)
+         * @see MediaStore#trash(Context, Uri)
+         * @see MediaStore#untrash(Context, Uri)
+         */
+        public static final String IS_TRASHED = "is_trashed";
+
+        /**
+         * The time the file should be considered expired. Units are seconds
+         * since 1970. Typically only meaningful in the context of
+         * {@link #IS_PENDING} or {@link #IS_TRASHED}.
+         * <p>
+         * Type: INTEGER
+         */
+        public static final String DATE_EXPIRES = "date_expires";
+
+        /**
          * The width of the image/video in pixels.
          */
         public static final String WIDTH = "width";
@@ -915,6 +1025,11 @@
             return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("dir").build();
         }
 
+        /** @hide */
+        public static final Uri getContentUriForPath(String path) {
+            return getContentUri(getVolumeNameForPath(path));
+        }
+
         /**
          * Fields for master table for all media files.
          * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
@@ -1021,6 +1136,13 @@
          * Type: TEXT
          */
         String REFERER_URI = "referer_uri";
+
+        /**
+         * The description of the download.
+         * <p>
+         * Type: Text
+         */
+        String DESCRIPTION = "description";
     }
 
     /**
@@ -1251,18 +1373,32 @@
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
-             * The bucket id of the image. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket ID of this media item. This can be useful to
+             * present the user a first-level clustering of related media items.
+             * This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
              */
             public static final String BUCKET_ID = "bucket_id";
 
             /**
-             * The bucket display name of the image. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket display name of this media item. This can be
+             * useful to present the user a first-level clustering of related
+             * media items. This is a read-only column that is automatically
+             * computed.
+             * <p>
+             * Type: TEXT
              */
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
+
+            /**
+             * The secondary bucket ID of this media item. This can be useful to
+             * present the user a second-level clustering of related media
+             * items. This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
+             */
+            public static final String SECONDARY_BUCKET_ID = "secondary_bucket_id";
         }
 
         public static final class Media implements ImageColumns {
@@ -2500,20 +2636,34 @@
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
-             * The bucket id of the video. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket ID of this media item. This can be useful to
+             * present the user a first-level clustering of related media items.
+             * This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
              */
             public static final String BUCKET_ID = "bucket_id";
 
             /**
-             * The bucket display name of the video. This is a read-only property that
-             * is automatically computed from the DATA column.
-             * <P>Type: TEXT</P>
+             * The primary bucket display name of this media item. This can be
+             * useful to present the user a first-level clustering of related
+             * media items. This is a read-only column that is automatically
+             * computed.
+             * <p>
+             * Type: TEXT
              */
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
+             * The secondary bucket ID of this media item. This can be useful to
+             * present the user a second-level clustering of related media
+             * items. This is a read-only column that is automatically computed.
+             * <p>
+             * Type: INTEGER
+             */
+            public static final String SECONDARY_BUCKET_ID = "secondary_bucket_id";
+
+            /**
              * The bookmark for the video. Time in ms. Represents the location in the video that the
              * video should start playing at the next time it is opened. If the value is null or
              * out of the range 0..DURATION-1 then the video should start playing from the
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 52effb3..940736a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9415,7 +9415,8 @@
                 "hdmi_control_auto_wakeup_enabled";
 
         /**
-         * Whether TV will also turn off other CEC devices when it goes to standby mode.
+         * Whether TV or Audio System will also turn off other CEC devices when it goes to standby
+         * mode.
          * (0 = false, 1 = true)
          *
          * @hide
@@ -9424,6 +9425,15 @@
                 "hdmi_control_auto_device_off_enabled";
 
         /**
+         * Whether Audio System will also turn off TV when it goes to standby mode.
+         * (0 = false, 1 = true)
+         *
+         * @hide
+         */
+        public static final String HDMI_CONTROL_AUTO_TV_OFF_ENABLED =
+                "hdmi_control_auto_tv_off_enabled";
+
+        /**
          * If <b>true</b>, enables out-of-the-box execution for priv apps.
          * Default: false
          * Values: 0 = false, 1 = true
@@ -14075,11 +14085,10 @@
             try {
                 Bundle arg = new Bundle();
                 arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
-                arg.putInt(Settings.CALL_METHOD_RESET_MODE_KEY, resetMode);
+                arg.putInt(CALL_METHOD_RESET_MODE_KEY, resetMode);
                 if (prefix != null) {
                     arg.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
                 }
-                arg.putInt(CALL_METHOD_RESET_MODE_KEY, resetMode);
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
                 cp.call(resolver.getPackageName(), sProviderHolder.mUri.getAuthority(),
                         CALL_METHOD_RESET_CONFIG, null, arg);
diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
index a976d0e..f334d9d 100644
--- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -27,5 +27,5 @@
     void attachEngine(IWallpaperEngine engine, int displayId);
     void engineShown(IWallpaperEngine engine);
     ParcelFileDescriptor setWallpaper(String name);
-    void onWallpaperColorsChanged(in WallpaperColors colors);
+    void onWallpaperColorsChanged(in WallpaperColors colors, int displayId);
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index a011d67..984846e 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -56,6 +56,7 @@
 import android.view.InputEventReceiver;
 import android.view.InsetsState;
 import android.view.MotionEvent;
+import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
 import android.view.View;
 import android.view.ViewGroup;
@@ -217,6 +218,8 @@
         private Context mDisplayContext;
         private int mDisplayState;
 
+        SurfaceControl mSurfaceControl = new SurfaceControl();
+
         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
             {
                 mRequestedFormat = PixelFormat.RGBX_8888;
@@ -642,7 +645,7 @@
             try {
                 final WallpaperColors newColors = onComputeColors();
                 if (mConnection != null) {
-                    mConnection.onWallpaperColorsChanged(newColors);
+                    mConnection.onWallpaperColorsChanged(newColors, mDisplay.getDisplayId());
                 } else {
                     Log.w(TAG, "Can't notify system because wallpaper connection "
                             + "was not established.");
@@ -843,8 +846,12 @@
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
-                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface,
+                            mDisplayCutout, mMergedConfiguration, mSurfaceControl,
                             mInsetsState);
+                    if (mSurfaceControl.isValid()) {
+                        mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
+                        mSurfaceControl.release();
+                    }
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
@@ -1466,7 +1473,7 @@
                         break;
                     }
                     try {
-                        mConnection.onWallpaperColorsChanged(mEngine.onComputeColors());
+                        mConnection.onWallpaperColorsChanged(mEngine.onComputeColors(), mDisplayId);
                     } catch (RemoteException e) {
                         // Connection went away, nothing to do in here.
                     }
diff --git a/core/java/android/view/AccessibilityIterators.java b/core/java/android/view/AccessibilityIterators.java
index 9f7560c..54cfc00 100644
--- a/core/java/android/view/AccessibilityIterators.java
+++ b/core/java/android/view/AccessibilityIterators.java
@@ -147,6 +147,9 @@
         @Override
         public void onConfigurationChanged(Configuration globalConfig) {
             final Locale locale = globalConfig.getLocales().get(0);
+            if (locale == null) {
+                return;
+            }
             if (!mLocale.equals(locale)) {
                 mLocale = locale;
                 onLocaleChanged(locale);
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index d1115c7..658f06a 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -99,7 +99,7 @@
             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 SurfaceControl outSurfaceControl,
             out InsetsState insetsState);
 
     /*
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 3f8f882..885b3e9 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -250,7 +250,7 @@
         }
     }
 
-    static String typeToString(int type) {
+    public static String typeToString(int type) {
         switch (type) {
             case TYPE_TOP_BAR:
                 return "TYPE_TOP_BAR";
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index be81c06..ffd4156 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -784,24 +784,8 @@
             ta.recycle();
         }
 
-        if (name.equals(TAG_1995)) {
-            // Let's party like it's 1995!
-            return new BlinkLayout(context, attrs);
-        }
-
         try {
-            View view;
-            if (mFactory2 != null) {
-                view = mFactory2.onCreateView(parent, name, context, attrs);
-            } else if (mFactory != null) {
-                view = mFactory.onCreateView(name, context, attrs);
-            } else {
-                view = null;
-            }
-
-            if (view == null && mPrivateFactory != null) {
-                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
-            }
+            View view = tryCreateView(parent, name, context, attrs);
 
             if (view == null) {
                 final Object lastContext = mConstructorArgs[0];
@@ -836,6 +820,48 @@
     }
 
     /**
+     * Tries to create a view from a tag name using the supplied attribute set.
+     *
+     * This method gives the factory provided by {@link LayoutInflater#setFactory} and
+     * {@link LayoutInflater#setFactory2} a chance to create a view. However, it does not apply all
+     * of the general view creation logic, and thus may return {@code null} for some tags. This
+     * method is used by {@link LayoutInflater#inflate} in creating {@code View} objects.
+     *
+     * @hide for use by precompiled layouts.
+     *
+     * @param parent the parent view, used to inflate layout params
+     * @param name the name of the XML tag used to define the view
+     * @param context the inflation context for the view, typically the
+     *                {@code parent} or base layout inflater context
+     * @param attrs the attribute set for the XML tag used to define the view
+     */
+    @UnsupportedAppUsage(trackingBug = 122360734)
+    @Nullable
+    public final View tryCreateView(@Nullable View parent, @NonNull String name,
+        @NonNull Context context,
+        @NonNull AttributeSet attrs) {
+        if (name.equals(TAG_1995)) {
+            // Let's party like it's 1995!
+            return new BlinkLayout(context, attrs);
+        }
+
+        View view;
+        if (mFactory2 != null) {
+            view = mFactory2.onCreateView(parent, name, context, attrs);
+        } else if (mFactory != null) {
+            view = mFactory.onCreateView(name, context, attrs);
+        } else {
+            view = null;
+        }
+
+        if (view == null && mPrivateFactory != null) {
+            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
+        }
+
+        return view;
+    }
+
+    /**
      * Recursive method used to inflate internal (non-root) children. This
      * method calls through to {@link #rInflate} using the parent context as
      * the inflation context.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 1a708e8..0b6beba 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -67,6 +67,7 @@
             int w, int h, int format, int flags, long parentObject, int windowType, int ownerUid)
             throws OutOfResourcesException;
     private static native long nativeReadFromParcel(Parcel in);
+    private static native long nativeCopyFromSurfaceControl(long nativeObject);
     private static native void nativeWriteToParcel(long nativeObject, Parcel out);
     private static native void nativeRelease(long nativeObject);
     private static native void nativeDestroy(long nativeObject);
@@ -169,7 +170,7 @@
             IBinder toToken);
 
     private final CloseGuard mCloseGuard = CloseGuard.get();
-    private final String mName;
+    private String mName;
     long mNativeObject; // package visibility only for Surface.java access
 
     // TODO: Move this to native.
@@ -359,6 +360,13 @@
      */
     public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731;
 
+    public void copyFrom(SurfaceControl other) {
+        mName = other.mName;
+        mWidth = other.mWidth;
+        mHeight = other.mHeight;
+        mNativeObject = nativeCopyFromSurfaceControl(other.mNativeObject);
+    }
+
     /**
      * Builder class for {@link SurfaceControl} objects.
      */
@@ -660,14 +668,29 @@
     }
 
     private SurfaceControl(Parcel in) {
+        readFromParcel(in);
+        mCloseGuard.open("release");
+    }
+
+    public SurfaceControl() {
+        mCloseGuard.open("release");
+    }
+
+    public void readFromParcel(Parcel in) {
+        if (in == null) {
+            throw new IllegalArgumentException("source must not be null");
+        }
+
         mName = in.readString();
         mWidth = in.readInt();
         mHeight = in.readInt();
-        mNativeObject = nativeReadFromParcel(in);
-        if (mNativeObject == 0) {
-            throw new IllegalArgumentException("Couldn't read SurfaceControl from parcel=" + in);
+
+        release();
+        if (in.readInt() != 0) {
+            mNativeObject = nativeReadFromParcel(in);
+        } else {
+            mNativeObject = 0;
         }
-        mCloseGuard.open("release");
     }
 
     @Override
@@ -680,7 +703,16 @@
         dest.writeString(mName);
         dest.writeInt(mWidth);
         dest.writeInt(mHeight);
+        if (mNativeObject == 0) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+        }
         nativeWriteToParcel(mNativeObject, dest);
+
+        if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
+            release();
+        }
     }
 
     /**
@@ -763,6 +795,10 @@
                 "mNativeObject is null. Have you called release() already?");
     }
 
+    public boolean isValid() {
+        return mNativeObject != 0;
+    }
+
     /*
      * set surface parameters.
      * needs to be inside open/closeTransaction block
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index a4fa12a..361ac93 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -30,7 +30,6 @@
     private long mNativeClient; // SurfaceComposerClient*
 
     private static native long nativeCreate();
-    private static native long nativeCreateScoped(long surfacePtr);
     private static native void nativeDestroy(long ptr);
     private static native void nativeKill(long ptr);
 
@@ -40,15 +39,6 @@
         mNativeClient = nativeCreate();
     }
 
-    public SurfaceSession(Surface root) {
-        synchronized (root.mLock) {
-            if (root.mNativeObject == 0) {
-                throw new IllegalStateException("Surface is not initialized or has been released");
-            }
-            mNativeClient = nativeCreateScoped(root.mNativeObject);
-        }
-    }
-
     /* no user serviceable parts here ... */
     @Override
     protected void finalize() throws Throwable {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a0af83d..61fb00d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -547,7 +547,7 @@
 
                 if (creating) {
                     viewRoot.createBoundsSurface(mSubLayer);
-                    mSurfaceSession = new SurfaceSession(viewRoot.mBoundsSurface);
+                    mSurfaceSession = new SurfaceSession();
                     mDeferredDestroySurfaceControl = mSurfaceControl;
 
                     updateOpaqueFlag();
@@ -559,6 +559,7 @@
                             new SurfaceControl.Builder(mSurfaceSession)
                                     .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                                     .setFormat(mFormat)
+                                    .setParent(viewRoot.getSurfaceControl())
                                     .setFlags(mSurfaceFlags));
                 } else if (mSurfaceControl == null) {
                     return;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6b07efc..4504887 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -25053,9 +25053,10 @@
         }
 
         final ViewRootImpl root = mAttachInfo.mViewRootImpl;
-        final SurfaceSession session = new SurfaceSession(root.mSurface);
+        final SurfaceSession session = new SurfaceSession();
         final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
                 .setName("drag surface")
+                .setParent(root.getSurfaceControl())
                 .setBufferSize(shadowSize.x, shadowSize.y)
                 .setFormat(PixelFormat.TRANSLUCENT)
                 .build();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3f7a512..c0b4283 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -432,6 +432,7 @@
     // Surface can never be reassigned or cleared (use Surface.clear()).
     @UnsupportedAppUsage
     public final Surface mSurface = new Surface();
+    private final SurfaceControl mSurfaceControl = new SurfaceControl();
 
     /**
      * Child surface of {@code mSurface} with the same bounds as its parent, and crop bounds
@@ -1526,7 +1527,7 @@
      */
     public void createBoundsSurface(int zOrderLayer) {
         if (mSurfaceSession == null) {
-            mSurfaceSession = new SurfaceSession(mSurface);
+            mSurfaceSession = new SurfaceSession();
         }
         if (mBoundsSurfaceControl != null && mBoundsSurface.isValid()) {
             return; // surface control for bounds surface already exists.
@@ -1534,6 +1535,7 @@
 
         mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                 .setName("Bounds for - " + getTitle().toString())
+                .setParent(mSurfaceControl)
                 .build();
 
         setBoundsSurfaceCrop();
@@ -1567,6 +1569,8 @@
 
     private void destroySurface() {
         mSurface.release();
+        mSurfaceControl.release();
+
         mSurfaceSession = null;
 
         if (mBoundsSurfaceControl != null) {
@@ -6801,7 +6805,12 @@
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                 mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                 mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
-                mPendingMergedConfiguration, mSurface, mTempInsets);
+                mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
+        if (mSurfaceControl.isValid()) {
+            mSurface.copyFrom(mSurfaceControl);
+        } else {
+            destroySurface();
+        }
 
         mPendingAlwaysConsumeNavBar =
                 (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -8483,6 +8492,10 @@
         mActivityRelaunched = true;
     }
 
+    public SurfaceControl getSurfaceControl() {
+        return mSurfaceControl;
+    }
+
     /**
      * Class for managing the accessibility interaction connection
      * based on the global accessibility state.
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index fb6cacf..46e6882 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -42,6 +42,8 @@
  */
 public abstract class ContentCaptureSession implements AutoCloseable {
 
+    private static final String TAG = ContentCaptureSession.class.getSimpleName();
+
     /**
      * Used on {@link #notifyViewTextChanged(AutofillId, CharSequence, int)} to indicate that the
      *
@@ -86,9 +88,6 @@
 
     private static final int INITIAL_CHILDREN_CAPACITY = 5;
 
-    /** @hide */
-    protected final String mTag = getClass().getSimpleName();
-
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
     /**
@@ -141,7 +140,7 @@
             @NonNull ContentCaptureContext context) {
         final ContentCaptureSession child = newChild(context);
         if (DEBUG) {
-            Log.d(mTag, "createContentCaptureSession(" + context + ": parent=" + mId + ", child= "
+            Log.d(TAG, "createContentCaptureSession(" + context + ": parent=" + mId + ", child="
                     + child.mId);
         }
         if (mChildren == null) {
@@ -165,7 +164,7 @@
      */
     public final void destroy() {
         if (!mDestroyed.compareAndSet(false, true)) {
-            Log.e(mTag, "destroy(): already destroyed");
+            Log.e(TAG, "destroy(): already destroyed");
             return;
         }
 
@@ -174,19 +173,19 @@
         //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(mTag, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
+            Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
         }
 
         // Finish children first
         if (mChildren != null) {
             final int numberChildren = mChildren.size();
-            if (VERBOSE) Log.v(mTag, "Destroying " + numberChildren + " children first");
+            if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first");
             for (int i = 0; i < numberChildren; i++) {
                 final ContentCaptureSession child = mChildren.get(i);
                 try {
                     child.destroy();
                 } catch (Exception e) {
-                    Log.w(mTag, "exception destroying child session #" + i + ": " + e);
+                    Log.w(TAG, "exception destroying child session #" + i + ": " + e);
                 }
             }
         }
@@ -311,11 +310,12 @@
 
     @CallSuper
     void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        pw.print(prefix); pw.print("id: "); pw.println(mId);
         pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed.get());
         if (mChildren != null && !mChildren.isEmpty()) {
             final String prefix2 = prefix + "  ";
             final int numberChildren = mChildren.size();
-            pw.print(prefix); pw.print("number children: "); pw.print(numberChildren);
+            pw.print(prefix); pw.print("number children: "); pw.println(numberChildren);
             for (int i = 0; i < numberChildren; i++) {
                 final ContentCaptureSession child = mChildren.get(i);
                 pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw);
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 12c50ce..baf4a35 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -63,6 +63,8 @@
  */
 public final class MainContentCaptureSession extends ContentCaptureSession {
 
+    private static final String TAG = MainContentCaptureSession.class.getSimpleName();
+
     /**
      * Handler message used to flush the buffer.
      */
@@ -128,9 +130,6 @@
     // Used just for debugging purposes (on dump)
     private long mNextFlush;
 
-    // Lazily created on demand.
-    private ContentCaptureSessionId mContentCaptureSessionId;
-
     /** @hide */
     protected MainContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
             @Nullable IContentCaptureManager systemServerInterface,
@@ -157,7 +156,7 @@
         if (!isContentCaptureEnabled()) return;
 
         if (VERBOSE) {
-            Log.v(mTag, "start(): token=" + applicationToken + ", comp="
+            Log.v(TAG, "start(): token=" + applicationToken + ", comp="
                     + ComponentName.flattenToShortString(activityComponent));
         }
 
@@ -179,7 +178,7 @@
     private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
         if (mState != STATE_UNKNOWN) {
             // TODO(b/111276913): revisit this scenario
-            Log.w(mTag, "ignoring handleStartSession(" + token + ") while on state "
+            Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
                     + getStateAsString(mState));
             return;
         }
@@ -188,7 +187,7 @@
         mComponentName = componentName;
 
         if (VERBOSE) {
-            Log.v(mTag, "handleStartSession(): token=" + token + ", act="
+            Log.v(TAG, "handleStartSession(): token=" + token + ", act="
                     + getActivityDebugName() + ", id=" + mId);
         }
         final int flags = 0; // TODO(b/111276913): get proper flags
@@ -202,7 +201,7 @@
                             if (resultData != null) {
                                 binder = resultData.getBinder(EXTRA_BINDER);
                                 if (binder == null) {
-                                    Log.wtf(mTag, "No " + EXTRA_BINDER + " extra result");
+                                    Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
                                     handleResetState();
                                     return;
                                 }
@@ -211,7 +210,7 @@
                         }
                     });
         } catch (RemoteException e) {
-            Log.w(mTag, "Error starting session for " + componentName.flattenToShortString() + ": "
+            Log.w(TAG, "Error starting session for " + componentName.flattenToShortString() + ": "
                     + e);
         }
     }
@@ -229,13 +228,13 @@
         if (binder != null) {
             mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
             mDirectServiceVulture = () -> {
-                Log.w(mTag, "Destroying session " + mId + " because service died");
+                Log.w(TAG, "Destroying session " + mId + " because service died");
                 destroy();
             };
             try {
                 binder.linkToDeath(mDirectServiceVulture, 0);
             } catch (RemoteException e) {
-                Log.w(mTag, "Failed to link to death on " + binder + ": " + e);
+                Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
             }
         }
         if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
@@ -245,7 +244,7 @@
             mDisabled.set(false);
         }
         if (VERBOSE) {
-            Log.v(mTag, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
+            Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
                     + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
                     + ", binder=" + binder);
         }
@@ -254,7 +253,7 @@
     private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
         if (mEvents == null) {
             if (VERBOSE) {
-                Log.v(mTag, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
+                Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
             }
             mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
         }
@@ -266,7 +265,7 @@
             if (lastEvent.getType() == TYPE_VIEW_TEXT_CHANGED
                     && lastEvent.getId().equals(event.getId())) {
                 if (VERBOSE) {
-                    Log.v(mTag, "Buffering VIEW_TEXT_CHANGED event, updated text = "
+                    Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text = "
                             + event.getText());
                 }
                 lastEvent.setText(event.getText());
@@ -293,7 +292,7 @@
             // not complete instead. Similarly, the manager service should return right away
             // when the user does not have a service set
             if (VERBOSE) {
-                Log.v(mTag, "Closing session for " + getActivityDebugName()
+                Log.v(TAG, "Closing session for " + getActivityDebugName()
                         + " after " + numberEvents + " delayed events and state "
                         + getStateAsString(mState));
             }
@@ -313,7 +312,7 @@
         }
         mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
         if (VERBOSE) {
-            Log.v(mTag, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
+            Log.v(TAG, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
         }
         mHandler.sendMessageDelayed(
                 obtainMessage(MainContentCaptureSession::handleFlushIfNeeded, this)
@@ -322,7 +321,7 @@
 
     private void handleFlushIfNeeded() {
         if (mEvents.isEmpty()) {
-            if (VERBOSE) Log.v(mTag, "Nothing to flush");
+            if (VERBOSE) Log.v(TAG, "Nothing to flush");
             return;
         }
         handleForceFlush();
@@ -332,7 +331,7 @@
         if (mEvents == null) return;
 
         if (mDirectServiceInterface == null) {
-            if (DEBUG) Log.d(mTag, "handleForceFlush(): hold your horses, client not ready yet!");
+            if (DEBUG) Log.d(TAG, "handleForceFlush(): hold your horses, client not ready yet!");
             if (!mHandler.hasMessages(MSG_FLUSH)) {
                 handleScheduleFlush(/* checkExisting= */ false);
             }
@@ -342,14 +341,14 @@
         final int numberEvents = mEvents.size();
         try {
             if (DEBUG) {
-                Log.d(mTag, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
+                Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
             }
             mHandler.removeMessages(MSG_FLUSH);
 
             final ParceledListSlice<ContentCaptureEvent> events = handleClearEvents();
             mDirectServiceInterface.sendEvents(events);
         } catch (RemoteException e) {
-            Log.w(mTag, "Error sending " + numberEvents + " for " + getActivityDebugName()
+            Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
                     + ": " + e);
         }
     }
@@ -370,7 +369,7 @@
 
     private void handleDestroySession() {
         if (DEBUG) {
-            Log.d(mTag, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
+            Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
                     + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
                     + getActivityDebugName());
         }
@@ -378,7 +377,7 @@
         try {
             mSystemServerInterface.finishSession(mContext.getUserId(), mId);
         } catch (RemoteException e) {
-            Log.e(mTag, "Error destroying system-service session " + mId + " for "
+            Log.e(TAG, "Error destroying system-service session " + mId + " for "
                     + getActivityDebugName() + ": " + e);
         }
     }
@@ -395,8 +394,6 @@
         }
 
         // TODO(b/121033016): must reset children (which currently is owned by superclass)
-
-        mContentCaptureSessionId = null;
         mApplicationToken = null;
         mComponentName = null;
         mEvents = null;
@@ -482,9 +479,6 @@
         }
         pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
         pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
-        if (mContentCaptureSessionId != null) {
-            pw.print(prefix); pw.print("public id: "); pw.println(mContentCaptureSessionId);
-        }
         pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" (");
         pw.print(getStateAsString(mState)); pw.println(")");
         if (mApplicationToken != null) {
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 49e11b8..9f7aa6a 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -159,6 +159,7 @@
                     0,  // runtimeFlags
                     "webview_zygote",  // seInfo
                     sPackage.applicationInfo.primaryCpuAbi,  // abi
+                    TextUtils.join(",", Build.SUPPORTED_ABIS),
                     null);  // instructionSet
 
             // All the work below is usually done by LoadedApk, but the zygote can't talk to
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 7d02757..886718d 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -271,7 +271,7 @@
             if (mWindow == null) {
                 synchronized (mLock) {
                     mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
-                            mParentSurface.mSurface, mWindowWidth, mWindowHeight,
+                            mParentSurface.mSurfaceControl, mWindowWidth, mWindowHeight,
                             mWindowElevation, mWindowCornerRadius,
                             mOverlay != null ? mOverlay : new ColorDrawable(Color.TRANSPARENT),
                             Handler.getMain() /* draw the magnifier on the UI thread */, mLock,
@@ -528,17 +528,20 @@
                 final int surfaceHeight =
                         viewRootImpl.getHeight() + surfaceInsets.top + surfaceInsets.bottom;
                 validMainWindowSurface =
-                        new SurfaceInfo(mainWindowSurface, surfaceWidth, surfaceHeight, true);
+                        new SurfaceInfo(viewRootImpl.getSurfaceControl(), mainWindowSurface,
+                                surfaceWidth, surfaceHeight, true);
             }
         }
         // Get the surface backing the magnified view, if it is a SurfaceView.
         SurfaceInfo validSurfaceViewSurface = SurfaceInfo.NULL;
         if (mView instanceof SurfaceView) {
+            final SurfaceControl sc = ((SurfaceView) mView).getSurfaceControl();
             final SurfaceHolder surfaceHolder = ((SurfaceView) mView).getHolder();
             final Surface surfaceViewSurface = surfaceHolder.getSurface();
-            if (surfaceViewSurface != null && surfaceViewSurface.isValid()) {
+
+            if (sc != null && sc.isValid()) {
                 final Rect surfaceFrame = surfaceHolder.getSurfaceFrame();
-                validSurfaceViewSurface = new SurfaceInfo(surfaceViewSurface,
+                validSurfaceViewSurface = new SurfaceInfo(sc, surfaceViewSurface,
                         surfaceFrame.right, surfaceFrame.bottom, false);
             }
         }
@@ -733,15 +736,18 @@
      * Contains a surface and metadata corresponding to it.
      */
     private static class SurfaceInfo {
-        public static final SurfaceInfo NULL = new SurfaceInfo(null, 0, 0, false);
+        public static final SurfaceInfo NULL = new SurfaceInfo(null, null, 0, 0, false);
 
         private Surface mSurface;
+        private SurfaceControl mSurfaceControl;
         private int mWidth;
         private int mHeight;
         private boolean mIsMainWindowSurface;
 
-        SurfaceInfo(final Surface surface, final int width, final int height,
+        SurfaceInfo(final SurfaceControl surfaceControl, final Surface surface,
+                final int width, final int height,
                 final boolean isMainWindowSurface) {
+            mSurfaceControl = surfaceControl;
             mSurface = surface;
             mWidth = width;
             mHeight = height;
@@ -819,7 +825,7 @@
         private Bitmap mCurrentContent;
 
         InternalPopupWindow(final Context context, final Display display,
-                final Surface parentSurface, final int width, final int height,
+                final SurfaceControl parentSurfaceControl, final int width, final int height,
                 final float elevation, final float cornerRadius, final Drawable overlay,
                 final Handler handler, final Object lock, final Callback callback) {
             mDisplay = display;
@@ -834,12 +840,13 @@
             // Setup the surface we will use for drawing the content and shadow.
             mSurfaceWidth = mContentWidth + 2 * mOffsetX;
             mSurfaceHeight = mContentHeight + 2 * mOffsetY;
-            mSurfaceSession = new SurfaceSession(parentSurface);
+            mSurfaceSession = new SurfaceSession();
             mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                     .setName("magnifier surface")
                     .setFlags(SurfaceControl.HIDDEN)
+                    .setParent(parentSurfaceControl)
                     .build();
             mSurface = new Surface();
             mSurface.copyFrom(mSurfaceControl);
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index e83e79b..925a589 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -357,15 +357,16 @@
     }
 
     private boolean performTtsPrompt(AlertDialog alertDialog) {
+        final String serviceName = getShortcutFeatureDescription(false /* no summary */);
         final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
-        if (serviceInfo == null) {
+        if (TextUtils.isEmpty(serviceName) || serviceInfo == null) {
             return false;
         }
         if ((serviceInfo.flags & AccessibilityServiceInfo
                 .FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK) == 0) {
             return false;
         }
-        final TtsPrompt tts = new TtsPrompt();
+        final TtsPrompt tts = new TtsPrompt(serviceName);
         alertDialog.setOnDismissListener(dialog -> tts.dismiss());
         return true;
     }
@@ -378,8 +379,9 @@
         private boolean mDismiss;
         private TextToSpeech mTts;
 
-        TtsPrompt() {
-            mText = mContext.getString(R.string.accessibility_shortcut_spoken_feedback);
+        TtsPrompt(String serviceName) {
+            mText = mContext.getString(R.string.accessibility_shortcut_spoken_feedback,
+                    serviceName);
             mTts = mFrameworkObjectProvider.getTextToSpeech(mContext, this);
         }
 
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 96d3baf..f61a03b 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -22,44 +22,36 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
-import android.content.SharedPreferences;
-import android.content.ServiceConnection;
 import android.metrics.LogMaker;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.os.storage.StorageManager;
 import android.os.UserHandle;
-import android.service.resolver.IResolverRankerService;
 import android.service.resolver.IResolverRankerResult;
+import android.service.resolver.IResolverRankerService;
 import android.service.resolver.ResolverRankerService;
 import android.service.resolver.ResolverTarget;
-import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.Log;
+
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
-import java.io.File;
-import java.lang.InterruptedException;
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Ranks and compares packages based on usage stats.
@@ -90,6 +82,8 @@
 
     private final Collator mCollator;
     private final boolean mHttp;
+    // can be null if mHttp == false or current user has no default browser package
+    private final String mDefaultBrowserPackageName;
     private final PackageManager mPm;
     private final UsageStatsManager mUsm;
     private final Map<String, UsageStats> mStats;
@@ -184,6 +178,10 @@
         getContentAnnotations(intent);
         mAction = intent.getAction();
         mRankerServiceName = new ComponentName(mContext, this.getClass());
+
+        mDefaultBrowserPackageName = mHttp
+                ? mPm.getDefaultBrowserPackageNameAsUser(UserHandle.myUserId())
+                : null;
     }
 
     // get annotations of content from intent.
@@ -312,7 +310,14 @@
         if (mHttp) {
             // Special case: we want filters that match URI paths/schemes to be
             // ordered before others.  This is for the case when opening URIs,
-            // to make native apps go above browsers.
+            // to make native apps go above browsers - except for 1 even more special case
+            // which is the default browser, as we want that to go above them all.
+            if (isDefaultBrowser(lhs)) {
+                return -1;
+            }
+            if (isDefaultBrowser(rhs)) {
+                return 1;
+            }
             final boolean lhsSpecific = ResolverActivity.isSpecificUriMatch(lhs.match);
             final boolean rhsSpecific = ResolverActivity.isSpecificUriMatch(rhs.match);
             if (lhsSpecific != rhsSpecific) {
@@ -419,6 +424,20 @@
         }
     }
 
+    private boolean isDefaultBrowser(ResolveInfo ri) {
+        // It makes sense to prefer the default browser
+        // only if the targeted user is the current user
+        if (ri.targetUserId != UserHandle.USER_CURRENT) {
+            return false;
+        }
+
+        if (ri.activityInfo.packageName != null
+                && ri.activityInfo.packageName.equals(mDefaultBrowserPackageName)) {
+            return true;
+        }
+        return false;
+    }
+
     // records metrics for evaluation.
     private void logMetrics(int selectedPos) {
         if (mRankerServiceName != null) {
diff --git a/core/java/com/android/internal/os/AppZygoteInit.java b/core/java/com/android/internal/os/AppZygoteInit.java
new file mode 100644
index 0000000..afe6dad
--- /dev/null
+++ b/core/java/com/android/internal/os/AppZygoteInit.java
@@ -0,0 +1,105 @@
+/*
+ * 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 android.app.LoadedApk;
+import android.content.pm.ApplicationInfo;
+import android.net.LocalSocket;
+import android.util.Log;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Startup class for an Application zygote process.
+ *
+ * See {@link ZygoteInit} for generic zygote startup documentation.
+ *
+ * @hide
+ */
+class AppZygoteInit {
+    public static final String TAG = "AppZygoteInit";
+
+    private static ZygoteServer sServer;
+
+    private static class AppZygoteServer extends ZygoteServer {
+        @Override
+        protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
+                throws IOException {
+            return new AppZygoteConnection(socket, abiList);
+        }
+    }
+
+    private static class AppZygoteConnection extends ZygoteConnection {
+        AppZygoteConnection(LocalSocket socket, String abiList) throws IOException {
+            super(socket, abiList);
+        }
+
+        @Override
+        protected void preload() {
+            // Nothing to preload by default.
+        }
+
+        @Override
+        protected boolean isPreloadComplete() {
+            // App zygotes don't preload any classes or resources or defaults, all of their
+            // preloading is package specific.
+            return true;
+        }
+
+        @Override
+        protected boolean canPreloadApp() {
+            return true;
+        }
+
+        @Override
+        protected void handlePreloadApp(ApplicationInfo appInfo) {
+            Log.i(TAG, "Beginning application preload for " + appInfo.packageName);
+            LoadedApk loadedApk = new LoadedApk(null, appInfo, null, null, false, true, false);
+            ClassLoader loader = loadedApk.getClassLoader();
+            Class<?> cl;
+            Method m;
+            try {
+                cl = Class.forName(appInfo.packageName + ".ZygotePreload", true, loader);
+                m = cl.getMethod("doPreload");
+                m.setAccessible(true);
+                m.invoke(null);
+            } catch (ClassNotFoundException e) {
+                // Don't treat this as an error since an app may not want to do any preloads
+                Log.w(TAG, "No ZygotePreload class found for " + appInfo.packageName);
+            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+                Log.e(TAG, "AppZygote application preload failed for "
+                        + appInfo.packageName, e);
+            }
+            try {
+                DataOutputStream socketOut = getSocketOutputStream();
+                socketOut.writeInt(loader != null ? 1 : 0);
+            } catch (IOException e) {
+                throw new IllegalStateException("Error writing to command socket", e);
+            }
+
+            Log.i(TAG, "Application preload done");
+        }
+    }
+
+    public static void main(String[] argv) {
+        AppZygoteServer server = new AppZygoteServer();
+        ChildZygoteInit.runZygoteServer(server, argv);
+    }
+}
diff --git a/core/java/com/android/internal/os/ChildZygoteInit.java b/core/java/com/android/internal/os/ChildZygoteInit.java
new file mode 100644
index 0000000..f90cd02
--- /dev/null
+++ b/core/java/com/android/internal/os/ChildZygoteInit.java
@@ -0,0 +1,99 @@
+/*
+ * 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 android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Log;
+
+/**
+ * ChildZygoteInit is shared by both the Application and WebView zygote to initialize
+ * and run a (child) Zygote server.
+ *
+ * @hide
+ */
+public class ChildZygoteInit {
+    private static final String TAG = "ChildZygoteInit";
+
+    static String parseSocketNameFromArgs(String[] argv) {
+        for (String arg : argv) {
+            if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+                return arg.substring(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG.length());
+            }
+        }
+
+        return null;
+    }
+
+    static String parseAbiListFromArgs(String[] argv) {
+        for (String arg : argv) {
+            if (arg.startsWith(Zygote.CHILD_ZYGOTE_ABI_LIST_ARG)) {
+                return arg.substring(Zygote.CHILD_ZYGOTE_ABI_LIST_ARG.length());
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Starts a ZygoteServer and listens for requests
+     *
+     * @param server An instance of a ZygoteServer to listen on
+     * @param args Passed in arguments for this ZygoteServer
+     */
+    static void runZygoteServer(ZygoteServer server, String[] args) {
+        String socketName = parseSocketNameFromArgs(args);
+        if (socketName == null) {
+            throw new NullPointerException("No socketName specified");
+        }
+
+        String abiList = parseAbiListFromArgs(args);
+        if (abiList == null) {
+            throw new NullPointerException("No abiList specified");
+        }
+
+        try {
+            Os.prctl(OsConstants.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+        } catch (ErrnoException ex) {
+            throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex);
+        }
+
+        final Runnable caller;
+        try {
+            server.registerServerSocketAtAbstractName(socketName);
+
+            // Add the abstract socket to the FD whitelist so that the native zygote code
+            // can properly detach it after forking.
+            Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName);
+
+            // The select loop returns early in the child process after a fork and
+            // loops forever in the zygote.
+            caller = server.runSelectLoop(abiList);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Fatal exception:", e);
+            throw e;
+        } finally {
+            server.closeServerSocket();
+        }
+
+        // We're in the child process and have exited the select loop. Proceed to execute the
+        // command.
+        if (caller != null) {
+            caller.run();
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index dc660a4..a319d83 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -30,6 +30,14 @@
     public static final String CONTROL_PRIVAPP_PERMISSIONS =
             SystemProperties.get("ro.control_privapp_permissions");
 
+    /**
+     * Property to indicate if a CEC audio device should forward volume keys when system audio
+     * mode is off.
+     */
+    public static final boolean CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF =
+            SystemProperties.getBoolean(
+                    "ro.hdmi.cec_audio_device_forward_volume_keys_system_audio_mode_off", false);
+
     // ------ ro.config.* -------- //
     public static final boolean CONFIG_AVOID_GFX_ACCEL =
             SystemProperties.getBoolean("ro.config.avoid_gfx_accel", false);
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index 9f2434e..0b329d7 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -18,11 +18,7 @@
 
 import android.app.ApplicationLoaders;
 import android.net.LocalSocket;
-import android.net.LocalServerSocket;
 import android.os.Build;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.Log;
 import android.webkit.WebViewFactory;
@@ -44,8 +40,6 @@
 class WebViewZygoteInit {
     public static final String TAG = "WebViewZygoteInit";
 
-    private static ZygoteServer sServer;
-
     private static class WebViewZygoteServer extends ZygoteServer {
         @Override
         protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
@@ -127,48 +121,7 @@
 
     public static void main(String argv[]) {
         Log.i(TAG, "Starting WebViewZygoteInit");
-
-        String socketName = null;
-        for (String arg : argv) {
-            Log.i(TAG, arg);
-            if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
-                socketName = arg.substring(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG.length());
-            }
-        }
-        if (socketName == null) {
-            throw new RuntimeException("No " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + " specified");
-        }
-
-        try {
-            Os.prctl(OsConstants.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
-        } catch (ErrnoException ex) {
-            throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex);
-        }
-
-        sServer = new WebViewZygoteServer();
-
-        final Runnable caller;
-        try {
-            sServer.registerServerSocketAtAbstractName(socketName);
-
-            // Add the abstract socket to the FD whitelist so that the native zygote code
-            // can properly detach it after forking.
-            Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName);
-
-            // The select loop returns early in the child process after a fork and
-            // loops forever in the zygote.
-            caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
-        } catch (RuntimeException e) {
-            Log.e(TAG, "Fatal exception:", e);
-            throw e;
-        } finally {
-            sServer.closeServerSocket();
-        }
-
-        // We're in the child process and have exited the select loop. Proceed to execute the
-        // command.
-        if (caller != null) {
-            caller.run();
-        }
+        WebViewZygoteServer server = new WebViewZygoteServer();
+        ChildZygoteInit.runZygoteServer(server, argv);
     }
 }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 65b9fad..d720c68 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -98,6 +98,12 @@
      */
     public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket=";
 
+    /**
+     * An extraArg passed when a zygote process is forking a child-zygote, specifying the
+     * requested ABI for the child Zygote.
+     */
+    public static final String CHILD_ZYGOTE_ABI_LIST_ARG = "--abi-list=";
+
     private Zygote() {}
 
     /** Called for some security initialization before any fork. */
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index f182c4d..5990d72 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -27,9 +27,11 @@
 import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
 import static com.android.internal.os.ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
 
+import android.content.pm.ApplicationInfo;
 import android.net.Credentials;
 import android.net.LocalSocket;
 import android.os.FactoryTest;
+import android.os.Parcel;
 import android.os.Process;
 import android.os.SystemProperties;
 import android.os.Trace;
@@ -52,6 +54,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Base64;
 
 /**
  * A connection that can make spawn requests.
@@ -168,6 +171,21 @@
             return null;
         }
 
+        if (canPreloadApp() && parsedArgs.mPreloadApp != null) {
+            byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp);
+            Parcel appInfoParcel = Parcel.obtain();
+            appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length);
+            appInfoParcel.setDataPosition(0);
+            ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(appInfoParcel);
+            appInfoParcel.recycle();
+            if (appInfo != null) {
+                handlePreloadApp(appInfo);
+            } else {
+                throw new IllegalArgumentException("Failed to deserialize --preload-app");
+            }
+            return null;
+        }
+
         if (parsedArgs.apiBlacklistExemptions != null) {
             handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions);
             return null;
@@ -341,7 +359,15 @@
 
     protected void handlePreloadPackage(String packagePath, String libsPath, String libFileName,
             String cacheKey) {
-        throw new RuntimeException("Zyogte does not support package preloading");
+        throw new RuntimeException("Zygote does not support package preloading");
+    }
+
+    protected boolean canPreloadApp() {
+        return false;
+    }
+
+    protected void handlePreloadApp(ApplicationInfo aInfo) {
+        throw new RuntimeException("Zygote does not support app preloading");
     }
 
     /**
@@ -467,6 +493,12 @@
         String preloadPackage;
 
         /**
+         * A Base64 string representing a serialize ApplicationInfo Parcel,
+           when using --preload-app.
+          */
+        String mPreloadApp;
+
+        /**
          * The native library path of the package to preload, when using --preload-package.
          */
         String preloadPackageLibs;
@@ -666,6 +698,8 @@
                     instructionSet = arg.substring(arg.indexOf('=') + 1);
                 } else if (arg.startsWith("--app-data-dir=")) {
                     appDataDir = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.equals("--preload-app")) {
+                    mPreloadApp = args[++curArg];
                 } else if (arg.equals("--preload-package")) {
                     preloadPackage = args[++curArg];
                     preloadPackageLibs = args[++curArg];
@@ -714,6 +748,11 @@
                     throw new IllegalArgumentException(
                             "Unexpected arguments after --preload-package.");
                 }
+            } else if (mPreloadApp != null) {
+                if (args.length - curArg > 0) {
+                    throw new IllegalArgumentException(
+                            "Unexpected arguments after --preload-app.");
+                }
             } else if (expectRuntimeArgs) {
                 if (!seenRuntimeArgs) {
                     throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 6576587..6a4b633 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -916,6 +916,17 @@
     return reinterpret_cast<jlong>(surface.get());
 }
 
+static jlong nativeCopyFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) {
+    sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
+    if (surface == nullptr) {
+        return 0;
+    }
+
+    sp<SurfaceControl> newSurface = new SurfaceControl(surface);
+    newSurface->incStrong((void *)nativeCreate);
+    return reinterpret_cast<jlong>(newSurface.get());
+}
+
 static void nativeWriteToParcel(JNIEnv* env, jclass clazz,
         jlong nativeObject, jobject parcelObj) {
     Parcel* parcel = parcelForJavaObject(env, parcelObj);
@@ -924,7 +935,9 @@
         return;
     }
     SurfaceControl* const self = reinterpret_cast<SurfaceControl *>(nativeObject);
-    self->writeToParcel(parcel);
+    if (self != nullptr) {
+        self->writeToParcel(parcel);
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -934,6 +947,8 @@
             (void*)nativeCreate },
     {"nativeReadFromParcel", "(Landroid/os/Parcel;)J",
             (void*)nativeReadFromParcel },
+    {"nativeCopyFromSurfaceControl", "(J)J" ,
+            (void*)nativeCopyFromSurfaceControl },
     {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
             (void*)nativeWriteToParcel },
     {"nativeRelease", "(J)V",
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 30c0030..191f748 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -46,13 +46,6 @@
     return reinterpret_cast<jlong>(client);
 }
 
-static jlong nativeCreateScoped(JNIEnv* env, jclass clazz, jlong surfaceObject) {
-    Surface *parent = reinterpret_cast<Surface*>(surfaceObject);
-    SurfaceComposerClient* client = new SurfaceComposerClient(parent->getIGraphicBufferProducer());
-    client->incStrong((void*)nativeCreate);
-    return reinterpret_cast<jlong>(client);
-}
-
 static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
     SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
     client->decStrong((void*)nativeCreate);
@@ -67,8 +60,6 @@
     /* name, signature, funcPtr */
     { "nativeCreate", "()J",
             (void*)nativeCreate },
-    { "nativeCreateScoped", "(J)J",
-            (void*)nativeCreateScoped },
     { "nativeDestroy", "(J)V",
             (void*)nativeDestroy },
     { "nativeKill", "(J)V",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b16c16d..0778304 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3379,6 +3379,11 @@
     <permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
          android:protectionLevel="signature|installer|verifier" />
 
+    <!-- @SystemApi Allows the system to read runtime permission state.
+        @hide -->
+    <permission android:name="android.permission.GET_RUNTIME_PERMISSIONS"
+                android:protectionLevel="signature" />
+
     <!-- @hide Allows an application to observe permission changes. -->
     <permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
         android:protectionLevel="signature|privileged" />
@@ -3854,6 +3859,10 @@
     <permission android:name="android.permission.PACKAGE_ROLLBACK_AGENT"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi @hide Allows managing apk level rollbacks. -->
+    <permission android:name="android.permission.MANAGE_ROLLBACKS"
+        android:protectionLevel="signature|installer" />
+
     <!-- @SystemApi @hide Allows an application to mark other applications as harmful -->
     <permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS"
         android:protectionLevel="signature|verifier" />
diff --git a/core/res/res/drawable/ic_account_circle.xml b/core/res/res/drawable/ic_account_circle.xml
index f7317db..71691ad 100644
--- a/core/res/res/drawable/ic_account_circle.xml
+++ b/core/res/res/drawable/ic_account_circle.xml
@@ -14,18 +14,14 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="48.0dp"
-        android:height="48.0dp"
-        android:viewportWidth="20.0"
-        android:viewportHeight="20.0">
-    <group
-        android:translateX="-2"
-        android:translateY="-2" >
-        <path
-            android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33C4.62,15.49 4,13.82 4,12c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,13.82 19.38,15.49 18.36,16.83z"
-            android:fillColor="#FFFFFFFF" />
-        <path
-            android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13c1.94,0 3.5,-1.56 3.5,-3.5S13.94,6 12,6z"
-            android:fillColor="#FFFFFFFF" />
-    </group>
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33C4.62,15.49 4,13.82 4,12c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,13.82 19.38,15.49 18.36,16.83z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13c1.94,0 3.5,-1.56 3.5,-3.5S13.94,6 12,6z"/>
 </vector>
diff --git a/core/res/res/drawable/ic_battery.xml b/core/res/res/drawable/ic_battery.xml
new file mode 100644
index 0000000..bd40f4d
--- /dev/null
+++ b/core/res/res/drawable/ic_battery.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2018 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
index 1dad977..5ab5045 100644
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ b/core/res/res/drawable/ic_corp_badge.xml
@@ -15,22 +15,11 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="20dp"
-        android:height="20dp"
-        android:viewportWidth="48"
-        android:viewportHeight="48">
-
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
     <path
-        android:fillType="evenOdd"
-        android:strokeColor="#1A73E8"
-        android:strokeWidth="4"
-        android:pathData="M 24 2 C 36.1502644963 2 46 11.8497355037 46 24 C 46 36.1502644963 36.1502644963 46 24 46 C 11.8497355037 46 2 36.1502644963 2 24 C 2 11.8497355037 11.8497355037 2 24 2 Z" />
-    <group
-        android:translateX="10.400000"
-        android:translateY="10.400000">
-        <path
-            android:fillColor="#1A73E8"
-            android:strokeWidth="1"
-            android:pathData="M24.2971429,5.38947368 L18.9485714,5.38947368 L18.9485714,2.80902256 C18.9485714,1.37687218 17.7585143,0.228571429 16.2742857,0.228571429 L10.9257143,0.228571429 C9.44148571,0.228571429 8.25142857,1.37687218 8.25142857,2.80902256 L8.25142857,5.38947368 L2.90285714,5.38947368 C1.41862857,5.38947368 0.241942857,6.53777444 0.241942857,7.96992481 L0.228571429,22.162406 C0.228571429,23.5945564 1.41862857,24.7428571 2.90285714,24.7428571 L24.2971429,24.7428571 C25.7813714,24.7428571 26.9714286,23.5945564 26.9714286,22.162406 L26.9714286,7.96992481 C26.9714286,6.53777444 25.7813714,5.38947368 24.2971429,5.38947368 Z M13.6,17.0015038 C12.1291429,17.0015038 10.9257143,15.8403008 10.9257143,14.4210526 C10.9257143,13.0018045 12.1291429,11.8406015 13.6,11.8406015 C15.0708571,11.8406015 16.2742857,13.0018045 16.2742857,14.4210526 C16.2742857,15.8403008 15.0708571,17.0015038 13.6,17.0015038 Z M16.2742857,5.38947368 L10.9257143,5.38947368 L10.9257143,2.80902256 L16.2742857,2.80902256 L16.2742857,5.38947368 Z" />
-    </group>
+        android:fillColor="@*android:color/accent_device_default_light"
+        android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4C8.89,2 8,2.89 8,4v2H4C2.89,6 2.01,6.89 2.01,8L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8C22,6.89 21.11,6 20,6zM10,4h4v2h-4V4zM20,19H4V8h16V19z"/>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/values-land/dimens_permission_controller.xml b/core/res/res/values-land/dimens_permission_controller.xml
deleted file mode 100644
index 2146241..0000000
--- a/core/res/res/values-land/dimens_permission_controller.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
-  -->
-
-<!-- Landscape dimensions for the permission grant dialog. -->
-<resources>
-    <!-- Assuming the dimension of a sailfish, this yields 95% width in splitscreen and 65% in
-         landscape -->
-    <dimen name="permissionGrantDialogWeight">8.6</dimen>
-    <dimen name="permissionGrantDialogWidth">334dp</dimen>
-</resources>
diff --git a/core/res/res/values-night/themes_permission_controller.xml b/core/res/res/values-night/themes_permission_controller.xml
deleted file mode 100644
index a071927..0000000
--- a/core/res/res/values-night/themes_permission_controller.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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.
-  -->
-
-<!-- themes for the permission grant dialog. -->
-<resources>
-    <style name="Theme.DeviceDefault.PermissionGrantApp"
-           parent="@style/Theme.DeviceDefault.Panel">
-        <item name="windowIsFloating">false</item>
-        <item name="windowTranslucentStatus">true</item>
-        <item name="backgroundDimEnabled">true</item>
-        <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
-    </style>
-
-    <style name="Theme.DeviceDefault.PermissionGrant"
-           parent="@style/Theme.DeviceDefault.Dialog">
-        <item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
-    </style>
-</resources>
diff --git a/core/res/res/values-port/dimens_permission_controller.xml b/core/res/res/values-port/dimens_permission_controller.xml
deleted file mode 100644
index af28713..0000000
--- a/core/res/res/values-port/dimens_permission_controller.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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.
-  -->
-
-<!-- portrait dimensions for the permission grant dialog. -->
-<resources>
-    <!-- This yields 95% width -->
-    <dimen name="permissionGrantDialogWeight">380</dimen>
-    <dimen name="permissionGrantDialogWidth">0dp</dimen>
-</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2ba3177..fa3a549 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3760,6 +3760,8 @@
         <!-- Component name of an activity that allows the user to modify
              the settings for this service. -->
         <attr name="settingsActivity"/>
+        <!-- Secure Element which the AIDs should be routed to -->
+        <attr name="secureElementName"/>
     </declare-styleable>
 
     <!-- Specify one or more <code>aid-group</code> elements inside a
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e4abf8f..8735557 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1169,7 +1169,7 @@
     <integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
 
     <!-- The app which will handle routine based automatic battery saver, if empty the UI for
-             routine based battery saver will be hidden -->
+         routine based battery saver will be hidden -->
     <string name="config_batterySaverScheduleProvider"></string>
 
     <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2b95dd0..0878562 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4436,7 +4436,8 @@
         <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> off</string>
 
     <!-- Text spoken when accessibility shortcut warning dialog is shown. [CHAR LIMIT=none] -->
-    <string name="accessibility_shortcut_spoken_feedback">Use Accessibility Shortcut again to start the current accessibility feature</string>
+    <string name="accessibility_shortcut_spoken_feedback">Press and hold both volume keys for three seconds to use
+        <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g></string>
 
     <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. -->
     <string name="accessibility_button_prompt_text">Choose a feature to use when you tap the Accessibility button:</string>
@@ -5237,6 +5238,15 @@
     <!-- Content description of the overlay icon in the notification. [CHAR LIMIT=NONE] -->
     <string name="notification_appops_overlay_active">displaying over other apps on your screen</string>
 
+    <!-- Dynamic mode battery saver strings -->
+    <!-- The user visible name of the notification channel for the routine mode battery saver fyi notification [CHAR_LIMIT=80]-->
+    <string name="dynamic_mode_notification_channel_name">Routine Mode info notification</string>
+    <!-- Title of notification letting users know why battery saver was turned on automatically [CHAR_LIMIT=NONE]-->
+    <string name="dynamic_mode_notification_title">Battery may run out before usual charge</string>
+    <!-- Summary of notification letting users know why battery saver was turned on automatically [CHAR_LIMIT=NONE]-->
+    <string name="dynamic_mode_notification_summary">Battery Saver activated to extend battery life</string>
+
+
     <!-- Strings for car -->
     <!-- String displayed when loading a user in the car [CHAR LIMIT=30] -->
     <string name="car_loading_profile">Loading</string>
diff --git a/core/res/res/values/styles_permission_controller.xml b/core/res/res/values/styles_permission_controller.xml
deleted file mode 100644
index 5a9d3e6..0000000
--- a/core/res/res/values/styles_permission_controller.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?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>
-    <!-- styles for the permission grant dialog. -->
-    <style name="PermissionGrantDialog">
-        <item name="background">?attr/windowBackground</item>
-        <item name="elevation">?attr/windowElevation</item>
-        <item name="layout_weight">@dimen/permissionGrantDialogWeight</item>
-        <item name="layout_width">@dimen/permissionGrantDialogWidth</item>
-    </style>
-
-    <style name="PermissionGrantTitleIcon">
-        <item name="layout_width">24dp</item>
-        <item name="layout_height">24dp</item>
-        <item name="layout_marginBottom">12dp</item>
-        <item name="tint">?attr/colorAccent</item>
-        <item name="scaleType">fitCenter</item>
-    </style>
-
-    <style name="PermissionGrantTitleMessage"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="gravity">center</item>
-        <item name="textSize">20sp</item>
-        <item name="textColor">?attr/textColorPrimary</item>
-    </style>
-
-    <style name="PermissionGrantIndex"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="paddingEnd">12dp</item>
-        <item name="singleLine">true</item>
-        <item name="textColor">?attr/textColorSecondary</item>
-    </style>
-
-    <style name="PermissionGrantDescription">
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginEnd">24dp</item>
-    </style>
-
-    <style name="PermissionGrantContent">
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginEnd">24dp</item>
-    </style>
-
-    <style name="PermissionGrantDetailMessage"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="layout_marginTop">18dp</item>
-        <item name="textColor">?attr/textColorPrimary</item>
-        <item name="textSize">16sp</item>
-    </style>
-
-    <!-- styles for the permission review screen. -->
-    <style name="PermissionReviewDescription">
-        <item name="layout_marginTop">20dp</item>
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginBottom">16dp</item>
-        <item name="layout_marginEnd">24dp</item>
-    </style>
-
-    <style name="PermissionReviewTitleIcon">
-        <item name="layout_marginTop">4dp</item>
-        <item name="layout_width">36dp</item>
-        <item name="layout_height">36dp</item>
-        <item name="scaleType">fitCenter</item>
-    </style>
-
-    <style name="PermissionReviewTitleMessage"
-           parent="@style/TextAppearance.DeviceDefault">
-        <item name="paddingStart">22dp</item>
-        <item name="textSize">20sp</item>
-        <item name="textColor">?attr/textColorPrimary</item>
-    </style>
-
-    <style name="PermissionReviewSettings">
-        <item name="layout_marginStart">8dp</item>
-        <item name="layout_marginEnd">8dp</item>
-    </style>
-
-    <style name="PermissionReviewButtonBar">
-        <item name="layout_marginStart">24dp</item>
-        <item name="layout_marginEnd">16dp</item>
-        <item name="layout_marginBottom">4dp</item>
-    </style>
-</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e8cbf66..c2d97bc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3519,4 +3519,9 @@
 
   <!-- For Secondary Launcher -->
   <java-symbol type="string" name="config_secondaryHomeComponent" />
+  
+  <java-symbol type="string" name="dynamic_mode_notification_channel_name" />
+  <java-symbol type="string" name="dynamic_mode_notification_title" />
+  <java-symbol type="string" name="dynamic_mode_notification_summary" />
+  <java-symbol type="drawable" name="ic_battery" />
 </resources>
diff --git a/core/res/res/values/themes_permission_controller.xml b/core/res/res/values/themes_permission_controller.xml
deleted file mode 100644
index 205c4eb..0000000
--- a/core/res/res/values/themes_permission_controller.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?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>
-    <!-- themes for the permission grant dialog. -->
-    <style name="Theme.DeviceDefault.PermissionGrantApp"
-           parent="@style/Theme.DeviceDefault.Light.Panel">
-        <item name="windowIsFloating">false</item>
-        <item name="windowTranslucentStatus">true</item>
-        <item name="backgroundDimEnabled">true</item>
-        <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
-    </style>
-
-    <style name="Theme.DeviceDefault.PermissionGrant"
-           parent="@style/Theme.DeviceDefault.Light.Dialog">
-        <item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
-    </style>
-
-    <!-- themes for the permission review dialog. -->
-    <style name="Theme.DeviceDefault.PermissionReviewApp"
-           parent="@style/Theme.DeviceDefault.Settings">
-        <item name="windowActionBar">false</item>
-        <item name="windowNoTitle">true</item>
-        <item name="titleTextStyle">@style/PermissionReviewTitleMessage</item>
-    </style>
-</resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index ac57d20..68b08f9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -269,6 +269,7 @@
                     Settings.Global.GNSS_SATELLITE_BLACKLIST,
                     Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
                     Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
+                    Settings.Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED,
                     Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
                     Settings.Global.HDMI_CONTROL_ENABLED,
                     Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 6a40792..04bf804 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -312,9 +312,8 @@
         <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
     </family>
     <family lang="und-Mymr" variant="elegant">
-        <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font>
-        <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font>
-        <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font>
+        <font weight="400" style="normal">NotoSansMyanmar-Regular-ZawDecode.ttf</font>
+        <font weight="700" style="normal">NotoSansMyanmar-Bold-ZawDecode.ttf</font>
         <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
         <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
     </family>
diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk
index bb8add1..c6c7d3b 100644
--- a/data/sounds/AllAudio.mk
+++ b/data/sounds/AllAudio.mk
@@ -15,227 +15,227 @@
 LOCAL_PATH := frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-    $(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-    $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-    $(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-    $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-    $(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Barium.ogg:system/media/audio/alarms/Barium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Promethium.ogg:system/media/audio/alarms/Promethium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Scandium.ogg:system/media/audio/alarms/Scandium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-    $(LOCAL_PATH)/notifications/Aldebaran.ogg:system/media/audio/notifications/Aldebaran.ogg \
-    $(LOCAL_PATH)/notifications/Altair.ogg:system/media/audio/notifications/Altair.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-    $(LOCAL_PATH)/notifications/Antares.ogg:system/media/audio/notifications/Antares.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Antimony.ogg:system/media/audio/notifications/Antimony.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:system/media/audio/notifications/Argon.ogg \
-    $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:system/media/audio/notifications/Beryllium.ogg \
-    $(LOCAL_PATH)/notifications/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
-    $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg \
-    $(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-    $(LOCAL_PATH)/notifications/Castor.ogg:system/media/audio/notifications/Castor.ogg \
-    $(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:system/media/audio/notifications/Cobalt.ogg \
-    $(LOCAL_PATH)/notifications/Cricket.ogg:system/media/audio/notifications/Cricket.ogg \
-    $(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-    $(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
-    $(LOCAL_PATH)/notifications/Doink.ogg:system/media/audio/notifications/Doink.ogg \
-    $(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-    $(LOCAL_PATH)/notifications/Drip.ogg:system/media/audio/notifications/Drip.ogg \
-    $(LOCAL_PATH)/notifications/Electra.ogg:system/media/audio/notifications/Electra.ogg \
-    $(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-    $(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-    $(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:system/media/audio/notifications/Fluorine.ogg \
-    $(LOCAL_PATH)/notifications/Fomalhaut.ogg:system/media/audio/notifications/Fomalhaut.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Gallium.ogg:system/media/audio/notifications/Gallium.ogg \
-    $(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Helium.ogg:system/media/audio/notifications/Helium.ogg \
-    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:system/media/audio/notifications/Iridium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Krypton.ogg:system/media/audio/notifications/Krypton.ogg \
-    $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-    $(LOCAL_PATH)/notifications/Merope.ogg:system/media/audio/notifications/Merope.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-    $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Palladium.ogg:system/media/audio/notifications/Palladium.ogg \
-    $(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:system/media/audio/notifications/Plastic_Pipe.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Radon.ogg:system/media/audio/notifications/Radon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:system/media/audio/notifications/Rubidium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Selenium.ogg:system/media/audio/notifications/Selenium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-    $(LOCAL_PATH)/notifications/Sirrah.ogg:system/media/audio/notifications/Sirrah.ogg \
-    $(LOCAL_PATH)/notifications/SpaceSeed.ogg:system/media/audio/notifications/SpaceSeed.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Strontium.ogg:system/media/audio/notifications/Strontium.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-    $(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Thallium.ogg:system/media/audio/notifications/Thallium.ogg \
-    $(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-    $(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Xenon.ogg:system/media/audio/notifications/Xenon.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:system/media/audio/notifications/Zirconium.ogg \
-    $(LOCAL_PATH)/notifications/arcturus.ogg:system/media/audio/notifications/arcturus.ogg \
-    $(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-    $(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-    $(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-    $(LOCAL_PATH)/notifications/regulus.ogg:system/media/audio/notifications/regulus.ogg \
-    $(LOCAL_PATH)/notifications/sirius.ogg:system/media/audio/notifications/sirius.ogg \
-    $(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-    $(LOCAL_PATH)/notifications/vega.ogg:system/media/audio/notifications/vega.ogg \
-    $(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:system/media/audio/ringtones/ANDROMEDA.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Atria.ogg:system/media/audio/ringtones/Atria.ogg \
-    $(LOCAL_PATH)/ringtones/BOOTES.ogg:system/media/audio/ringtones/BOOTES.ogg \
-    $(LOCAL_PATH)/newwavelabs/Backroad.ogg:system/media/audio/ringtones/Backroad.ogg \
-    $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-    $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-    $(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-    $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-    $(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-    $(LOCAL_PATH)/newwavelabs/BussaMove.ogg:system/media/audio/ringtones/BussaMove.ogg \
-    $(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:system/media/audio/ringtones/CANISMAJOR.ogg \
-    $(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:system/media/audio/ringtones/CASSIOPEIA.ogg \
-    $(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-    $(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-    $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-    $(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-    $(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-    $(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:system/media/audio/ringtones/CrayonRock.ogg \
-    $(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
-    $(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-    $(LOCAL_PATH)/newwavelabs/DancinFool.ogg:system/media/audio/ringtones/DancinFool.ogg \
-    $(LOCAL_PATH)/newwavelabs/Ding.ogg:system/media/audio/ringtones/Ding.ogg \
-    $(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:system/media/audio/ringtones/DonMessWivIt.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-    $(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \
-    $(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-    $(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:system/media/audio/ringtones/Enter_the_Nexus.ogg \
-    $(LOCAL_PATH)/ringtones/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \
-    $(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-    $(LOCAL_PATH)/ringtones/FreeFlight.ogg:system/media/audio/ringtones/FreeFlight.ogg \
-    $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
-    $(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-    $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
-    $(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-    $(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-    $(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-    $(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:system/media/audio/ringtones/HalfwayHome.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-    $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-    $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-    $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-    $(LOCAL_PATH)/ringtones/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-    $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-    $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-    $(LOCAL_PATH)/newwavelabs/Nairobi.ogg:system/media/audio/ringtones/Nairobi.ogg \
-    $(LOCAL_PATH)/newwavelabs/Nassau.ogg:system/media/audio/ringtones/Nassau.ogg \
-    $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-    $(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-    $(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-    $(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-    $(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-    $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-    $(LOCAL_PATH)/ringtones/PERSEUS.ogg:system/media/audio/ringtones/PERSEUS.ogg \
-    $(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-    $(LOCAL_PATH)/newwavelabs/Playa.ogg:system/media/audio/ringtones/Playa.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:system/media/audio/ringtones/Rasalas.ogg \
-    $(LOCAL_PATH)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-    $(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-    $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-    $(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-    $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
-    $(LOCAL_PATH)/newwavelabs/Safari.ogg:system/media/audio/ringtones/Safari.ogg \
-    $(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-    $(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-    $(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-    $(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:system/media/audio/ringtones/SilkyWay.ogg \
-    $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-    $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
-    $(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-    $(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-    $(LOCAL_PATH)/ringtones/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-    $(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-    $(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \
-    $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-    $(LOCAL_PATH)/ringtones/URSAMINOR.ogg:system/media/audio/ringtones/URSAMINOR.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-    $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
-    $(LOCAL_PATH)/ringtones/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg \
-    $(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg \
-    $(LOCAL_PATH)/ringtones/hydra.ogg:system/media/audio/ringtones/hydra.ogg \
-    $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-    $(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-    $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-    $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:system/media/audio/ui/Trusted.ogg \
-    $(LOCAL_PATH)/effects/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
-    $(LOCAL_PATH)/effects/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
-    $(LOCAL_PATH)/effects/ogg/WirelessChargingStarted.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
-    $(LOCAL_PATH)/effects/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
-    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-    $(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:system/media/audio/ui/ChargingStarted.ogg \
-    $(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:system/media/audio/ui/InCallNotification.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCFailure.ogg:system/media/audio/ui/NFCFailure.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCInitiated.ogg:system/media/audio/ui/NFCInitiated.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCSuccess.ogg:system/media/audio/ui/NFCSuccess.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCTransferComplete.ogg:system/media/audio/ui/NFCTransferComplete.ogg \
-    $(LOCAL_PATH)/effects/ogg/NFCTransferInitiated.ogg:system/media/audio/ui/NFCTransferInitiated.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+    $(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+    $(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+    $(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Barium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Barium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Fermium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Fermium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Hassium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Hassium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neptunium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Nobelium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Promethium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Promethium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Scandium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Scandium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+    $(LOCAL_PATH)/notifications/Aldebaran.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Aldebaran.ogg \
+    $(LOCAL_PATH)/notifications/Altair.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Altair.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+    $(LOCAL_PATH)/notifications/Antares.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antares.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Antimony.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antimony.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Argon.ogg \
+    $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beryllium.ogg \
+    $(LOCAL_PATH)/notifications/Betelgeuse.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Betelgeuse.ogg \
+    $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg \
+    $(LOCAL_PATH)/notifications/Canopus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Canopus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+    $(LOCAL_PATH)/notifications/Castor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Castor.ogg \
+    $(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cobalt.ogg \
+    $(LOCAL_PATH)/notifications/Cricket.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cricket.ogg \
+    $(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+    $(LOCAL_PATH)/notifications/Deneb.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Deneb.ogg \
+    $(LOCAL_PATH)/notifications/Doink.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Doink.ogg \
+    $(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+    $(LOCAL_PATH)/notifications/Drip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Drip.ogg \
+    $(LOCAL_PATH)/notifications/Electra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Electra.ogg \
+    $(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+    $(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+    $(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fluorine.ogg \
+    $(LOCAL_PATH)/notifications/Fomalhaut.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fomalhaut.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Gallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Gallium.ogg \
+    $(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Helium.ogg \
+    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Iridium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Krypton.ogg \
+    $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+    $(LOCAL_PATH)/notifications/Merope.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Merope.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+    $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Palladium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Palladium.ogg \
+    $(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Plastic_Pipe.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Radon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Radon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Rubidium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Selenium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Selenium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+    $(LOCAL_PATH)/notifications/Sirrah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Sirrah.ogg \
+    $(LOCAL_PATH)/notifications/SpaceSeed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/SpaceSeed.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Strontium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Strontium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+    $(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Thallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Thallium.ogg \
+    $(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+    $(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Xenon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Xenon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Zirconium.ogg \
+    $(LOCAL_PATH)/notifications/arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/arcturus.ogg \
+    $(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+    $(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+    $(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+    $(LOCAL_PATH)/notifications/regulus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/regulus.ogg \
+    $(LOCAL_PATH)/notifications/sirius.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/sirius.ogg \
+    $(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+    $(LOCAL_PATH)/notifications/vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/vega.ogg \
+    $(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ANDROMEDA.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Atria.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Atria.ogg \
+    $(LOCAL_PATH)/ringtones/BOOTES.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BOOTES.ogg \
+    $(LOCAL_PATH)/newwavelabs/Backroad.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Backroad.ogg \
+    $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+    $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+    $(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+    $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+    $(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+    $(LOCAL_PATH)/newwavelabs/BussaMove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BussaMove.ogg \
+    $(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CANISMAJOR.ogg \
+    $(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CASSIOPEIA.ogg \
+    $(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+    $(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+    $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CaribbeanIce.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+    $(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+    $(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+    $(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrayonRock.ogg \
+    $(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrazyDream.ogg \
+    $(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+    $(LOCAL_PATH)/newwavelabs/DancinFool.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DancinFool.ogg \
+    $(LOCAL_PATH)/newwavelabs/Ding.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ding.ogg \
+    $(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DonMessWivIt.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+    $(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DreamTheme.ogg \
+    $(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+    $(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Enter_the_Nexus.ogg \
+    $(LOCAL_PATH)/ringtones/Eridani.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eridani.ogg \
+    $(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+    $(LOCAL_PATH)/ringtones/FreeFlight.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FreeFlight.ogg \
+    $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FriendlyGhost.ogg \
+    $(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+    $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/GameOverGuitar.ogg \
+    $(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+    $(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+    $(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+    $(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/HalfwayHome.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+    $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/InsertCoin.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+    $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+    $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+    $(LOCAL_PATH)/ringtones/Lyra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Lyra.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+    $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+    $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+    $(LOCAL_PATH)/newwavelabs/Nairobi.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nairobi.ogg \
+    $(LOCAL_PATH)/newwavelabs/Nassau.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nassau.ogg \
+    $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+    $(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+    $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+    $(LOCAL_PATH)/ringtones/PERSEUS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/PERSEUS.ogg \
+    $(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+    $(LOCAL_PATH)/newwavelabs/Playa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Playa.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rasalas.ogg \
+    $(LOCAL_PATH)/newwavelabs/Revelation.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Revelation.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+    $(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+    $(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+    $(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+    $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/RomancingTheTone.ogg \
+    $(LOCAL_PATH)/newwavelabs/Safari.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Safari.ogg \
+    $(LOCAL_PATH)/newwavelabs/Savannah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Savannah.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+    $(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+    $(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+    $(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SilkyWay.ogg \
+    $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SitarVsSitar.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+    $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SpringyJalopy.ogg \
+    $(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+    $(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+    $(LOCAL_PATH)/ringtones/Testudo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Testudo.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+    $(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+    $(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Thunderfoot.ogg \
+    $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+    $(LOCAL_PATH)/ringtones/URSAMINOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/URSAMINOR.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+    $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/VeryAlarmed.ogg \
+    $(LOCAL_PATH)/ringtones/Vespa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Vespa.ogg \
+    $(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg \
+    $(LOCAL_PATH)/ringtones/hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/hydra.ogg \
+    $(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+    $(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+    $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+    $(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+    $(LOCAL_PATH)/effects/ogg/VideoRecord_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+    $(LOCAL_PATH)/effects/ogg/VideoStop_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+    $(LOCAL_PATH)/effects/ogg/WirelessChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
+    $(LOCAL_PATH)/effects/ogg/camera_click_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+    $(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \
+    $(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCFailure.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCFailure.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCInitiated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCInitiated.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCSuccess.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCSuccess.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCTransferComplete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCTransferComplete.ogg \
+    $(LOCAL_PATH)/effects/ogg/NFCTransferInitiated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/NFCTransferInitiated.ogg \
diff --git a/data/sounds/AudioPackage10.mk b/data/sounds/AudioPackage10.mk
index 72aa7fe..699dbd6 100644
--- a/data/sounds/AudioPackage10.mk
+++ b/data/sounds/AudioPackage10.mk
@@ -8,63 +8,63 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:system/media/audio/ui/ChargingStarted.ogg \
-	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:system/media/audio/ui/InCallNotification.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:system/media/audio/ringtones/Atria.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:system/media/audio/ringtones/Rasalas.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \
+	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Atria.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rasalas.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage11.mk b/data/sounds/AudioPackage11.mk
index 665ce52..99dfd0a 100644
--- a/data/sounds/AudioPackage11.mk
+++ b/data/sounds/AudioPackage11.mk
@@ -8,63 +8,63 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:system/media/audio/ui/ChargingStarted.ogg \.
-	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:system/media/audio/ui/InCallNotification.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat_proc48.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:system/media/audio/ringtones/Atria.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:system/media/audio/ringtones/Rasalas.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/ChargingStarted.ogg \.
+	$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/InCallNotification.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/WirelessChargingStarted_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/WirelessChargingStarted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat_proc48.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Atria.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Atria.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rasalas.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage12.mk b/data/sounds/AudioPackage12.mk
index 44a8f9e..6159a89 100644
--- a/data/sounds/AudioPackage12.mk
+++ b/data/sounds/AudioPackage12.mk
@@ -16,15 +16,15 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord LowBattery WirelessChargingStarted VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/ogg/$(fn).ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/ogg/$(fn).ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
diff --git a/data/sounds/AudioPackage12_48.mk b/data/sounds/AudioPackage12_48.mk
index 09fab04..2899cd1 100644
--- a/data/sounds/AudioPackage12_48.mk
+++ b/data/sounds/AudioPackage12_48.mk
@@ -17,21 +17,21 @@
 
 # Alarms not yet available in 48 kHz
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/ogg/$(fn)_48k.ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/ogg/$(fn)_48k.ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 
 # no gold-plated version yet
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-    $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg
+    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+    $(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg
diff --git a/data/sounds/AudioPackage13.mk b/data/sounds/AudioPackage13.mk
index de4ee04..9423c0b 100644
--- a/data/sounds/AudioPackage13.mk
+++ b/data/sounds/AudioPackage13.mk
@@ -17,15 +17,15 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
diff --git a/data/sounds/AudioPackage13_48.mk b/data/sounds/AudioPackage13_48.mk
index 889d581..806c4e2 100644
--- a/data/sounds/AudioPackage13_48.mk
+++ b/data/sounds/AudioPackage13_48.mk
@@ -17,21 +17,21 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/material/ogg/$(fn)_48k.ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/material/ogg/$(fn)_48k.ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/material/ogg/$(fn)_48k.ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn)_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 
 # no gold-plated version yet
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-    $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-    $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg
+    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+    $(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg
diff --git a/data/sounds/AudioPackage14.mk b/data/sounds/AudioPackage14.mk
index c903a2b..3d161aa 100644
--- a/data/sounds/AudioPackage14.mk
+++ b/data/sounds/AudioPackage14.mk
@@ -18,15 +18,15 @@
 MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
 
 PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
-	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+	$(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
-	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:system/media/audio/notifications/$(fn).ogg)
+	$(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
-	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:system/media/audio/ringtones/$(fn).ogg)
+	$(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/$(fn).ogg)
 
 PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
 PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
-	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+	$(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/$(fn).ogg)
diff --git a/data/sounds/AudioPackage2.mk b/data/sounds/AudioPackage2.mk
index 40319c4..bc4e8fb 100644
--- a/data/sounds/AudioPackage2.mk
+++ b/data/sounds/AudioPackage2.mk
@@ -10,98 +10,98 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-	$(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-	$(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-	$(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+	$(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+	$(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+	$(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
-	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
-	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
-	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \
-	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \
-	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-	$(LOCAL_PATH)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \
-	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg
+	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CaribbeanIce.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FriendlyGhost.ogg \
+	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/GameOverGuitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/InsertCoin.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/RomancingTheTone.ogg \
+	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SitarVsSitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SpringyJalopy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/VeryAlarmed.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrazyDream.ogg \
+	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DreamTheme.ogg \
+	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Savannah.ogg \
+	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+	$(LOCAL_PATH)/newwavelabs/Revelation.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Revelation.ogg \
+	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Thunderfoot.ogg
 endif
diff --git a/data/sounds/AudioPackage3.mk b/data/sounds/AudioPackage3.mk
index a05de72..a98fb74 100644
--- a/data/sounds/AudioPackage3.mk
+++ b/data/sounds/AudioPackage3.mk
@@ -10,94 +10,94 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-	$(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-	$(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-	$(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+	$(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+	$(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+	$(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \
-	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \
-	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:system/media/audio/ringtones/HalfwayHome.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:system/media/audio/ringtones/CrayonRock.ogg \
-	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:system/media/audio/ringtones/DancinFool.ogg \
-	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:system/media/audio/ringtones/BussaMove.ogg \
-	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:system/media/audio/ringtones/DonMessWivIt.ogg \
-	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:system/media/audio/ringtones/SilkyWay.ogg \
-	$(LOCAL_PATH)/newwavelabs/Playa.ogg:system/media/audio/ringtones/Playa.ogg
+	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+	$(LOCAL_PATH)/newwavelabs/Savannah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Savannah.ogg \
+	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+	$(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Thunderfoot.ogg \
+	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/HalfwayHome.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrayonRock.ogg \
+	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DancinFool.ogg \
+	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BussaMove.ogg \
+	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DonMessWivIt.ogg \
+	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SilkyWay.ogg \
+	$(LOCAL_PATH)/newwavelabs/Playa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Playa.ogg
 endif
diff --git a/data/sounds/AudioPackage4.mk b/data/sounds/AudioPackage4.mk
index d376a2d..54c3c02 100644
--- a/data/sounds/AudioPackage4.mk
+++ b/data/sounds/AudioPackage4.mk
@@ -10,98 +10,98 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Cricket.ogg:system/media/audio/notifications/Cricket.ogg \
-	$(LOCAL_PATH)/notifications/Doink.ogg:system/media/audio/notifications/Doink.ogg \
-	$(LOCAL_PATH)/notifications/Drip.ogg:system/media/audio/notifications/Drip.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/SpaceSeed.ogg:system/media/audio/notifications/SpaceSeed.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
-	$(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-	$(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
-	$(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:system/media/audio/notifications/Plastic_Pipe.ogg \
-	$(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/ringtones/FreeFlight.ogg:system/media/audio/ringtones/FreeFlight.ogg \
-	$(LOCAL_PATH)/newwavelabs/Backroad.ogg:system/media/audio/ringtones/Backroad.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Cricket.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cricket.ogg \
+	$(LOCAL_PATH)/notifications/Doink.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Doink.ogg \
+	$(LOCAL_PATH)/notifications/Drip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Drip.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/SpaceSeed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/SpaceSeed.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/notifications/moonbeam.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/moonbeam.ogg \
+	$(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+	$(LOCAL_PATH)/notifications/pizzicato.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pizzicato.ogg \
+	$(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Plastic_Pipe.ogg \
+	$(LOCAL_PATH)/notifications/tweeters.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/tweeters.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/ringtones/FreeFlight.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FreeFlight.ogg \
+	$(LOCAL_PATH)/newwavelabs/Backroad.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Backroad.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
-	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:system/media/audio/ringtones/BussaMove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
-	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
-	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
-	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:system/media/audio/ringtones/CrayonRock.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:system/media/audio/ringtones/DancinFool.ogg \
-	$(LOCAL_PATH)/newwavelabs/Ding.ogg:system/media/audio/ringtones/Ding.ogg \
-	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:system/media/audio/ringtones/DonMessWivIt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
-	$(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:system/media/audio/ringtones/Enter_the_Nexus.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
-	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
-	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:system/media/audio/ringtones/HalfwayHome.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/Nairobi.ogg:system/media/audio/ringtones/Nairobi.ogg \
-	$(LOCAL_PATH)/newwavelabs/Nassau.ogg:system/media/audio/ringtones/Nassau.ogg \
-	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
-	$(LOCAL_PATH)/newwavelabs/Playa.ogg:system/media/audio/ringtones/Playa.ogg \
-	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
-	$(LOCAL_PATH)/newwavelabs/Safari.ogg:system/media/audio/ringtones/Safari.ogg \
-	$(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
-	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
-	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:system/media/audio/ringtones/SilkyWay.ogg \
-	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Big_Easy.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/Bollywood.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Bollywood.ogg \
+	$(LOCAL_PATH)/newwavelabs/BussaMove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BussaMove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Cairo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cairo.ogg \
+	$(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Calypso_Steel.ogg \
+	$(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Champagne_Edition.ogg \
+	$(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Club_Cubano.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrayonRock.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/DancinFool.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DancinFool.ogg \
+	$(LOCAL_PATH)/newwavelabs/Ding.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ding.ogg \
+	$(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DonMessWivIt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eastern_Sky.ogg \
+	$(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Enter_the_Nexus.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Funk_Yall.ogg \
+	$(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Gimme_Mo_Town.ogg \
+	$(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Glacial_Groove.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/HalfwayHome.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/Nairobi.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nairobi.ogg \
+	$(LOCAL_PATH)/newwavelabs/Nassau.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Nassau.ogg \
+	$(LOCAL_PATH)/newwavelabs/No_Limits.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/No_Limits.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Paradise_Island.ogg \
+	$(LOCAL_PATH)/newwavelabs/Playa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Playa.ogg \
+	$(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Road_Trip.ogg \
+	$(LOCAL_PATH)/newwavelabs/Safari.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Safari.ogg \
+	$(LOCAL_PATH)/newwavelabs/Seville.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Seville.ogg \
+	$(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Shes_All_That.ogg \
+	$(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SilkyWay.ogg \
+	$(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Steppin_Out.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Third_Eye.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg
 endif
diff --git a/data/sounds/AudioPackage5.mk b/data/sounds/AudioPackage5.mk
index 72384c8..8a03a2e1 100644
--- a/data/sounds/AudioPackage5.mk
+++ b/data/sounds/AudioPackage5.mk
@@ -8,69 +8,69 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/Aldebaran.ogg:system/media/audio/notifications/Aldebaran.ogg \
-	$(LOCAL_PATH)/notifications/Altair.ogg:system/media/audio/notifications/Altair.ogg \
-	$(LOCAL_PATH)/notifications/Antares.ogg:system/media/audio/notifications/Antares.ogg \
-	$(LOCAL_PATH)/notifications/arcturus.ogg:system/media/audio/notifications/arcturus.ogg \
-	$(LOCAL_PATH)/notifications/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
-	$(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
-	$(LOCAL_PATH)/notifications/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/Castor.ogg:system/media/audio/notifications/Castor.ogg \
-	$(LOCAL_PATH)/notifications/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
-	$(LOCAL_PATH)/notifications/Electra.ogg:system/media/audio/notifications/Electra.ogg \
-	$(LOCAL_PATH)/notifications/Fomalhaut.ogg:system/media/audio/notifications/Fomalhaut.ogg \
-	$(LOCAL_PATH)/notifications/Merope.ogg:system/media/audio/notifications/Merope.ogg \
-	$(LOCAL_PATH)/notifications/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/regulus.ogg:system/media/audio/notifications/regulus.ogg \
-	$(LOCAL_PATH)/notifications/sirius.ogg:system/media/audio/notifications/sirius.ogg \
-	$(LOCAL_PATH)/notifications/Sirrah.ogg:system/media/audio/notifications/Sirrah.ogg \
-	$(LOCAL_PATH)/notifications/vega.ogg:system/media/audio/notifications/vega.ogg \
-	$(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:system/media/audio/ringtones/ANDROMEDA.ogg \
-	$(LOCAL_PATH)/ringtones/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/BOOTES.ogg:system/media/audio/ringtones/BOOTES.ogg \
-	$(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:system/media/audio/ringtones/CANISMAJOR.ogg \
-	$(LOCAL_PATH)/ringtones/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:system/media/audio/ringtones/CASSIOPEIA.ogg \
-	$(LOCAL_PATH)/ringtones/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \
-	$(LOCAL_PATH)/ringtones/hydra.ogg:system/media/audio/ringtones/hydra.ogg \
-	$(LOCAL_PATH)/ringtones/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
-	$(LOCAL_PATH)/ringtones/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/PERSEUS.ogg:system/media/audio/ringtones/PERSEUS.ogg \
-	$(LOCAL_PATH)/ringtones/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
-	$(LOCAL_PATH)/ringtones/URSAMINOR.ogg:system/media/audio/ringtones/URSAMINOR.ogg \
-	$(LOCAL_PATH)/ringtones/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/Aldebaran.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Aldebaran.ogg \
+	$(LOCAL_PATH)/notifications/Altair.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Altair.ogg \
+	$(LOCAL_PATH)/notifications/Antares.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antares.ogg \
+	$(LOCAL_PATH)/notifications/arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/arcturus.ogg \
+	$(LOCAL_PATH)/notifications/Betelgeuse.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Betelgeuse.ogg \
+	$(LOCAL_PATH)/notifications/Canopus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Canopus.ogg \
+	$(LOCAL_PATH)/notifications/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/Castor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Castor.ogg \
+	$(LOCAL_PATH)/notifications/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/Deneb.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Deneb.ogg \
+	$(LOCAL_PATH)/notifications/Electra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Electra.ogg \
+	$(LOCAL_PATH)/notifications/Fomalhaut.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fomalhaut.ogg \
+	$(LOCAL_PATH)/notifications/Merope.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Merope.ogg \
+	$(LOCAL_PATH)/notifications/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/regulus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/regulus.ogg \
+	$(LOCAL_PATH)/notifications/sirius.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/sirius.ogg \
+	$(LOCAL_PATH)/notifications/Sirrah.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Sirrah.ogg \
+	$(LOCAL_PATH)/notifications/vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/vega.ogg \
+	$(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ANDROMEDA.ogg \
+	$(LOCAL_PATH)/ringtones/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/BOOTES.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BOOTES.ogg \
+	$(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CANISMAJOR.ogg \
+	$(LOCAL_PATH)/ringtones/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CASSIOPEIA.ogg \
+	$(LOCAL_PATH)/ringtones/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/Eridani.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Eridani.ogg \
+	$(LOCAL_PATH)/ringtones/hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/hydra.ogg \
+	$(LOCAL_PATH)/ringtones/Lyra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Lyra.ogg \
+	$(LOCAL_PATH)/ringtones/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/PERSEUS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/PERSEUS.ogg \
+	$(LOCAL_PATH)/ringtones/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/Testudo.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Testudo.ogg \
+	$(LOCAL_PATH)/ringtones/URSAMINOR.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/URSAMINOR.ogg \
+	$(LOCAL_PATH)/ringtones/Vespa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Vespa.ogg
diff --git a/data/sounds/AudioPackage6.mk b/data/sounds/AudioPackage6.mk
index 5413704..a778261 100644
--- a/data/sounds/AudioPackage6.mk
+++ b/data/sounds/AudioPackage6.mk
@@ -8,41 +8,41 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Barium.ogg:system/media/audio/alarms/Barium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Scandium.ogg:system/media/audio/alarms/Scandium.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Antimony.ogg:system/media/audio/notifications/Antimony.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Argon.ogg:system/media/audio/notifications/Argon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:system/media/audio/notifications/Beryllium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:system/media/audio/notifications/Cobalt.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:system/media/audio/notifications/Fluorine.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Gallium.ogg:system/media/audio/notifications/Gallium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Helium.ogg:system/media/audio/notifications/Helium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Iridium.ogg:system/media/audio/notifications/Iridium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Krypton.ogg:system/media/audio/notifications/Krypton.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Palladium.ogg:system/media/audio/notifications/Palladium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Radon.ogg:system/media/audio/notifications/Radon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:system/media/audio/notifications/Rubidium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Selenium.ogg:system/media/audio/notifications/Selenium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Strontium.ogg:system/media/audio/notifications/Strontium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Thallium.ogg:system/media/audio/notifications/Thallium.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Xenon.ogg:system/media/audio/notifications/Xenon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:system/media/audio/notifications/Zirconium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Barium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Barium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Scandium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Scandium.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Antimony.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Antimony.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Argon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beryllium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Cobalt.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Fluorine.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Gallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Gallium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Helium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Iridium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Iridium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Krypton.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Palladium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Palladium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Radon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Radon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Rubidium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Selenium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Selenium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Strontium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Strontium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Thallium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Thallium.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Xenon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Xenon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Zirconium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk
index e4763be..27e349d2 100644
--- a/data/sounds/AudioPackage7.mk
+++ b/data/sounds/AudioPackage7.mk
@@ -8,64 +8,64 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Fermium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Hassium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neptunium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Nobelium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage7alt.mk b/data/sounds/AudioPackage7alt.mk
index 30e6173..a0f4d89 100644
--- a/data/sounds/AudioPackage7alt.mk
+++ b/data/sounds/AudioPackage7alt.mk
@@ -8,63 +8,63 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg-jp/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-	$(LOCAL_PATH)/alarms/ogg-jp/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg-jp/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+	$(LOCAL_PATH)/alarms/ogg-jp/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage8.mk b/data/sounds/AudioPackage8.mk
index b38e62d..032b4d2 100644
--- a/data/sounds/AudioPackage8.mk
+++ b/data/sounds/AudioPackage8.mk
@@ -8,66 +8,66 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
-	$(LOCAL_PATH)/alarms/ogg/Promethium.ogg:system/media/audio/alarms/Promethium.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg
+	$(LOCAL_PATH)/alarms/ogg/Cesium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Cesium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Fermium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Fermium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Hassium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Hassium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neptunium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Nobelium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Plutonium.ogg \
+	$(LOCAL_PATH)/alarms/ogg/Promethium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Promethium.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Bellatrix.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Lalande.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Lalande.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Polaris.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Polaris.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Proxima.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Proxima.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Upsilon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Andromeda.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Aquila.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/ArgoNavis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CanisMajor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Carina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Carina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Centaurus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Draco.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Draco.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Hydra.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Machina.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Machina.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Orion.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Orion.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pegasus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Perseus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Pyxis.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Rigel.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Scarabaeus.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Sceptrum.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Solarium.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/UrsaMinor.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Zeta.ogg
diff --git a/data/sounds/AudioPackage9.mk b/data/sounds/AudioPackage9.mk
index dbe1350..53cc8c0 100644
--- a/data/sounds/AudioPackage9.mk
+++ b/data/sounds/AudioPackage9.mk
@@ -8,43 +8,43 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
-        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
-	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:system/media/audio/ui/Trusted.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
-	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg
+        $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Carbon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Krypton.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Neon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Neon.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Osmium.ogg \
+        $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+	$(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_focus.ogg \
+	$(LOCAL_PATH)/effects/material/ogg/LowBattery.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/LowBattery.ogg \
+	$(LOCAL_PATH)/effects/ogg/Dock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Dock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Undock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Undock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Lock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Lock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Unlock.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Unlock.ogg \
+	$(LOCAL_PATH)/effects/ogg/Trusted.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Trusted.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Adara.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Adara.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Arcturus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Capella.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Capella.ogg \
+	$(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CetiAlpha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Hojus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Hojus.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Mira.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Mira.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Pollux.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Pollux.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Procyon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Procyon.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Shaula.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Shaula.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Spica.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Syrma.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Vega.ogg \
+	$(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Girtab.ogg
diff --git a/data/sounds/AudioPackageGo.mk b/data/sounds/AudioPackageGo.mk
index 0296219..e3b27f2 100644
--- a/data/sounds/AudioPackageGo.mk
+++ b/data/sounds/AudioPackageGo.mk
@@ -20,30 +20,30 @@
 # Ring_Synth_04 : Flutey Phone
 # Alarm_Beep_03 : Beep Beep Beep
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:system/media/audio/notifications/Argon.ogg \
-    $(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
-    $(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
-    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:system/media/audio/notifications/Iridium.ogg \
-    $(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
-    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-    $(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-    $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-    $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
-    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
-    $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
-    $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
-    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
-    $(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Alya.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Argon.ogg \
+    $(LOCAL_PATH)/notifications/Canopus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Canopus.ogg \
+    $(LOCAL_PATH)/notifications/Deneb.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Deneb.ogg \
+    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Iridium.ogg \
+    $(LOCAL_PATH)/notifications/pixiedust.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/pixiedust.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Talitha.ogg \
+    $(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Cygnus.ogg \
+    $(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Kuma.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Themos.ogg \
+    $(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Argon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Platinum.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Helium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Oxygen.ogg \
+    $(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressInvalid.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
diff --git a/data/sounds/AudioTv.mk b/data/sounds/AudioTv.mk
index 91265af..d0006b7 100644
--- a/data/sounds/AudioTv.mk
+++ b/data/sounds/AudioTv.mk
@@ -15,8 +15,8 @@
 LOCAL_PATH := frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:system/media/audio/ui/KeypressStandard.ogg
+    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg
diff --git a/data/sounds/OriginalAudio.mk b/data/sounds/OriginalAudio.mk
index f683752..4d74d12 100644
--- a/data/sounds/OriginalAudio.mk
+++ b/data/sounds/OriginalAudio.mk
@@ -9,67 +9,67 @@
 LOCAL_PATH:= frameworks/base/data/sounds
 
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
-	$(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
-	$(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
-	$(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
-	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
-	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
-	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
-	$(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
-	$(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
-	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/VideoStop.ogg:system/media/audio/ui/VideoStop.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg
+	$(LOCAL_PATH)/F1_MissedCall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_MissedCall.ogg \
+	$(LOCAL_PATH)/F1_New_MMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_MMS.ogg \
+	$(LOCAL_PATH)/F1_New_SMS.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/F1_New_SMS.ogg \
+	$(LOCAL_PATH)/Alarm_Buzzer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Buzzer.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_01.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
+	$(LOCAL_PATH)/Alarm_Classic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Classic.ogg \
+	$(LOCAL_PATH)/Alarm_Beep_03.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_03.ogg \
+	$(LOCAL_PATH)/Alarm_Rooster_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Rooster_02.ogg \
+	$(LOCAL_PATH)/Ring_Classic_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Classic_02.ogg \
+	$(LOCAL_PATH)/Ring_Digital_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Digital_02.ogg \
+	$(LOCAL_PATH)/Ring_Synth_04.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_04.ogg \
+	$(LOCAL_PATH)/Ring_Synth_02.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Ring_Synth_02.ogg \
+	$(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Beat_Box_Android.ogg \
+	$(LOCAL_PATH)/notifications/Heaven.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Heaven.ogg \
+	$(LOCAL_PATH)/notifications/TaDa.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/TaDa.ogg \
+	$(LOCAL_PATH)/notifications/Tinkerbell.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Tinkerbell.ogg \
+	$(LOCAL_PATH)/effects/Effect_Tick.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
+	$(LOCAL_PATH)/effects/KeypressStandard.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressStandard.ogg \
+	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressSpacebar.ogg \
+	$(LOCAL_PATH)/effects/KeypressDelete.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
+	$(LOCAL_PATH)/effects/KeypressReturn.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressReturn.ogg \
+	$(LOCAL_PATH)/effects/VideoRecord.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoRecord.ogg \
+	$(LOCAL_PATH)/effects/VideoStop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/VideoStop.ogg \
+	$(LOCAL_PATH)/effects/camera_click.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BeatPlucker.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/CaffeineSnake.ogg
 
 ifneq ($(MINIMAL_NEWWAVELABS),true)
 PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
-	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
-	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
-	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
-	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
-	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
-	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
-	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
-	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
-	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
-	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
-	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
-	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
-	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
-	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
-	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
-	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
-	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
-	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
-	$(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
-	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
-	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
-	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
-	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
-	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
-	$(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
-	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
-	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg
+	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BentleyDubs.ogg \
+	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/BirdLoop.ogg \
+	$(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CaribbeanIce.ogg \
+	$(LOCAL_PATH)/newwavelabs/CurveBall.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CurveBall.ogg \
+	$(LOCAL_PATH)/newwavelabs/EtherShake.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/EtherShake.ogg \
+	$(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/FriendlyGhost.ogg \
+	$(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/GameOverGuitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/Growl.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Growl.ogg \
+	$(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/InsertCoin.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoopyLounge.ogg \
+	$(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/LoveFlute.ogg \
+	$(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MidEvilJaunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/MildlyAlarming.ogg \
+	$(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/NewPlayer.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises1.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises1.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises2.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises2.ogg \
+	$(LOCAL_PATH)/newwavelabs/Noises3.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Noises3.ogg \
+	$(LOCAL_PATH)/newwavelabs/OrganDub.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/OrganDub.ogg \
+	$(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/RomancingTheTone.ogg \
+	$(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SitarVsSitar.ogg \
+	$(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/SpringyJalopy.ogg \
+	$(LOCAL_PATH)/newwavelabs/Terminated.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/Terminated.ogg \
+	$(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/TwirlAway.ogg \
+	$(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/VeryAlarmed.ogg \
+	$(LOCAL_PATH)/newwavelabs/World.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/World.ogg \
+	$(LOCAL_PATH)/newwavelabs/DearDeer.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DearDeer.ogg \
+	$(LOCAL_PATH)/newwavelabs/DontPanic.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/DontPanic.ogg \
+	$(LOCAL_PATH)/newwavelabs/Highwire.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Highwire.ogg \
+	$(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/KzurbSonar.ogg \
+	$(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/OnTheHunt.ogg \
+	$(LOCAL_PATH)/newwavelabs/Voila.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/notifications/Voila.ogg \
+	$(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/CrazyDream.ogg \
+	$(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ringtones/DreamTheme.ogg
 endif
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 5b7ae70..9170d6d 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -18,6 +18,7 @@
 
 #include <private/hwui/WebViewFunctor.h>
 #include "Properties.h"
+#include "renderthread/RenderThread.h"
 
 #include <log/log.h>
 #include <utils/Trace.h>
@@ -90,6 +91,10 @@
         mHasContext = false;
         ATRACE_NAME("WebViewFunctor::onContextDestroyed");
         mCallbacks.onContextDestroyed(mFunctor, mData);
+
+        // grContext may be null in unit tests.
+        auto* grContext = renderthread::RenderThread::getInstance().getGrContext();
+        if (grContext) grContext->resetContext();
     }
 }
 
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 12666b3..5272227 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -123,6 +123,7 @@
     friend class RenderProxy;
     friend class DummyVsyncSource;
     friend class android::uirenderer::TestUtils;
+    friend class android::uirenderer::WebViewFunctor;
 
     RenderThread();
     virtual ~RenderThread();
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 3d50d2d..29e4256 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -139,6 +139,7 @@
     uint32_t file_version = *reinterpret_cast<uint32_t*>(addr);
     if (file_version != sCurrentFileVersion) {
         ALOGW("file_version mismatch! expected %d got %d", sCurrentFileVersion, file_version);
+        munmap(addr, sb.st_size);
         return false;
     }
 
@@ -150,6 +151,7 @@
         ALOGW("Parse failed on '%s' error='%s'", path.c_str(),
               output->InitializationErrorString().c_str());
     }
+    munmap(addr, sb.st_size);
     return success;
 }
 
diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
index 1d3d607..5af7d43 100644
--- a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
@@ -31,7 +31,7 @@
 
 class BitmapFillrate : public TestScene {
 public:
-    BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TestScene(), mAllocator(allocator) {}
 
     void createContent(int width, int height, Canvas& canvas) override {
@@ -70,4 +70,4 @@
 
     BitmapAllocationTestUtils::BitmapAllocator mAllocator;
     std::vector<sp<RenderNode> > mNodes;
-};
\ No newline at end of file
+};
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index ad11a1d..5107660 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -26,7 +26,7 @@
 
 class BitmapShaders : public TestScene {
 public:
-    BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TestScene(), mAllocator(allocator) {}
 
     sp<RenderNode> card;
diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp
index a64e844..286f5f1 100644
--- a/libs/hwui/tests/common/scenes/TvApp.cpp
+++ b/libs/hwui/tests/common/scenes/TvApp.cpp
@@ -48,7 +48,7 @@
 
 class TvApp : public TestScene {
 public:
-    TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TestScene(), mAllocator(allocator) {}
 
     sp<RenderNode> mBg;
@@ -232,7 +232,7 @@
 
 class TvAppNoRoundedCorner : public TvApp {
 public:
-    TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
+    explicit TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
 
 private:
     virtual float roundedCornerRadius() override { return dp(0); }
@@ -240,7 +240,7 @@
 
 class TvAppColorFilter : public TvApp {
 public:
-    TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
+    explicit TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
 
 private:
     virtual bool useOverlay() override { return false; }
@@ -248,7 +248,7 @@
 
 class TvAppNoRoundedCornerColorFilter : public TvApp {
 public:
-    TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)
+    explicit TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)
             : TvApp(allocator) {}
 
 private:
diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
index 479c462..635429d 100644
--- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -44,7 +44,7 @@
     static const int CANVAS_HEIGHT = 100;
     class PropertyTestCanvas : public TestCanvasBase {
     public:
-        PropertyTestCanvas(std::function<void(const SkCanvas&)> callback)
+        explicit PropertyTestCanvas(std::function<void(const SkCanvas&)> callback)
                 : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT), mCallback(callback) {}
         void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
             EXPECT_EQ(mDrawCounter++, 0);
diff --git a/libs/hwui/tests/unit/ThreadBaseTests.cpp b/libs/hwui/tests/unit/ThreadBaseTests.cpp
index 1168ff2..817c1f3 100644
--- a/libs/hwui/tests/unit/ThreadBaseTests.cpp
+++ b/libs/hwui/tests/unit/ThreadBaseTests.cpp
@@ -95,7 +95,7 @@
     };
 
     struct Counter {
-        Counter(EventCount* count) : mCount(count) { mCount->construct++; }
+        explicit Counter(EventCount* count) : mCount(count) { mCount->construct++; }
 
         Counter(const Counter& other) : mCount(other.mCount) {
             if (mCount) mCount->copy++;
@@ -148,4 +148,4 @@
     ASSERT_EQ(1, dummyObject->getStrongCount());
     ASSERT_EQ(2, lifecycleTestHelper(dummyObject));
     ASSERT_EQ(1, dummyObject->getStrongCount());
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 7665c92..b8381a7 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -109,7 +109,7 @@
         mContext = context;
         mSessionToken = token;
         mCallbackExecutor = (executor == null) ? context.getMainExecutor() : executor;
-        mCallback = (callback == null) ? new ControllerCallback() { } : callback;
+        mCallback = (callback == null) ? new ControllerCallback() {} : callback;
         mControllerStub = new Controller2Link(this);
         // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
         mResultHandler = new Handler(context.getMainLooper());
@@ -132,7 +132,7 @@
                 try {
                     mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
                     mSessionBinder.disconnect(mControllerStub, getNextSeqNumber());
-                } catch (RuntimeException e)  {
+                } catch (RuntimeException e) {
                     // No-op
                 }
             }
@@ -326,7 +326,7 @@
          * @param allowedCommands commands that's allowed by the session.
          */
         public void onConnected(@NonNull MediaController2 controller,
-                @NonNull Session2CommandGroup allowedCommands) { }
+                @NonNull Session2CommandGroup allowedCommands) {}
 
         /**
          * Called when the session refuses the controller or the controller is disconnected from
@@ -338,7 +338,7 @@
          *
          * @param controller the controller for this event
          */
-        public void onDisconnected(@NonNull MediaController2 controller) { }
+        public void onDisconnected(@NonNull MediaController2 controller) {}
 
         /**
          * Called when the connected session sent a session command.
@@ -364,6 +364,6 @@
          * @param result the result of the session command
          */
         public void onCommandResult(@NonNull MediaController2 controller, @NonNull Object token,
-                @NonNull Session2Command command, @NonNull Session2Command.Result result) { }
+                @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
     }
 }
diff --git a/media/java/android/media/MediaItem2.java b/media/java/android/media/MediaItem2.java
index 235325e..c496cf7 100644
--- a/media/java/android/media/MediaItem2.java
+++ b/media/java/android/media/MediaItem2.java
@@ -40,9 +40,8 @@
  * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
  * for consistent behavior across all devices.
  * <p>
- * @hide
  */
-public class MediaItem2 implements Parcelable {
+public final class MediaItem2 implements Parcelable {
     private static final String TAG = "MediaItem2";
 
     // intentionally less than long.MAX_VALUE.
@@ -69,7 +68,6 @@
                 }
             };
 
-    // TODO: Use SessionPlayer2.UNKNOWN_TIME instead
     private static final long UNKNOWN_TIME = -1;
 
     private final long mStartPositionMs;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 0057875..fb18c3b 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -25,8 +25,10 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
+import android.graphics.SurfaceTexture;
+import android.media.SubtitleController.Anchor;
+import android.media.SubtitleTrack.RenderingWidget;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -35,30 +37,19 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
-import android.os.Process;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
-import android.util.ArrayMap;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.widget.VideoView;
-import android.graphics.SurfaceTexture;
-import android.media.AudioManager;
-import android.media.MediaDrm;
-import android.media.MediaFormat;
-import android.media.MediaTimeProvider;
-import android.media.PlaybackParams;
-import android.media.SubtitleController;
-import android.media.SubtitleController.Anchor;
-import android.media.SubtitleData;
-import android.media.SubtitleTrack.RenderingWidget;
-import android.media.SyncParams;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -72,7 +63,6 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.Runnable;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
@@ -2105,9 +2095,11 @@
         mOnInfoListener = null;
         mOnVideoSizeChangedListener = null;
         mOnTimedTextListener = null;
-        if (mTimeProvider != null) {
-            mTimeProvider.close();
-            mTimeProvider = null;
+        synchronized (mTimeProviderLock) {
+            if (mTimeProvider != null) {
+                mTimeProvider.close();
+                mTimeProvider = null;
+            }
         }
         synchronized(this) {
             mSubtitleDataListenerDisabled = false;
@@ -2147,9 +2139,11 @@
         if (mSubtitleController != null) {
             mSubtitleController.reset();
         }
-        if (mTimeProvider != null) {
-            mTimeProvider.close();
-            mTimeProvider = null;
+        synchronized (mTimeProviderLock) {
+            if (mTimeProvider != null) {
+                mTimeProvider.close();
+                mTimeProvider = null;
+            }
         }
 
         stayAwake(false);
@@ -2790,12 +2784,17 @@
                 synchronized (mIndexTrackPairs) {
                     mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track));
                 }
-                Handler h = mTimeProvider.mEventHandler;
-                int what = TimeProvider.NOTIFY;
-                int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
-                Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, contents.getBytes());
-                Message m = h.obtainMessage(what, arg1, 0, trackData);
-                h.sendMessage(m);
+                synchronized (mTimeProviderLock) {
+                    if (mTimeProvider != null) {
+                        Handler h = mTimeProvider.mEventHandler;
+                        int what = TimeProvider.NOTIFY;
+                        int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
+                        Pair<SubtitleTrack, byte[]> trackData =
+                                Pair.create(track, contents.getBytes());
+                        Message m = h.obtainMessage(what, arg1, 0, trackData);
+                        h.sendMessage(m);
+                    }
+                }
                 return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
             }
 
@@ -3020,12 +3019,17 @@
                             total += bytes;
                         }
                     }
-                    Handler h = mTimeProvider.mEventHandler;
-                    int what = TimeProvider.NOTIFY;
-                    int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
-                    Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, bos.toByteArray());
-                    Message m = h.obtainMessage(what, arg1, 0, trackData);
-                    h.sendMessage(m);
+                    synchronized (mTimeProviderLock) {
+                        if (mTimeProvider != null) {
+                            Handler h = mTimeProvider.mEventHandler;
+                            int what = TimeProvider.NOTIFY;
+                            int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
+                            Pair<SubtitleTrack, byte[]> trackData =
+                                    Pair.create(track, bos.toByteArray());
+                            Message m = h.obtainMessage(what, arg1, 0, trackData);
+                            h.sendMessage(m);
+                        }
+                    }
                     return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
                 } catch (Exception e) {
                     Log.e(TAG, e.getMessage(), e);
@@ -3308,14 +3312,17 @@
     private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000;
 
     private TimeProvider mTimeProvider;
+    private final Object mTimeProviderLock = new Object();
 
     /** @hide */
     @UnsupportedAppUsage
     public MediaTimeProvider getMediaTimeProvider() {
-        if (mTimeProvider == null) {
-            mTimeProvider = new TimeProvider(this);
+        synchronized (mTimeProviderLock) {
+            if (mTimeProvider == null) {
+                mTimeProvider = new TimeProvider(this);
+            }
+            return mTimeProvider;
         }
-        return mTimeProvider;
     }
 
     private class EventHandler extends Handler
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index b874ba46..1ee851f 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -464,7 +464,21 @@
             if (mId == null) {
                 mId = "";
             }
-            return new MediaSession2(mContext, mId, mSessionActivity, mCallbackExecutor, mCallback);
+            MediaSession2 session2 = new MediaSession2(mContext, mId, mSessionActivity,
+                    mCallbackExecutor, mCallback);
+
+            // Notify framework about the newly create session after the constructor is finished.
+            // Otherwise, framework may access the session before the initialization is finished.
+            try {
+                MediaSessionManager manager = (MediaSessionManager) mContext.getSystemService(
+                        Context.MEDIA_SESSION_SERVICE);
+                manager.notifySession2Created(session2.getSessionToken());
+            } catch (Exception e) {
+                session2.close();
+                throw e;
+            }
+
+            return session2;
         }
     }
 
@@ -643,7 +657,7 @@
     public abstract static class SessionCallback {
         /**
          * Called when a controller is created for this session. Return allowed commands for
-         * controller. By default it allows all connection requests and commands.
+         * controller. By default it returns {@code null}.
          * <p>
          * You can reject the connection by returning {@code null}. In that case, controller
          * receives {@link MediaController2.ControllerCallback#onDisconnected(MediaController2)}
@@ -656,10 +670,7 @@
         @Nullable
         public Session2CommandGroup onConnect(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller) {
-            Session2CommandGroup commands = new Session2CommandGroup.Builder()
-                    .addAllPredefinedCommands(Session2Command.COMMAND_VERSION_1)
-                    .build();
-            return commands;
+            return null;
         }
 
         /**
@@ -669,7 +680,7 @@
          * @param controller controller information
          */
         public void onDisconnected(@NonNull MediaSession2 session,
-                @NonNull ControllerInfo controller) { }
+                @NonNull ControllerInfo controller) {}
 
         /**
          * Called when a controller sent a session command.
@@ -699,6 +710,6 @@
          */
         public void onCommandResult(@NonNull MediaSession2 session,
                 @NonNull ControllerInfo controller, @NonNull Object token,
-                @NonNull Session2Command command, @NonNull Session2Command.Result result) { }
+                @NonNull Session2Command command, @NonNull Session2Command.Result result) {}
     }
 }
diff --git a/media/java/android/media/Session2Command.java b/media/java/android/media/Session2Command.java
index e46e64e..d2a5aae 100644
--- a/media/java/android/media/Session2Command.java
+++ b/media/java/android/media/Session2Command.java
@@ -16,18 +16,13 @@
 
 package android.media;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
 import java.util.Objects;
 
 /**
@@ -46,392 +41,11 @@
  */
 public final class Session2Command implements Parcelable {
     /**
-     * The first version of session commands. This version is for commands introduced in API 29.
-     * <p>
-     * This would be used to specify which commands should be added by
-     * {@link Session2CommandGroup.Builder#addAllPredefinedCommands(int)}
-     *
-     * @see Session2CommandGroup.Builder#addAllPredefinedCommands(int)
-     */
-    public static final int COMMAND_VERSION_1 = 1;
-
-    /**
-     * @hide
-     */
-    public static final int COMMAND_VERSION_CURRENT = COMMAND_VERSION_1;
-
-    /**
-     * @hide
-     */
-    @IntDef({COMMAND_VERSION_1})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CommandVersion {}
-
-    /**
-     * @hide
-     */
-    @IntDef({COMMAND_CODE_CUSTOM,
-            COMMAND_CODE_PLAYER_PLAY,
-            COMMAND_CODE_PLAYER_PAUSE,
-            COMMAND_CODE_PLAYER_PREPARE,
-            COMMAND_CODE_PLAYER_SEEK_TO,
-            COMMAND_CODE_PLAYER_SET_SPEED,
-            COMMAND_CODE_PLAYER_GET_PLAYLIST,
-            COMMAND_CODE_PLAYER_SET_PLAYLIST,
-            COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM,
-            COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM,
-            COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM,
-            COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE,
-            COMMAND_CODE_PLAYER_SET_REPEAT_MODE,
-            COMMAND_CODE_PLAYER_GET_PLAYLIST_METADATA,
-            COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM,
-            COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM,
-            COMMAND_CODE_PLAYER_REPLACE_PLAYLIST_ITEM,
-            COMMAND_CODE_PLAYER_GET_CURRENT_MEDIA_ITEM,
-            COMMAND_CODE_PLAYER_UPDATE_LIST_METADATA,
-            COMMAND_CODE_PLAYER_SET_MEDIA_ITEM,
-            COMMAND_CODE_VOLUME_SET_VOLUME,
-            COMMAND_CODE_VOLUME_ADJUST_VOLUME,
-            COMMAND_CODE_SESSION_FAST_FORWARD,
-            COMMAND_CODE_SESSION_REWIND,
-            COMMAND_CODE_SESSION_SKIP_FORWARD,
-            COMMAND_CODE_SESSION_SKIP_BACKWARD,
-            COMMAND_CODE_SESSION_SET_RATING,
-            COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT,
-            COMMAND_CODE_LIBRARY_SUBSCRIBE,
-            COMMAND_CODE_LIBRARY_UNSUBSCRIBE,
-            COMMAND_CODE_LIBRARY_GET_CHILDREN,
-            COMMAND_CODE_LIBRARY_GET_ITEM,
-            COMMAND_CODE_LIBRARY_SEARCH,
-            COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CommandCode {}
-
-    /**
      * Command code for the custom command which can be defined by string action in the
      * {@link Session2Command}.
      */
     public static final int COMMAND_CODE_CUSTOM = 0;
 
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    // Player commands (i.e. commands to {@link Session2Player})
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    static final ArrayMap<Integer, Range> VERSION_PLAYER_COMMANDS_MAP = new ArrayMap<>();
-    static final ArrayMap<Integer, Range> VERSION_PLAYER_PLAYLIST_COMMANDS_MAP = new ArrayMap<>();
-
-    // TODO: check the link tag, and reassign int values properly.
-    /**
-     * Command code for {@link MediaController2#play()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
-     * Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_PLAY = 10000;
-
-    /**
-     * Command code for {@link MediaController2#pause()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
-     * Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_PAUSE = 10001;
-
-    /**
-     * Command code for {@link MediaController2#prepare()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
-     * Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_PREPARE = 10002;
-
-    /**
-     * Command code for {@link MediaController2#seekTo(long)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
-     * Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_SEEK_TO = 10003;
-
-    /**
-     * Command code for {@link MediaController2#setPlaybackSpeed(float)}}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link Session22Callback#onCommandRequest(MediaSession2, Controller2Info,
-     * Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_SET_SPEED = 10004;
-
-    /**
-     * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
-     * information to the controller.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST = 10005;
-
-    /**
-     * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the
-     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_SET_PLAYLIST = 10006;
-
-    /**
-     * Command code for {@link MediaController2#skipToPlaylistItem(int)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the
-     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM = 10007;
-
-    /**
-     * Command code for {@link MediaController2#skipToPreviousPlaylistItem()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link Session2Callback#onCommandRequest(
-     * MediaSession2, Controller2Info, Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM = 10008;
-
-    /**
-     * Command code for {@link MediaController2#skipToNextPlaylistItem()}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the {@link Session2Callback#onCommandRequest(
-     * MediaSession2, Controller2Info, Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-
-    public static final int COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM = 10009;
-
-    /**
-     * Command code for {@link MediaController2#setShuffleMode(int)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the
-     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE = 10010;
-
-    /**
-     * Command code for {@link MediaController2#setRepeatMode(int)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the
-     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_SET_REPEAT_MODE = 10011;
-
-    /**
-     * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose metadata
-     * information to the controller.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST_METADATA = 10012;
-
-    /**
-     * Command code for {@link MediaController2#addPlaylistItem(int, String)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the
-     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM = 10013;
-
-    /**
-     * Command code for {@link MediaController2#removePlaylistItem(int, String)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the
-     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM = 10014;
-
-    /**
-     * Command code for {@link MediaController2#replacePlaylistItem(int, String)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the
-     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_REPLACE_PLAYLIST_ITEM = 10015;
-
-    /**
-     * Command code for {@link MediaController2#getCurrentMediaItem()}. This will expose metadata
-     * information to the controller.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_GET_CURRENT_MEDIA_ITEM = 10016;
-
-    /**
-     * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the
-     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_UPDATE_LIST_METADATA = 10017;
-
-    /**
-     * Command code for {@link MediaController2#setMediaItem(String)}.
-     * <p>
-     * Command would be sent directly to the player if the session doesn't reject the request
-     * through the
-     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_PLAYER_SET_MEDIA_ITEM = 10018;
-
-    static {
-        VERSION_PLAYER_COMMANDS_MAP.put(COMMAND_VERSION_1,
-                new Range(COMMAND_CODE_PLAYER_PLAY, COMMAND_CODE_PLAYER_SET_MEDIA_ITEM));
-    }
-
-    static {
-        VERSION_PLAYER_PLAYLIST_COMMANDS_MAP.put(COMMAND_VERSION_1,
-                new Range(COMMAND_CODE_PLAYER_GET_PLAYLIST,
-                        COMMAND_CODE_PLAYER_SET_MEDIA_ITEM));
-    }
-
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    // Volume commands (i.e. commands to {@link AudioManager} or {@link RouteMediaPlayer})
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    static final ArrayMap<Integer, Range> VERSION_VOLUME_COMMANDS_MAP = new ArrayMap<>();
-
-    /**
-     * Command code for {@link MediaController2#setVolumeTo(int, int)}.
-     * <p>
-     * <p>
-     * If the session doesn't reject the request through the
-     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)},
-     * command would adjust the device volume. It would send to the player directly only if it's
-     * remote player. See RouteMediaPlayer for a remote player.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_VOLUME_SET_VOLUME = 30000;
-
-    /**
-     * Command code for {@link MediaController2#adjustVolume(int, int)}.
-     * <p>
-     * If the session doesn't reject the request through the
-     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)},
-     * command would adjust the device volume. It would send to the player directly only if it's
-     * remote player. See RouteMediaPlayer for a remote player.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_VOLUME_ADJUST_VOLUME = 30001;
-
-    static {
-        VERSION_VOLUME_COMMANDS_MAP.put(COMMAND_VERSION_1,
-                new Range(COMMAND_CODE_VOLUME_SET_VOLUME,
-                        COMMAND_CODE_VOLUME_ADJUST_VOLUME));
-    }
-
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    // Session commands (i.e. commands to {@link MediaSession2#Session2Callback})
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    static final ArrayMap<Integer, Range> VERSION_SESSION_COMMANDS_MAP = new ArrayMap<>();
-
-    /**
-     * Command code for {@link MediaController2#fastForward()}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 40000;
-
-    /**
-     * Command code for {@link MediaController2#rewind()}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_SESSION_REWIND = 40001;
-
-    /**
-     * Command code for {@link MediaController2#skipForward()}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_SESSION_SKIP_FORWARD = 40002;
-
-    /**
-     * Command code for {@link MediaController2#skipBackward()}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_SESSION_SKIP_BACKWARD = 40003;
-
-    /**
-     * Command code for {@link MediaController2#setRating(String, Rating)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_SESSION_SET_RATING = 40010;
-
-    /**
-     * @hide
-     */
-    @IntDef(flag = false, /*prefix = "RESULT_CODE",*/ value = {
-            RESULT_SUCCESS,
-            RESULT_ERROR_UNKNOWN_ERROR,
-            RESULT_ERROR_INVALID_STATE,
-            RESULT_ERROR_BAD_VALUE,
-            RESULT_ERROR_PERMISSION_DENIED,
-            RESULT_ERROR_IO_ERROR,
-            RESULT_INFO_SKIPPED,
-            RESULT_ERROR_SESSION_DISCONNECTED,
-            RESULT_ERROR_NOT_SUPPORTED,
-            RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED,
-            RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED,
-            RESULT_ERROR_SESSION_CONCURRENT_STREAM_LIMIT,
-            RESULT_ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED,
-            RESULT_ERROR_SESSION_NOT_AVAILABLE_IN_REGION,
-            RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED,
-            RESULT_ERROR_SESSION_SETUP_REQUIRED})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ResultCode {}
-
     /**
      * Result code representing that the command is skipped or canceled. For an example, a seek
      * command can be skipped if it is followed by another seek command.
@@ -448,73 +62,6 @@
      */
     public static final int RESULT_ERROR_UNKNOWN_ERROR = -1;
 
-    /**
-     * Result code representing that the command cannot be completed because the current state is
-     * not valid for the command.
-     */
-    public static final int RESULT_ERROR_INVALID_STATE = -2;
-
-    /**
-     * Result code representing that an argument is illegal.
-     */
-    public static final int RESULT_ERROR_BAD_VALUE = -3;
-
-    /**
-     * Result code representing that the command is not allowed.
-     */
-    public static final int RESULT_ERROR_PERMISSION_DENIED = -4;
-
-    /**
-     * Result code representing a file or network related command error.
-     */
-    public static final int RESULT_ERROR_IO_ERROR = -5;
-
-    /**
-     * Result code representing that the command is not supported nor implemented.
-     */
-    public static final int RESULT_ERROR_NOT_SUPPORTED = -6;
-
-    /**
-     * Result code representing that the session and controller were disconnected.
-     */
-    public static final int RESULT_ERROR_SESSION_DISCONNECTED = -100;
-
-    /**
-     * Result code representing that the authentication has expired.
-     */
-    public static final int RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED = -102;
-
-    /**
-     * Result code representing that a premium account is required.
-     */
-    public static final int RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103;
-
-    /**
-     * Result code representing that too many concurrent streams are detected.
-     */
-    public static final int RESULT_ERROR_SESSION_CONCURRENT_STREAM_LIMIT = -104;
-
-    /**
-     * Result code representing that the content is blocked due to parental controls.
-     */
-    public static final int RESULT_ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED = -105;
-
-    /**
-     * Result code representing that the content is blocked due to being regionally unavailable.
-     */
-    public static final int RESULT_ERROR_SESSION_NOT_AVAILABLE_IN_REGION = -106;
-
-    /**
-     * Result code representing that the application cannot skip any more because the skip limit is
-     * reached.
-     */
-    public static final int RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED = -107;
-
-    /**
-     * Result code representing that the session needs user's manual intervention.
-     */
-    public static final int RESULT_ERROR_SESSION_SETUP_REQUIRED = -108;
-
     public static final Parcelable.Creator<Session2Command> CREATOR =
             new Parcelable.Creator<Session2Command>() {
                 @Override
@@ -528,82 +75,17 @@
                 }
             };
 
-    static {
-        VERSION_SESSION_COMMANDS_MAP.put(COMMAND_VERSION_1,
-                new Range(COMMAND_CODE_SESSION_FAST_FORWARD, COMMAND_CODE_SESSION_SET_RATING));
-    }
-
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    // Session commands (i.e. commands to {@link MediaLibrarySession#MediaLibrarySessionCallback})
-    ////////////////////////////////////////////////////////////////////////////////////////////////
-    static final ArrayMap<Integer, Range> VERSION_LIBRARY_COMMANDS_MAP = new ArrayMap<>();
-
-    /**
-     * Command code for {@link MediaBrowser2#getLibraryRoot(Library2Params)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 50000;
-
-    /**
-     * Command code for {@link MediaBrowser2#subscribe(String, Library2Params)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 50001;
-
-    /**
-     * Command code for {@link MediaBrowser2#unsubscribe(String)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 50002;
-
-    /**
-     * Command code for {@link MediaBrowser2#getChildren(String, int, int, Library2Params)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 50003;
-
-    /**
-     * Command code for {@link MediaBrowser2#getItem(String)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 50004;
-
-    /**
-     * Command code for {@link MediaBrowser2#search(String, LibraryParams)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_LIBRARY_SEARCH = 50005;
-
-    /**
-     * Command code for {@link MediaBrowser2#getSearchResult(String, int, int, Library2Params)}.
-     * <p>
-     * Code version is {@link #COMMAND_VERSION_1}.
-     */
-    public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 50006;
-
-    static {
-        VERSION_LIBRARY_COMMANDS_MAP.put(COMMAND_VERSION_1,
-                new Range(COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT,
-                        COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT));
-    }
-
-    @CommandCode private final int mCommandCode;
+    private final int mCommandCode;
     // Nonnull if it's custom command
     private final String mCustomCommand;
     private final Bundle mExtras;
 
     /**
-     * Constructor for creating a predefined command.
+     * Constructor for creating a command predefined in AndroidX media2.
      *
-     * @param commandCode A command code for predefined command.
+     * @param commandCode A command code for a command predefined in AndroidX media2.
      */
-    public Session2Command(@CommandCode int commandCode) {
+    public Session2Command(int commandCode) {
         if (commandCode == COMMAND_CODE_CUSTOM) {
             throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM");
         }
@@ -641,7 +123,7 @@
      * Gets the command code of a predefined command.
      * This will return {@link #COMMAND_CODE_CUSTOM} for a custom command.
      */
-    public @CommandCode int getCommandCode() {
+    public int getCommandCode() {
         return mCommandCode;
     }
 
@@ -649,7 +131,8 @@
      * Gets the action of a custom command.
      * This will return {@code null} for a predefined command.
      */
-    public @Nullable String getCustomCommand() {
+    @Nullable
+    public String getCustomCommand() {
         return mCustomCommand;
     }
 
@@ -657,7 +140,8 @@
      * Gets the extra bundle of a custom command.
      * This will return {@code null} for a predefined command.
      */
-    public @Nullable Bundle getExtras() {
+    @Nullable
+    public Bundle getExtras() {
         return mExtras;
     }
 
@@ -720,16 +204,4 @@
             return mResultData;
         }
     }
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    static final class Range {
-        public final int lower;
-        public final int upper;
-
-        Range(int lower, int upper) {
-            this.lower = lower;
-            this.upper = upper;
-        }
-    }
 }
-
diff --git a/media/java/android/media/Session2CommandGroup.java b/media/java/android/media/Session2CommandGroup.java
index 18aff30..122dfb1 100644
--- a/media/java/android/media/Session2CommandGroup.java
+++ b/media/java/android/media/Session2CommandGroup.java
@@ -17,14 +17,11 @@
 package android.media;
 
 import static android.media.Session2Command.COMMAND_CODE_CUSTOM;
-import static android.media.Session2Command.COMMAND_VERSION_1;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.media.Session2Command.Range;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.ArrayMap;
 
 import java.util.Collection;
 import java.util.HashSet;
@@ -61,7 +58,7 @@
     /**
      * Default Constructor.
      */
-    public Session2CommandGroup() { }
+    public Session2CommandGroup() {}
 
     /**
      * Creates a new Session2CommandGroup with commands copied from another object.
@@ -89,38 +86,6 @@
     }
 
     /**
-     * Adds a command to this command group.
-     *
-     * @param command A command to add. Shouldn't be {@code null}.
-     * @hide TODO remove this method
-     */
-    public void addCommand(@NonNull Session2Command command) {
-        if (command == null) {
-            throw new IllegalArgumentException("command shouldn't be null");
-        }
-        if (!hasCommand(command)) {
-            mCommands.add(command);
-        }
-    }
-
-    /**
-     * Adds a predefined command with given {@code commandCode} to this command group.
-     *
-     * @param commandCode A command code to add.
-     *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
-     * @hide TODO remove this method
-     */
-    public void addCommand(@Session2Command.CommandCode int commandCode) {
-        if (commandCode == COMMAND_CODE_CUSTOM) {
-            throw new IllegalArgumentException(
-                    "Use addCommand(Session2Command) for COMMAND_CODE_CUSTOM.");
-        }
-        if (!hasCommand(commandCode)) {
-            mCommands.add(new Session2Command(commandCode));
-        }
-    }
-
-    /**
      * Checks whether this command group has a command that matches given {@code command}.
      *
      * @param command A command to find. Shouldn't be {@code null}.
@@ -138,7 +103,7 @@
      * @param commandCode A command code to find.
      *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
      */
-    public boolean hasCommand(@Session2Command.CommandCode int commandCode) {
+    public boolean hasCommand(int commandCode) {
         if (commandCode == COMMAND_CODE_CUSTOM) {
             throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
         }
@@ -153,7 +118,8 @@
     /**
      * Gets all commands of this command group.
      */
-    public @NonNull Set<Session2Command> getCommands() {
+    @NonNull
+    public Set<Session2Command> getCommands() {
         return new HashSet<>(mCommands);
     }
 
@@ -191,7 +157,8 @@
          *
          * @param command A command to add. Shouldn't be {@code null}.
          */
-        public @NonNull Builder addCommand(@NonNull Session2Command command) {
+        @NonNull
+        public Builder addCommand(@NonNull Session2Command command) {
             if (command == null) {
                 throw new IllegalArgumentException("command shouldn't be null");
             }
@@ -205,7 +172,8 @@
          * @param commandCode A command code to add.
          *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
          */
-        public @NonNull Builder addCommand(@Session2Command.CommandCode int commandCode) {
+        @NonNull
+        public Builder addCommand(int commandCode) {
             if (commandCode == COMMAND_CODE_CUSTOM) {
                 throw new IllegalArgumentException(
                         "Use addCommand(Session2Command) for COMMAND_CODE_CUSTOM.");
@@ -215,33 +183,12 @@
         }
 
         /**
-         * Adds all predefined session commands except for the commands added after the specified
-         * version without default implementation. This provides convenient way to add commands
-         * with implementation.
-         *
-         * @param version command version
-         * @see Session2Command#COMMAND_VERSION_1
-         * @see
-         * MediaSession2.Session2Callback#onConnect
-         */
-        public @NonNull Builder addAllPredefinedCommands(
-                @Session2Command.CommandVersion int version) {
-            if (version != COMMAND_VERSION_1) {
-                throw new IllegalArgumentException("Unknown command version " + version);
-            }
-            addAllPlayerCommands(version);
-            addAllVolumeCommands(version);
-            addAllSessionCommands(version);
-            addAllLibraryCommands(version);
-            return this;
-        }
-
-        /**
          * Removes a command from this group which matches given {@code command}.
          *
          * @param command A command to find. Shouldn't be {@code null}.
          */
-        public @NonNull Builder removeCommand(@NonNull Session2Command command) {
+        @NonNull
+        public Builder removeCommand(@NonNull Session2Command command) {
             if (command == null) {
                 throw new IllegalArgumentException("command shouldn't be null");
             }
@@ -255,7 +202,8 @@
          * @param commandCode A command code to find.
          *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
          */
-        public @NonNull Builder removeCommand(@Session2Command.CommandCode int commandCode) {
+        @NonNull
+        public Builder removeCommand(int commandCode) {
             if (commandCode == COMMAND_CODE_CUSTOM) {
                 throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM");
             }
@@ -263,59 +211,13 @@
             return this;
         }
 
-        @NonNull Builder addAllPlayerCommands(@Session2Command.CommandVersion int version) {
-            addCommands(version, Session2Command.VERSION_PLAYER_COMMANDS_MAP);
-            return this;
-        }
-
-        @NonNull Builder addAllPlayerCommands(@Session2Command.CommandVersion int version,
-                boolean includePlaylistCommands) {
-            if (includePlaylistCommands) {
-                return addAllPlayerCommands(version);
-            }
-            for (int i = COMMAND_VERSION_1; i <= version; i++) {
-                Range include = Session2Command.VERSION_PLAYER_COMMANDS_MAP.get(i);
-                Range exclude = Session2Command.VERSION_PLAYER_PLAYLIST_COMMANDS_MAP.get(i);
-                for (int code = include.lower; code <= include.upper; code++) {
-                    if (code < exclude.lower && code > exclude.upper) {
-                        addCommand(code);
-                    }
-                }
-            }
-            return this;
-        }
-
-        @NonNull Builder addAllVolumeCommands(@Session2Command.CommandVersion int version) {
-            addCommands(version, Session2Command.VERSION_VOLUME_COMMANDS_MAP);
-            return this;
-        }
-
-        @NonNull Builder addAllSessionCommands(@Session2Command.CommandVersion int version) {
-            addCommands(version, Session2Command.VERSION_SESSION_COMMANDS_MAP);
-            return this;
-        }
-
-        @NonNull Builder addAllLibraryCommands(@Session2Command.CommandVersion int version) {
-            addCommands(version, Session2Command.VERSION_LIBRARY_COMMANDS_MAP);
-            return this;
-        }
-
-        private void addCommands(
-                @Session2Command.CommandVersion int version, ArrayMap<Integer, Range> map) {
-            for (int i = COMMAND_VERSION_1; i <= version; i++) {
-                Range range = map.get(i);
-                for (int code = range.lower; code <= range.upper; code++) {
-                    addCommand(code);
-                }
-            }
-        }
-
         /**
          * Builds {@link Session2CommandGroup}.
          *
          * @return a new {@link Session2CommandGroup}.
          */
-        public @NonNull Session2CommandGroup build() {
+        @NonNull
+        public Session2CommandGroup build() {
             return new Session2CommandGroup(mCommands);
         }
     }
diff --git a/core/java/android/hardware/display/BrightnessCorrection.aidl b/media/java/android/media/Session2Token.aidl
similarity index 81%
rename from core/java/android/hardware/display/BrightnessCorrection.aidl
rename to media/java/android/media/Session2Token.aidl
index 3abe29c..c5980e9 100644
--- a/core/java/android/hardware/display/BrightnessCorrection.aidl
+++ b/media/java/android/media/Session2Token.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.hardware.display;
+package android.media;
 
-parcelable BrightnessCorrection;
+parcelable Session2Token;
diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java
index 7642faa..4634c69 100644
--- a/media/java/android/media/Session2Token.java
+++ b/media/java/android/media/Session2Token.java
@@ -102,7 +102,8 @@
     private final ComponentName mComponentName;
 
     /**
-     * Constructor for the token.
+     * Constructor for the token with type {@link #TYPE_SESSION_SERVICE} or
+     * {@link #TYPE_LIBRARY_SERVICE}.
      *
      * @param context The context.
      * @param serviceComponent The component name of the service.
@@ -119,7 +120,7 @@
         final int uid = getUid(manager, serviceComponent.getPackageName());
 
         // TODO: Uncomment below to stop hardcode type.
-        final int type = TYPE_SESSION;
+        final int type = TYPE_SESSION_SERVICE;
 //        final int type;
 //        if (isInterfaceDeclared(manager, MediaLibraryService2.SERVICE_INTERFACE,
 //                serviceComponent)) {
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index d6c226f..51148e2 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -17,6 +17,7 @@
 
 import android.content.ComponentName;
 import android.media.IRemoteVolumeController;
+import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -32,6 +33,7 @@
  */
 interface ISessionManager {
     ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
+    void notifySession2Created(in Session2Token sessionToken);
     List<IBinder> getSessions(in ComponentName compName, int userId);
     void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
             boolean needWakeLock);
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 181ee53..acbc828 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -1154,7 +1154,7 @@
         private boolean mRegistered = false;
 
         public MessageHandler(Looper looper, MediaController.Callback cb) {
-            super(looper, null, true);
+            super(looper);
             mCallback = cb;
         }
 
@@ -1193,6 +1193,7 @@
 
         public void post(int what, Object obj, Bundle data) {
             Message msg = obtainMessage(what, obj);
+            msg.setAsynchronous(true);
             msg.setData(data);
             msg.sendToTarget();
         }
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 8962bb7..df8cc35 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -1453,7 +1453,7 @@
         private RemoteUserInfo mCurrentControllerInfo;
 
         public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
-            super(looper, null, true);
+            super(looper);
             mCallback = callback;
             mCallback.mHandler = this;
         }
@@ -1461,6 +1461,7 @@
         public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) {
             Pair<RemoteUserInfo, Object> objWithCaller = Pair.create(caller, obj);
             Message msg = obtainMessage(what, objWithCaller);
+            msg.setAsynchronous(true);
             msg.setData(data);
             if (delayMs > 0) {
                 sendMessageDelayed(msg, delayMs);
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 4221d66..ef5cf00 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -26,6 +26,8 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.IRemoteVolumeController;
+import android.media.MediaSession2;
+import android.media.Session2Token;
 import android.media.browse.MediaBrowser;
 import android.os.Handler;
 import android.os.IBinder;
@@ -102,6 +104,30 @@
     }
 
     /**
+     * Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is
+     * created.
+     * <p>
+     * Do not use this API directly, but create a new instance through the
+     * {@link MediaSession2.Builder} instead.
+     *
+     * @param token newly created session2 token
+     * @hide
+     */
+    public void notifySession2Created(@NonNull Session2Token token) {
+        if (token == null) {
+            throw new IllegalArgumentException("token shouldn't be null");
+        }
+        if (token.getType() != Session2Token.TYPE_SESSION) {
+            throw new IllegalArgumentException("token's type should be TYPE_SESSION");
+        }
+        try {
+            mService.notifySession2Created(token);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Get a list of controllers for all ongoing sessions. The controllers will
      * be provided in priority order with the most important controller at index
      * 0.
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index f75f69b..fda0fc4 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -102,7 +102,7 @@
         "libhidlbase",
         "libhidlmemory",
 
-        "libpowermanager",  // Used by JWakeLock. Will be replace with public SDJ API.
+        "libpowermanager",  // Used by JWakeLock. Will be replace with public SDK API.
         "libmediametrics",  // Used by MediaMetrics. Will be replaced with stable C API.
         "libbinder",  // Used by JWakeLock and MediaMetrics.
 
@@ -111,6 +111,7 @@
 
         // NDK or NDK-compliant
         "libandroid",
+        "libbinder_ndk",
         "libmediandk",
         "libnativehelper_compat_libc++",
         "liblog",
diff --git a/media/tests/MtpTests/Android.mk b/media/tests/MtpTests/Android.mk
index 6375ed3..4cee62e 100644
--- a/media/tests/MtpTests/Android.mk
+++ b/media/tests/MtpTests/Android.mk
@@ -5,7 +5,7 @@
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
 
 LOCAL_PACKAGE_NAME := MtpTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/media/tests/MtpTests/AndroidManifest.xml b/media/tests/MtpTests/AndroidManifest.xml
index 21e2b01..72e03f1 100644
--- a/media/tests/MtpTests/AndroidManifest.xml
+++ b/media/tests/MtpTests/AndroidManifest.xml
@@ -25,7 +25,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.mtp"
                      android:label="MtpTests"/>
 </manifest>
diff --git a/media/tests/MtpTests/AndroidTest.xml b/media/tests/MtpTests/AndroidTest.xml
index a61a3b4..22638bc 100644
--- a/media/tests/MtpTests/AndroidTest.xml
+++ b/media/tests/MtpTests/AndroidTest.xml
@@ -10,6 +10,6 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="android.mtp"/>
-        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
     </test>
 </configuration>
\ No newline at end of file
diff --git a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
index 566d1c6..1f58bde 100644
--- a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
+++ b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
@@ -18,20 +18,19 @@
 import android.os.FileUtils;
 import android.os.UserHandle;
 import android.os.storage.StorageVolume;
-import android.support.test.filters.SmallTest;
-import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
-import org.junit.runners.MethodSorters;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.junit.runners.MethodSorters;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -39,8 +38,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
 
 /**
  * Tests for MtpStorageManager functionality.
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index 3c0a297..0c35204 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -20,11 +20,9 @@
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.car.CarNotificationEntryManager;
 import com.android.systemui.car.CarNotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.car.CarFacetButtonController;
 import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.volume.CarVolumeDialogComponent;
@@ -65,16 +63,16 @@
     }
 
     @Override
-    public NotificationEntryManager provideNotificationEntryManager(Context context) {
-        return new CarNotificationEntryManager(context);
-    }
-
-    @Override
     public NotificationInterruptionStateProvider provideNotificationInterruptionStateProvider(
             Context context) {
         return new CarNotificationInterruptionStateProvider(context);
     }
 
+    @Override
+    public boolean provideAllowNotificationLongPress() {
+        return false;
+    }
+
     @Module
     protected static class ContextHolder {
         private Context mContext;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
deleted file mode 100644
index 323cae0..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ /dev/null
@@ -1,41 +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.systemui.car;
-
-import android.content.Context;
-
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-
-public class CarNotificationEntryManager extends NotificationEntryManager {
-    public CarNotificationEntryManager(Context context) {
-        super(context);
-    }
-
-    /**
-     * Returns the
-     * {@link ExpandableNotificationRow.LongPressListener} that will
-     * be triggered when a notification card is long-pressed.
-     */
-    @Override
-    public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
-        // For the automative use case, we do not want to the user to be able to interact with
-        // a notification other than a regular click. As a result, just return null for the
-        // long click listener.
-        return null;
-    }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
index 62502ef..6d960d7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -18,8 +18,8 @@
 
 import android.content.Context;
 
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */
 public class CarNotificationInterruptionStateProvider extends
@@ -29,7 +29,7 @@
     }
 
     @Override
-    public boolean shouldHeadsUp(NotificationData.Entry entry) {
+    public boolean shouldHeadsUp(NotificationEntry entry) {
         // Because space is usually constrained in the auto use-case, there should not be a
         // pinned notification when the shade has been expanded. Ensure this by not pinning any
         // notification if the shade is already opened.
diff --git a/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java b/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java
new file mode 100644
index 0000000..50cb0ab
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ext.services.notification;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+
+/**
+ * Counts the entity types for smart actions. Some entity types are considered the same
+ * type, like {@link TextClassifier#TYPE_DATE} and {@link TextClassifier#TYPE_DATE_TIME}.
+ */
+class EntityTypeCounter {
+
+    private static final ArrayMap<String, String> ENTITY_TYPE_MAPPING = new ArrayMap<>();
+
+    static {
+        ENTITY_TYPE_MAPPING.put(TextClassifier.TYPE_DATE_TIME, TextClassifier.TYPE_DATE);
+    }
+
+    private final ArrayMap<String, Integer> mEntityTypeCount = new ArrayMap<>();
+
+
+    void increment(@NonNull String entityType) {
+        entityType = convertToBaseEntityType(entityType);
+        if (mEntityTypeCount.containsKey(entityType)) {
+            mEntityTypeCount.put(entityType, mEntityTypeCount.get(entityType) + 1);
+        } else {
+            mEntityTypeCount.put(entityType, 1);
+        }
+    }
+
+    int getCount(@NonNull String entityType) {
+        entityType = convertToBaseEntityType(entityType);
+        return mEntityTypeCount.getOrDefault(entityType, 0);
+    }
+
+    @NonNull
+    private String convertToBaseEntityType(@NonNull String entityType) {
+        return ENTITY_TYPE_MAPPING.getOrDefault(entityType, entityType);
+    }
+
+    /**
+     * Given the links extracted from a piece of text, returns the frequency of each entity
+     * type.
+     */
+    @NonNull
+    static EntityTypeCounter fromTextLinks(@NonNull TextLinks links) {
+        EntityTypeCounter counter = new EntityTypeCounter();
+        for (TextLinks.TextLink link : links.getLinks()) {
+            if (link.getEntityCount() == 0) {
+                continue;
+            }
+            String entityType = link.getEntity(0);
+            counter.increment(entityType);
+        }
+        return counter;
+    }
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index b041842..f2db2da 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -26,7 +26,6 @@
 import android.os.Process;
 import android.service.notification.NotificationAssistantService;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.LruCache;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.TextClassification;
@@ -356,7 +355,7 @@
                                         TextClassifier.HINT_TEXT_IS_NOT_EDITABLE)))
                 .build();
         TextLinks links = mTextClassifier.generateLinks(textLinksRequest);
-        ArrayMap<String, Integer> entityTypeFrequency = getEntityTypeFrequency(links);
+        EntityTypeCounter entityTypeCounter = EntityTypeCounter.fromTextLinks(links);
 
         ArrayList<Notification.Action> actions = new ArrayList<>();
         for (TextLinks.TextLink link : links.getLinks()) {
@@ -364,7 +363,7 @@
             // case where a notification contains e.g. a list of phone numbers. In such cases, the
             // user likely wants to act on the whole list rather than an individual entity.
             if (link.getEntityCount() == 0
-                    || entityTypeFrequency.get(link.getEntity(0)) != 1) {
+                    || entityTypeCounter.getCount(link.getEntity(0)) != 1) {
                 continue;
             }
 
@@ -384,8 +383,7 @@
                         remoteAction.getIcon(),
                         remoteAction.getTitle(),
                         remoteAction.getActionIntent())
-                        .setSemanticAction(
-                                Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION)
+                        .setContextual(true)
                         .addExtras(Bundle.forPair(KEY_ACTION_TYPE, classification.getEntity(0)))
                         .build();
                 actions.add(action);
@@ -398,25 +396,4 @@
         }
         return actions;
     }
-
-    /**
-     * Given the links extracted from a piece of text, returns the frequency of each entity
-     * type.
-     */
-    @NonNull
-    private ArrayMap<String, Integer> getEntityTypeFrequency(@NonNull TextLinks links) {
-        ArrayMap<String, Integer> entityTypeCount = new ArrayMap<>();
-        for (TextLinks.TextLink link : links.getLinks()) {
-            if (link.getEntityCount() == 0) {
-                continue;
-            }
-            String entityType = link.getEntity(0);
-            if (entityTypeCount.containsKey(entityType)) {
-                entityTypeCount.put(entityType, entityTypeCount.get(entityType) + 1);
-            } else {
-                entityTypeCount.put(entityType, 1);
-            }
-        }
-        return entityTypeCount;
-    }
 }
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java
new file mode 100644
index 0000000..2d29c7b
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.ext.services.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.view.textclassifier.TextClassifier;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class EntityTypeCounterTest {
+    private EntityTypeCounter mCounter;
+
+    @Before
+    public void setup() {
+        mCounter = new EntityTypeCounter();
+    }
+
+    @Test
+    public void testIncrementAndGetCount() {
+        mCounter.increment(TextClassifier.TYPE_URL);
+        mCounter.increment(TextClassifier.TYPE_URL);
+        mCounter.increment(TextClassifier.TYPE_URL);
+
+        mCounter.increment(TextClassifier.TYPE_PHONE);
+        mCounter.increment(TextClassifier.TYPE_PHONE);
+
+        assertThat(mCounter.getCount(TextClassifier.TYPE_URL)).isEqualTo(3);
+        assertThat(mCounter.getCount(TextClassifier.TYPE_PHONE)).isEqualTo(2);
+        assertThat(mCounter.getCount(TextClassifier.TYPE_DATE_TIME)).isEqualTo(0);
+    }
+
+    @Test
+    public void testIncrementAndGetCount_typeDateAndDateTime() {
+        mCounter.increment(TextClassifier.TYPE_DATE_TIME);
+        mCounter.increment(TextClassifier.TYPE_DATE);
+
+        assertThat(mCounter.getCount(TextClassifier.TYPE_DATE_TIME)).isEqualTo(2);
+        assertThat(mCounter.getCount(TextClassifier.TYPE_DATE)).isEqualTo(2);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
index 2387b01..5e5c22a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
@@ -16,8 +16,8 @@
 package com.android.settingslib.applications;
 
 import android.content.Context;
+import android.permission.PermissionControllerManager;
 import android.permission.RuntimePermissionPresentationInfo;
-import android.permission.RuntimePermissionPresenter;
 
 import java.text.Collator;
 import java.util.ArrayList;
@@ -28,9 +28,9 @@
 
     public static void getPermissionSummary(Context context, String pkg,
             final PermissionsResultCallback callback) {
-        final RuntimePermissionPresenter presenter =
-                RuntimePermissionPresenter.getInstance(context);
-        presenter.getAppPermissions(pkg, permissions -> {
+        final PermissionControllerManager permController =
+                context.getSystemService(PermissionControllerManager.class);
+        permController.getAppPermissions(pkg, permissions -> {
             final int permissionCount = permissions.size();
 
             int grantedStandardCount = 0;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 58feef5..24d7011 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -35,7 +35,6 @@
 
 public class A2dpProfile implements LocalBluetoothProfile {
     private static final String TAG = "A2dpProfile";
-    private static boolean V = false;
 
     private Context mContext;
 
@@ -60,7 +59,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothA2dp) proxy;
             // We just bound to the service, so refresh the UI for any connected A2DP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -79,7 +77,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -302,7 +299,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.A2DP,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 988062d..873dd1a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -55,7 +55,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected");
             mService = (BluetoothA2dpSink) proxy;
             // We just bound to the service, so refresh the UI for any connected A2DP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -74,7 +73,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 62507f5..6b6df9b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -58,7 +58,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothHeadset) proxy;
             // We just bound to the service, so refresh the UI for any connected HFP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -80,7 +79,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG,"Bluetooth service disconnected");
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index adb5ab3..577d98d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -51,7 +51,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothHearingAid) proxy;
             // We just bound to the service, so refresh the UI for any connected HearingAid devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -77,7 +76,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -234,7 +232,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HEARING_AID,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 4879144..c6bb2b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -59,7 +59,6 @@
 
         @Override
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected");
             mService = (BluetoothHeadsetClient) proxy;
             // We just bound to the service, so refresh the UI for any connected HFP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -80,7 +79,6 @@
 
         @Override
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index 61e5b6b..4dc050c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -58,7 +58,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected :-), profile:" + profile);
             mService = (BluetoothHidDevice) proxy;
             // We just bound to the service, so refresh the UI for any connected HID devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -78,7 +77,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady = false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 75d16db..ca840d9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -33,7 +33,6 @@
  */
 public class HidProfile implements LocalBluetoothProfile {
     private static final String TAG = "HidProfile";
-    private static boolean V = true;
 
     private BluetoothHidHost mService;
     private boolean mIsProfileReady;
@@ -51,7 +50,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothHidHost) proxy;
             // We just bound to the service, so refresh the UI for any connected HID devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -70,7 +68,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -186,7 +183,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_HOST,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 1e22f44..6acdcac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -59,7 +59,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothMapClient) proxy;
             // We just bound to the service, so refresh the UI for any connected MAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,7 +80,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index 7582024..28975d4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -58,7 +58,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected");
             mService = (BluetoothMap) proxy;
             // We just bound to the service, so refresh the UI for any connected MAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -80,7 +79,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected");
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 7b81162..2d0a090 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -34,7 +34,6 @@
  */
 public class PanProfile implements LocalBluetoothProfile {
     private static final String TAG = "PanProfile";
-    private static boolean V = true;
 
     private BluetoothPan mService;
     private boolean mIsProfileReady;
@@ -53,13 +52,11 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothPan) proxy;
             mIsProfileReady=true;
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -173,7 +170,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.PAN, mService);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 1f15601..4672393 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -55,7 +55,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothPbapClient) proxy;
             // We just bound to the service, so refresh the UI for any connected PBAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -74,7 +73,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady = false;
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index adef0841..1b3c453 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -33,7 +33,6 @@
  */
 public class PbapServerProfile implements LocalBluetoothProfile {
     private static final String TAG = "PbapServerProfile";
-    private static boolean V = true;
 
     private BluetoothPbap mService;
     private boolean mIsProfileReady;
@@ -56,13 +55,11 @@
             implements BluetoothPbap.ServiceListener {
 
         public void onServiceConnected(BluetoothPbap proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
             mService = (BluetoothPbap) proxy;
             mIsProfileReady=true;
         }
 
         public void onServiceDisconnected() {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
             mIsProfileReady=false;
         }
     }
@@ -142,7 +139,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 mService.close();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index b4acc48..ea2ebde 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -57,7 +57,6 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothSap) proxy;
             // We just bound to the service, so refresh the UI for any connected SAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -79,7 +78,6 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index f2b2719..d0ffe7a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -242,7 +242,7 @@
                 Bundle args = new Bundle();
                 args.putInt(Settings.CALL_METHOD_USER_KEY,
                         ActivityManager.getService().getCurrentUser().id);
-                Bundle b = provider.call(resolveCallingPackage(),
+                Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
                         Settings.CALL_METHOD_DELETE_CONFIG, compositeKey, args);
                 success = (b != null && b.getInt(SettingsProvider.RESULT_ROWS_DELETED) == 1);
             } catch (RemoteException e) {
@@ -261,7 +261,7 @@
                 if (namespace != null) {
                     args.putString(Settings.CALL_METHOD_PREFIX_KEY, namespace);
                 }
-                Bundle b = provider.call(resolveCallingPackage(),
+                Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
                         Settings.CALL_METHOD_LIST_CONFIG, null, args);
                 if (b != null) {
                     Map<String, String> flagsToValues =
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1727e75..bce5593 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1097,7 +1097,7 @@
                 case MUTATION_OPERATION_INSERT: {
                     return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
                             UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
-                            getCallingPackage(), false, null);
+                            resolveCallingPackage(), false, null);
                 }
 
                 case MUTATION_OPERATION_DELETE: {
@@ -1107,7 +1107,7 @@
 
                 case MUTATION_OPERATION_RESET: {
                     mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
-                            UserHandle.USER_SYSTEM, getCallingPackage(), mode, null, prefix);
+                            UserHandle.USER_SYSTEM, resolveCallingPackage(), mode, null, prefix);
                 } return true;
             }
         }
@@ -2247,6 +2247,22 @@
         return !(TextUtils.isEmpty(key) || SettingsState.isBinary(key));
     }
 
+    private String resolveCallingPackage() {
+        switch (Binder.getCallingUid()) {
+            case Process.ROOT_UID: {
+                return "root";
+            }
+
+            case Process.SHELL_UID: {
+                return "com.android.shell";
+            }
+
+            default: {
+                return getCallingPackage();
+            }
+        }
+    }
+
     private static final class Arguments {
         private static final Pattern WHERE_PATTERN_WITH_PARAM_NO_BRACKETS =
                 Pattern.compile("[\\s]*name[\\s]*=[\\s]*\\?[\\s]*");
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 8cfc2a6..bff2c84 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -82,6 +82,7 @@
     <uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
     <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
     <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+    <uses-permission android:name="android.permission.MANAGE_ROLLBACKS" />
     <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
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 4891e50..5317a6d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -32,10 +32,11 @@
     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.
+     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but allows
+     * you to specify the callback that is executed on the UI thread after the intent is sent.
      */
-    void startPendingIntentDismissingKeyguard(PendingIntent intent, Runnable intentSentCallback);
+    void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentUiThreadCallback);
     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/core/java/android/hardware/display/BrightnessCorrection.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java
similarity index 62%
copy from core/java/android/hardware/display/BrightnessCorrection.aidl
copy to packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java
index 3abe29c..51fcb0a 100644
--- a/core/java/android/hardware/display/BrightnessCorrection.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java
@@ -14,6 +14,21 @@
  * limitations under the License.
  */
 
-package android.hardware.display;
+package com.android.systemui.shared.system;
 
-parcelable BrightnessCorrection;
+import android.content.Context;
+
+/**
+ * Wraps a context to expose some methods for launcher to call.
+ */
+public class ContextCompat {
+    private final Context mWrapped;
+
+    public ContextCompat(Context context) {
+        mWrapped = context;
+    }
+
+    public int getUserId() {
+        return mWrapped.getUserId();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 570d351..27d624a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -16,6 +16,8 @@
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
 
 import java.util.Objects;
 import java.util.TimeZone;
@@ -82,6 +84,24 @@
                     }
                 }
             };
+    private final StatusBarStateController.StateListener mStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onStateChanged(int newState) {
+                    if (mBigClockContainer == null) {
+                        return;
+                    }
+                    if (newState == StatusBarState.SHADE) {
+                        if (mBigClockContainer.getVisibility() == View.VISIBLE) {
+                            mBigClockContainer.setVisibility(View.INVISIBLE);
+                        }
+                    } else {
+                        if (mBigClockContainer.getVisibility() == View.INVISIBLE) {
+                            mBigClockContainer.setVisibility(View.VISIBLE);
+                        }
+                    }
+                }
+    };
 
     public KeyguardClockSwitch(Context context) {
         this(context, null);
@@ -104,12 +124,14 @@
         super.onAttachedToWindow();
         Dependency.get(PluginManager.class).addPluginListener(mClockPluginListener,
                 ClockPlugin.class);
+        Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         Dependency.get(PluginManager.class).removePluginListener(mClockPluginListener);
+        Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
     }
 
     /**
@@ -238,4 +260,9 @@
     PluginListener getClockPluginListener() {
         return mClockPluginListener;
     }
+
+    @VisibleForTesting (otherwise = VisibleForTesting.NONE)
+    StatusBarStateController.StateListener getStateListener() {
+        return mStateListener;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 2605a11..9d81064 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -56,15 +56,16 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
@@ -156,6 +157,12 @@
     public static final String LEAK_REPORT_EMAIL_NAME = "leak_report_email";
 
     /**
+     * Whether this platform supports long-pressing notifications to show notification channel
+     * settings.
+     */
+    public static final String ALLOW_NOTIFICATION_LONG_PRESS_NAME = "allow_notif_longpress";
+
+    /**
      * Key for getting a background Looper for background work.
      */
     public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>(BG_LOOPER_NAME);
@@ -270,6 +277,8 @@
     @Inject
     Lazy<NotificationAlertingManager> mNotificationAlertingManager;
     @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
+    @Inject Lazy<AutoHideController> mAutoHideController;
+    @Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
     @Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
     @Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
     @Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
@@ -442,6 +451,14 @@
         mProviders.put(BubbleController.class, mBubbleController::get);
         mProviders.put(NotificationEntryManager.class, mNotificationEntryManager::get);
         mProviders.put(NotificationAlertingManager.class, mNotificationAlertingManager::get);
+        mProviders.put(ForegroundServiceNotificationListener.class,
+                mForegroundServiceNotificationListener::get);
+
+        // TODO(b/118592525): to support multi-display , we start to add something which is
+        //                    per-display, while others may be global. I think it's time to add
+        //                    a new class maybe named DisplayDependency to solve per-display
+        //                    Dependency problem.
+        mProviders.put(AutoHideController.class, mAutoHideController::get);
 
         sDependency = this;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
index 1ee1dcf..f324a05b 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
@@ -219,12 +219,6 @@
     /**
      */
     @Binds
-    public abstract ForegroundServiceController provideForegroundService(
-            ForegroundServiceControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
     public abstract PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
index 5c9b999..88e32cb 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -42,9 +42,8 @@
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.plugins.PluginManagerImpl;
 import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.NetworkController;
@@ -140,12 +139,6 @@
 
     @Singleton
     @Provides
-    public ShadeController provideShadeController(Context context) {
-        return SysUiServiceProvider.getComponent(context, StatusBar.class);
-    }
-
-    @Singleton
-    @Provides
     public SensorPrivacyManager provideSensorPrivacyManager(Context context) {
         return context.getSystemService(SensorPrivacyManager.class);
     }
@@ -181,4 +174,11 @@
     public ConfigurationController provideConfigurationController(Context context) {
         return new ConfigurationControllerImpl(context);
     }
+
+    @Singleton
+    @Provides
+    public AutoHideController provideAutoHideController(Context context,
+            @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+        return new AutoHideController(context, mainHandler);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index ae6ee2a..df0d787 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -15,65 +15,158 @@
 package com.android.systemui;
 
 import android.annotation.Nullable;
+import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
+import android.util.SparseArray;
 
-public interface ForegroundServiceController {
-    /**
-     * @param sbn notification that was just posted
-     * @param importance
-     */
-    void addNotification(StatusBarNotification sbn, int importance);
+import com.android.internal.messages.nano.SystemMessageProto;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Tracks state of foreground services and notifications related to foreground services per user.
+ */
+@Singleton
+public class ForegroundServiceController {
+
+    private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
+    private final Object mMutex = new Object();
+
+    @Inject
+    public ForegroundServiceController() {
+    }
 
     /**
-     * @param sbn notification that was just changed in some way
-     * @param newImportance
-     */
-    void updateNotification(StatusBarNotification sbn, int newImportance);
-
-    /**
-     * @param sbn notification that was just canceled
-     */
-    boolean removeNotification(StatusBarNotification sbn);
-
-    /**
-     * @param userId
      * @return true if this user has services missing notifications and therefore needs a
      * disclosure notification.
      */
-    boolean isDungeonNeededForUser(int userId);
-
-    /**
-     * @param sbn
-     * @return true if sbn is the system-provided "dungeon" (list of running foreground services).
-     */
-    boolean isDungeonNotification(StatusBarNotification sbn);
-
-    /**
-     * @return true if sbn is one of the window manager "drawing over other apps" notifications
-     */
-    boolean isSystemAlertNotification(StatusBarNotification sbn);
-
-    /**
-     * Returns the key of the foreground service from this package using the standard template,
-     * if one exists.
-     */
-    @Nullable String getStandardLayoutKey(int userId, String pkg);
+    public boolean isDisclosureNeededForUser(int userId) {
+        synchronized (mMutex) {
+            final ForegroundServicesUserState services = mUserServices.get(userId);
+            if (services == null) return false;
+            return services.isDisclosureNeeded();
+        }
+    }
 
     /**
      * @return true if this user/pkg has a missing or custom layout notification and therefore needs
      * a disclosure notification for system alert windows.
      */
-    boolean isSystemAlertWarningNeeded(int userId, String pkg);
+    public boolean isSystemAlertWarningNeeded(int userId, String pkg) {
+        synchronized (mMutex) {
+            final ForegroundServicesUserState services = mUserServices.get(userId);
+            if (services == null) return false;
+            return services.getStandardLayoutKey(pkg) == null;
+        }
+    }
+
+    /**
+     * Returns the key of the foreground service from this package using the standard template,
+     * if one exists.
+     */
+    @Nullable
+    public String getStandardLayoutKey(int userId, String pkg) {
+        synchronized (mMutex) {
+            final ForegroundServicesUserState services = mUserServices.get(userId);
+            if (services == null) return null;
+            return services.getStandardLayoutKey(pkg);
+        }
+    }
+
+    /**
+     * Gets active app ops for this user and package
+     */
+    @Nullable
+    public ArraySet<Integer> getAppOps(int userId, String pkg) {
+        synchronized (mMutex) {
+            final ForegroundServicesUserState services = mUserServices.get(userId);
+            if (services == null) {
+                return null;
+            }
+            return services.getFeatures(pkg);
+        }
+    }
 
     /**
      * Records active app ops. App Ops are stored in FSC in addition to NotificationData in
      * case they change before we have a notification to tag.
      */
-    void onAppOpChanged(int code, int uid, String packageName, boolean active);
+    public void onAppOpChanged(int code, int uid, String packageName, boolean active) {
+        int userId = UserHandle.getUserId(uid);
+        synchronized (mMutex) {
+            ForegroundServicesUserState userServices = mUserServices.get(userId);
+            if (userServices == null) {
+                userServices = new ForegroundServicesUserState();
+                mUserServices.put(userId, userServices);
+            }
+            if (active) {
+                userServices.addOp(packageName, code);
+            } else {
+                userServices.removeOp(packageName, code);
+            }
+        }
+    }
 
     /**
-     * Gets active app ops for this user and package
+     * Looks up the {@link ForegroundServicesUserState} for the given {@code userId}, then performs
+     * the given {@link UserStateUpdateCallback} on it.  If no state exists for the user ID, creates
+     * a new one if {@code createIfNotFound} is true, then performs the update on the new state.
+     * If {@code createIfNotFound} is false, no update is performed.
+     *
+     * @return false if no user state was found and none was created; true otherwise.
      */
-    @Nullable ArraySet<Integer> getAppOps(int userId, String packageName);
+    boolean updateUserState(int userId,
+            UserStateUpdateCallback updateCallback,
+            boolean createIfNotFound) {
+        synchronized (mMutex) {
+            ForegroundServicesUserState userState = mUserServices.get(userId);
+            if (userState == null) {
+                if (createIfNotFound) {
+                    userState = new ForegroundServicesUserState();
+                    mUserServices.put(userId, userState);
+                } else {
+                    return false;
+                }
+            }
+            return updateCallback.updateUserState(userState);
+        }
+    }
+
+    /**
+     * @return true if {@code sbn} is the system-provided disclosure notification containing the
+     * list of running foreground services.
+     */
+    public boolean isDisclosureNotification(StatusBarNotification sbn) {
+        return sbn.getId() == SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES
+                && sbn.getTag() == null
+                && sbn.getPackageName().equals("android");
+    }
+
+    /**
+     * @return true if sbn is one of the window manager "drawing over other apps" notifications
+     */
+    public boolean isSystemAlertNotification(StatusBarNotification sbn) {
+        return sbn.getPackageName().equals("android")
+                && sbn.getTag() != null
+                && sbn.getTag().contains("AlertWindowNotification");
+    }
+
+    /**
+     * Callback provided to {@link #updateUserState(int, UserStateUpdateCallback, boolean)}
+     * to perform the update.
+     */
+    interface UserStateUpdateCallback {
+        /**
+         * Perform update operations on the provided {@code userState}.
+         *
+         * @return true if the update succeeded.
+         */
+        boolean updateUserState(ForegroundServicesUserState userState);
+
+        /** Called if the state was not found and was not created. */
+        default void userStateNotFound(int userId) {
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
deleted file mode 100644
index ae446dd..0000000
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java
+++ /dev/null
@@ -1,309 +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.systemui;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.messages.nano.SystemMessageProto;
-
-import java.util.Arrays;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * Foreground service controller, a/k/a Dianne's Dungeon.
- */
-@Singleton
-public class ForegroundServiceControllerImpl
-        implements ForegroundServiceController {
-
-    // shelf life of foreground services before they go bad
-    public static final long FG_SERVICE_GRACE_MILLIS = 5000;
-
-    private static final String TAG = "FgServiceController";
-    private static final boolean DBG = false;
-
-    private final Context mContext;
-    private final SparseArray<UserServices> mUserServices = new SparseArray<>();
-    private final Object mMutex = new Object();
-
-    @Inject
-    public ForegroundServiceControllerImpl(Context context) {
-        mContext = context;
-    }
-
-    @Override
-    public boolean isDungeonNeededForUser(int userId) {
-        synchronized (mMutex) {
-            final UserServices services = mUserServices.get(userId);
-            if (services == null) return false;
-            return services.isDungeonNeeded();
-        }
-    }
-
-    @Override
-    public boolean isSystemAlertWarningNeeded(int userId, String pkg) {
-        synchronized (mMutex) {
-            final UserServices services = mUserServices.get(userId);
-            if (services == null) return false;
-            return services.getStandardLayoutKey(pkg) == null;
-        }
-    }
-
-    @Override
-    public String getStandardLayoutKey(int userId, String pkg) {
-        synchronized (mMutex) {
-            final UserServices services = mUserServices.get(userId);
-            if (services == null) return null;
-            return services.getStandardLayoutKey(pkg);
-        }
-    }
-
-    @Override
-    public ArraySet<Integer> getAppOps(int userId, String pkg) {
-        synchronized (mMutex) {
-            final UserServices services = mUserServices.get(userId);
-            if (services == null) {
-                return null;
-            }
-            return services.getFeatures(pkg);
-        }
-    }
-
-    @Override
-    public void onAppOpChanged(int code, int uid, String packageName, boolean active) {
-        int userId = UserHandle.getUserId(uid);
-        synchronized (mMutex) {
-            UserServices userServices = mUserServices.get(userId);
-            if (userServices == null) {
-                userServices = new UserServices();
-                mUserServices.put(userId, userServices);
-            }
-            if (active) {
-                userServices.addOp(packageName, code);
-            } else {
-                userServices.removeOp(packageName, code);
-            }
-        }
-    }
-
-    @Override
-    public void addNotification(StatusBarNotification sbn, int importance) {
-        updateNotification(sbn, importance);
-    }
-
-    @Override
-    public boolean removeNotification(StatusBarNotification sbn) {
-        synchronized (mMutex) {
-            final UserServices userServices = mUserServices.get(sbn.getUserId());
-            if (userServices == null) {
-                if (DBG) {
-                    Log.w(TAG, String.format(
-                            "user %d with no known notifications got removeNotification for %s",
-                            sbn.getUserId(), sbn));
-                }
-                return false;
-            }
-            if (isDungeonNotification(sbn)) {
-                // if you remove the dungeon entirely, we take that to mean there are
-                // no running services
-                userServices.setRunningServices(null, 0);
-                return true;
-            } else {
-                // this is safe to call on any notification, not just FLAG_FOREGROUND_SERVICE
-                return userServices.removeNotification(sbn.getPackageName(), sbn.getKey());
-            }
-        }
-    }
-
-    @Override
-    public void updateNotification(StatusBarNotification sbn, int newImportance) {
-        synchronized (mMutex) {
-            UserServices userServices = mUserServices.get(sbn.getUserId());
-            if (userServices == null) {
-                userServices = new UserServices();
-                mUserServices.put(sbn.getUserId(), userServices);
-            }
-
-            if (isDungeonNotification(sbn)) {
-                final Bundle extras = sbn.getNotification().extras;
-                if (extras != null) {
-                    final String[] svcs = extras.getStringArray(Notification.EXTRA_FOREGROUND_APPS);
-                    userServices.setRunningServices(svcs, sbn.getNotification().when);
-                }
-            } else {
-                userServices.removeNotification(sbn.getPackageName(), sbn.getKey());
-                if (0 != (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE)) {
-                    if (newImportance > NotificationManager.IMPORTANCE_MIN) {
-                        userServices.addImportantNotification(sbn.getPackageName(), sbn.getKey());
-                    }
-                    final Notification.Builder builder = Notification.Builder.recoverBuilder(
-                            mContext, sbn.getNotification());
-                    if (builder.usesStandardHeader()) {
-                        userServices.addStandardLayoutNotification(
-                                sbn.getPackageName(), sbn.getKey());
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public boolean isDungeonNotification(StatusBarNotification sbn) {
-        return sbn.getId() == SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES
-                && sbn.getTag() == null
-                && sbn.getPackageName().equals("android");
-    }
-
-    @Override
-    public boolean isSystemAlertNotification(StatusBarNotification sbn) {
-        return sbn.getPackageName().equals("android")
-                && sbn.getTag() != null
-                && sbn.getTag().contains("AlertWindowNotification");
-    }
-
-    /**
-     * Struct to track relevant packages and notifications for a userid's foreground services.
-     */
-    private static class UserServices {
-        private String[] mRunning = null;
-        private long mServiceStartTime = 0;
-        // package -> sufficiently important posted notification keys
-        private ArrayMap<String, ArraySet<String>> mImportantNotifications = new ArrayMap<>(1);
-        // package -> standard layout posted notification keys
-        private ArrayMap<String, ArraySet<String>> mStandardLayoutNotifications = new ArrayMap<>(1);
-
-        // package -> app ops
-        private ArrayMap<String, ArraySet<Integer>> mAppOps = new ArrayMap<>(1);
-
-        public void setRunningServices(String[] pkgs, long serviceStartTime) {
-            mRunning = pkgs != null ? Arrays.copyOf(pkgs, pkgs.length) : null;
-            mServiceStartTime = serviceStartTime;
-        }
-
-        public void addOp(String pkg, int op) {
-            if (mAppOps.get(pkg) == null) {
-                mAppOps.put(pkg, new ArraySet<>(3));
-            }
-            mAppOps.get(pkg).add(op);
-        }
-
-        public boolean removeOp(String pkg, int op) {
-            final boolean found;
-            final ArraySet<Integer> keys = mAppOps.get(pkg);
-            if (keys == null) {
-                found = false;
-            } else {
-                found = keys.remove(op);
-                if (keys.size() == 0) {
-                    mAppOps.remove(pkg);
-                }
-            }
-            return found;
-        }
-
-        public void addImportantNotification(String pkg, String key) {
-            addNotification(mImportantNotifications, pkg, key);
-        }
-
-        public boolean removeImportantNotification(String pkg, String key) {
-            return removeNotification(mImportantNotifications, pkg, key);
-        }
-
-        public void addStandardLayoutNotification(String pkg, String key) {
-            addNotification(mStandardLayoutNotifications, pkg, key);
-        }
-
-        public boolean removeStandardLayoutNotification(String pkg, String key) {
-            return removeNotification(mStandardLayoutNotifications, pkg, key);
-        }
-
-        public boolean removeNotification(String pkg, String key) {
-            boolean removed = false;
-            removed |= removeImportantNotification(pkg, key);
-            removed |= removeStandardLayoutNotification(pkg, key);
-            return removed;
-        }
-
-        public void addNotification(ArrayMap<String, ArraySet<String>> map, String pkg,
-                String key) {
-            if (map.get(pkg) == null) {
-                map.put(pkg, new ArraySet<>());
-            }
-            map.get(pkg).add(key);
-        }
-
-        public boolean removeNotification(ArrayMap<String, ArraySet<String>> map,
-                String pkg, String key) {
-            final boolean found;
-            final ArraySet<String> keys = map.get(pkg);
-            if (keys == null) {
-                found = false;
-            } else {
-                found = keys.remove(key);
-                if (keys.size() == 0) {
-                    map.remove(pkg);
-                }
-            }
-            return found;
-        }
-
-        public boolean isDungeonNeeded() {
-            if (mRunning != null
-                && System.currentTimeMillis() - mServiceStartTime >= FG_SERVICE_GRACE_MILLIS) {
-
-                for (String pkg : mRunning) {
-                    final ArraySet<String> set = mImportantNotifications.get(pkg);
-                    if (set == null || set.size() == 0) {
-                        return true;
-                    }
-                }
-            }
-            return false;
-        }
-
-        public ArraySet<Integer> getFeatures(String pkg) {
-            return mAppOps.get(pkg);
-        }
-
-        public String getStandardLayoutKey(String pkg) {
-            final ArraySet<String> set = mStandardLayoutNotifications.get(pkg);
-            if (set == null || set.size() == 0) {
-                return null;
-            }
-            return set.valueAt(0);
-        }
-
-        @Override
-        public String toString() {
-            return "UserServices{" +
-                    "mRunning=" + Arrays.toString(mRunning) +
-                    ", mServiceStartTime=" + mServiceStartTime +
-                    ", mImportantNotifications=" + mImportantNotifications +
-                    ", mStandardLayoutNotifications=" + mStandardLayoutNotifications +
-                    '}';
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
new file mode 100644
index 0000000..96f216e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Updates foreground service notification state in response to notification data events. */
+@Singleton
+public class ForegroundServiceNotificationListener {
+
+    private static final String TAG = "FgServiceController";
+    private static final boolean DBG = false;
+
+    private final Context mContext;
+    private final ForegroundServiceController mForegroundServiceController;
+
+    @Inject
+    public ForegroundServiceNotificationListener(Context context,
+            ForegroundServiceController foregroundServiceController,
+            NotificationEntryManager notificationEntryManager) {
+        mContext = context;
+        mForegroundServiceController = foregroundServiceController;
+        notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onPendingEntryAdded(NotificationEntry entry) {
+                addNotification(entry.notification, entry.importance);
+            }
+
+            @Override
+            public void onEntryUpdated(NotificationEntry entry) {
+                updateNotification(entry.notification, entry.importance);
+            }
+
+            @Override
+            public void onEntryRemoved(
+                    NotificationEntry entry,
+                    NotificationVisibility visibility,
+                    boolean removedByUser) {
+                removeNotification(entry.notification);
+            }
+        });
+    }
+
+    /**
+     * @param sbn notification that was just posted
+     */
+    private void addNotification(StatusBarNotification sbn, int importance) {
+        updateNotification(sbn, importance);
+    }
+
+    /**
+     * @param sbn notification that was just removed
+     */
+    private void removeNotification(StatusBarNotification sbn) {
+        mForegroundServiceController.updateUserState(
+                sbn.getUserId(),
+                new ForegroundServiceController.UserStateUpdateCallback() {
+                    @Override
+                    public boolean updateUserState(ForegroundServicesUserState userState) {
+                        if (mForegroundServiceController.isDisclosureNotification(sbn)) {
+                            // if you remove the dungeon entirely, we take that to mean there are
+                            // no running services
+                            userState.setRunningServices(null, 0);
+                            return true;
+                        } else {
+                            // this is safe to call on any notification, not just
+                            // FLAG_FOREGROUND_SERVICE
+                            return userState.removeNotification(sbn.getPackageName(), sbn.getKey());
+                        }
+                    }
+
+                    @Override
+                    public void userStateNotFound(int userId) {
+                        if (DBG) {
+                            Log.w(TAG, String.format(
+                                    "user %d with no known notifications got removeNotification "
+                                            + "for %s",
+                                    sbn.getUserId(), sbn));
+                        }
+                    }
+                },
+                false /* don't create */);
+    }
+
+    /**
+     * @param sbn notification that was just changed in some way
+     */
+    private void updateNotification(StatusBarNotification sbn, int newImportance) {
+        mForegroundServiceController.updateUserState(
+                sbn.getUserId(),
+                userState -> {
+                    if (mForegroundServiceController.isDisclosureNotification(sbn)) {
+                        final Bundle extras = sbn.getNotification().extras;
+                        if (extras != null) {
+                            final String[] svcs = extras.getStringArray(
+                                    Notification.EXTRA_FOREGROUND_APPS);
+                            userState.setRunningServices(svcs, sbn.getNotification().when);
+                        }
+                    } else {
+                        userState.removeNotification(sbn.getPackageName(), sbn.getKey());
+                        if (0 != (sbn.getNotification().flags
+                                & Notification.FLAG_FOREGROUND_SERVICE)) {
+                            if (newImportance > NotificationManager.IMPORTANCE_MIN) {
+                                userState.addImportantNotification(sbn.getPackageName(),
+                                        sbn.getKey());
+                            }
+                            final Notification.Builder builder =
+                                    Notification.Builder.recoverBuilder(
+                                            mContext, sbn.getNotification());
+                            if (builder.usesStandardHeader()) {
+                                userState.addStandardLayoutNotification(
+                                        sbn.getPackageName(), sbn.getKey());
+                            }
+                        }
+                    }
+                    return true;
+                },
+                true /* create if not found */);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java b/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java
new file mode 100644
index 0000000..a8ae654
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import java.util.Arrays;
+
+/**
+ * Struct to track relevant packages and notifications for a userid's foreground services.
+ */
+class ForegroundServicesUserState {
+    // shelf life of foreground services before they go bad
+    private static final long FG_SERVICE_GRACE_MILLIS = 5000;
+
+    private String[] mRunning = null;
+    private long mServiceStartTime = 0;
+    // package -> sufficiently important posted notification keys
+    private ArrayMap<String, ArraySet<String>> mImportantNotifications = new ArrayMap<>(1);
+    // package -> standard layout posted notification keys
+    private ArrayMap<String, ArraySet<String>> mStandardLayoutNotifications = new ArrayMap<>(1);
+
+    // package -> app ops
+    private ArrayMap<String, ArraySet<Integer>> mAppOps = new ArrayMap<>(1);
+
+    public void setRunningServices(String[] pkgs, long serviceStartTime) {
+        mRunning = pkgs != null ? Arrays.copyOf(pkgs, pkgs.length) : null;
+        mServiceStartTime = serviceStartTime;
+    }
+
+    public void addOp(String pkg, int op) {
+        if (mAppOps.get(pkg) == null) {
+            mAppOps.put(pkg, new ArraySet<>(3));
+        }
+        mAppOps.get(pkg).add(op);
+    }
+
+    public boolean removeOp(String pkg, int op) {
+        final boolean found;
+        final ArraySet<Integer> keys = mAppOps.get(pkg);
+        if (keys == null) {
+            found = false;
+        } else {
+            found = keys.remove(op);
+            if (keys.size() == 0) {
+                mAppOps.remove(pkg);
+            }
+        }
+        return found;
+    }
+
+    public void addImportantNotification(String pkg, String key) {
+        addNotification(mImportantNotifications, pkg, key);
+    }
+
+    public boolean removeImportantNotification(String pkg, String key) {
+        return removeNotification(mImportantNotifications, pkg, key);
+    }
+
+    public void addStandardLayoutNotification(String pkg, String key) {
+        addNotification(mStandardLayoutNotifications, pkg, key);
+    }
+
+    public boolean removeStandardLayoutNotification(String pkg, String key) {
+        return removeNotification(mStandardLayoutNotifications, pkg, key);
+    }
+
+    public boolean removeNotification(String pkg, String key) {
+        boolean removed = false;
+        removed |= removeImportantNotification(pkg, key);
+        removed |= removeStandardLayoutNotification(pkg, key);
+        return removed;
+    }
+
+    public void addNotification(ArrayMap<String, ArraySet<String>> map, String pkg,
+            String key) {
+        if (map.get(pkg) == null) {
+            map.put(pkg, new ArraySet<>());
+        }
+        map.get(pkg).add(key);
+    }
+
+    public boolean removeNotification(ArrayMap<String, ArraySet<String>> map,
+            String pkg, String key) {
+        final boolean found;
+        final ArraySet<String> keys = map.get(pkg);
+        if (keys == null) {
+            found = false;
+        } else {
+            found = keys.remove(key);
+            if (keys.size() == 0) {
+                map.remove(pkg);
+            }
+        }
+        return found;
+    }
+
+    public boolean isDisclosureNeeded() {
+        if (mRunning != null
+                && System.currentTimeMillis() - mServiceStartTime
+                >= FG_SERVICE_GRACE_MILLIS) {
+
+            for (String pkg : mRunning) {
+                final ArraySet<String> set = mImportantNotifications.get(pkg);
+                if (set == null || set.size() == 0) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public ArraySet<Integer> getFeatures(String pkg) {
+        return mAppOps.get(pkg);
+    }
+
+    public String getStandardLayoutKey(String pkg) {
+        final ArraySet<String> set = mStandardLayoutNotifications.get(pkg);
+        if (set == null || set.size() == 0) {
+            return null;
+        }
+        return set.valueAt(0);
+    }
+
+    @Override
+    public String toString() {
+        return "UserServices{"
+                + "mRunning=" + Arrays.toString(mRunning)
+                + ", mServiceStartTime=" + mServiceStartTime
+                + ", mImportantNotifications=" + mImportantNotifications
+                + ", mStandardLayoutNotifications=" + mStandardLayoutNotifications
+                + '}';
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index e3bfdc9..d0111cb 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui;
 
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
 
 import android.annotation.Nullable;
@@ -39,9 +40,9 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
@@ -50,6 +51,7 @@
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ScrimState;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -200,6 +202,19 @@
         return new NotificationInterruptionStateProvider(context);
     }
 
+    @Singleton
+    @Provides
+    @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
+    public boolean provideAllowNotificationLongPress() {
+        return true;
+    }
+
+    @Singleton
+    @Provides
+    public ShadeController provideShadeController(Context context) {
+        return SysUiServiceProvider.getComponent(context, StatusBar.class);
+    }
+
     @Module
     protected static class ContextHolder {
         private Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 881aa18..7577609 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -33,8 +33,11 @@
 import android.widget.FrameLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 
 import java.util.ArrayList;
@@ -57,47 +60,31 @@
     private static final String TAG = "BubbleController";
 
     // Enables some subset of notifs to automatically become bubbles
-    public static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
+    private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
     // When a bubble is dismissed, recreate it as a notification
-    public static final boolean DEBUG_DEMOTE_TO_NOTIF = false;
+    private 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 final Context mContext;
+    private final NotificationEntryManager mNotificationEntryManager;
     private BubbleStateChangeListener mStateChangeListener;
     private BubbleExpandListener mExpandListener;
 
-    private Map<String, BubbleView> mBubbles = new HashMap<>();
+    private final Map<String, BubbleView> mBubbles = new HashMap<>();
     private BubbleStackView mStackView;
-    private Point mDisplaySize;
+    private final Point mDisplaySize;
 
     // Bubbles get added to the status bar view
-    @VisibleForTesting
-    protected StatusBarWindowController mStatusBarWindowController;
+    private final StatusBarWindowController mStatusBarWindowController;
 
     // Used for determining view rect for touch interaction
     private Rect mTempRect = new Rect();
 
     /**
-     * Listener to find out about bubble / bubble stack dismissal events.
-     */
-    public interface BubbleDismissListener {
-        /**
-         * Called when the entire stack of bubbles is dismissed by the user.
-         */
-        void onStackDismissed();
-
-        /**
-         * Called when a specific bubble is dismissed by the user.
-         */
-        void onBubbleDismissed(String key);
-    }
-
-    /**
      * Listener to be notified when some states of the bubbles change.
      */
     public interface BubbleStateChangeListener {
@@ -123,17 +110,13 @@
     @Inject
     public BubbleController(Context context, StatusBarWindowController statusBarWindowController) {
         mContext = context;
+        mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mDisplaySize = new Point();
         wm.getDefaultDisplay().getSize(mDisplaySize);
         mStatusBarWindowController = statusBarWindowController;
-    }
 
-    /**
-     * Set a listener to be notified of bubble dismissal events.
-     */
-    public void setDismissListener(BubbleDismissListener listener) {
-        mDismissListener = listener;
+        mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
     }
 
     /**
@@ -180,7 +163,7 @@
     /**
      * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
      */
-    public void dismissStack() {
+    void dismissStack() {
         if (mStackView == null) {
             return;
         }
@@ -190,16 +173,14 @@
         for (String key: mBubbles.keySet()) {
             removeBubble(key);
         }
-        if (mDismissListener != null) {
-            mDismissListener.onStackDismissed();
-        }
+        mNotificationEntryManager.updateNotifications();
         updateBubblesShowing();
     }
 
     /**
      * Adds a bubble associated with the provided notification entry or updates it if it exists.
      */
-    public void addBubble(NotificationData.Entry notif) {
+    public void addBubble(NotificationEntry notif) {
         if (mBubbles.containsKey(notif.key)) {
             // It's an update
             BubbleView bubble = mBubbles.get(notif.key);
@@ -238,18 +219,35 @@
     /**
      * Removes the bubble associated with the {@param uri}.
      */
-    public void removeBubble(String key) {
+    void removeBubble(String key) {
         BubbleView bv = mBubbles.get(key);
         if (mStackView != null && bv != null) {
             mStackView.removeBubble(bv);
             bv.getEntry().setBubbleDismissed(true);
         }
-        if (mDismissListener != null) {
-            mDismissListener.onBubbleDismissed(key);
+
+        NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key);
+        if (entry != null) {
+            entry.setBubbleDismissed(true);
+            if (!DEBUG_DEMOTE_TO_NOTIF) {
+                mNotificationEntryManager.performRemoveNotification(entry.notification);
+            }
         }
+        mNotificationEntryManager.updateNotifications();
+
         updateBubblesShowing();
     }
 
+    @SuppressWarnings("FieldCanBeLocal")
+    private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
+        @Override
+        public void onPendingEntryAdded(NotificationEntry entry) {
+            if (shouldAutoBubble(mContext, entry)) {
+                entry.setIsBubble(true);
+            }
+        }
+    };
+
     private void updateBubblesShowing() {
         boolean hasBubblesShowing = false;
         for (BubbleView bv : mBubbles.values()) {
@@ -277,7 +275,7 @@
         }
         ArrayList<BubbleView> viewsToRemove = new ArrayList<>();
         for (BubbleView bv : mBubbles.values()) {
-            NotificationData.Entry entry = bv.getEntry();
+            NotificationEntry entry = bv.getEntry();
             if (entry != null) {
                 if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
                     viewsToRemove.add(bv);
@@ -309,7 +307,7 @@
     }
 
     @VisibleForTesting
-    public BubbleStackView getStackView() {
+    BubbleStackView getStackView() {
         return mStackView;
     }
 
@@ -317,7 +315,7 @@
     /**
      * Gets an appropriate starting point to position the bubble stack.
      */
-    public static Point getStartPoint(int size, Point displaySize) {
+    private static Point getStartPoint(int size, Point displaySize) {
         final int x = displaySize.x - size + EDGE_OVERLAP;
         final int y = displaySize.y / 4;
         return new Point(x, y);
@@ -326,7 +324,7 @@
     /**
      * Gets an appropriate position for the bubble when the stack is expanded.
      */
-    public static Point getExpandPoint(BubbleStackView view, int size, Point displaySize) {
+    static Point getExpandPoint(BubbleStackView view, int size, Point displaySize) {
         // Same place for now..
         return new Point(EDGE_OVERLAP, size);
     }
@@ -334,7 +332,7 @@
     /**
      * Whether the notification should bubble or not.
      */
-    public static boolean shouldAutoBubble(Context context, NotificationData.Entry entry) {
+    private static boolean shouldAutoBubble(Context context, NotificationEntry entry) {
         if (entry.isBubbleDismissed()) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index dfd18b2..d69e7b3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -40,7 +40,7 @@
 
 import com.android.internal.widget.ViewClippingUtil;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -247,7 +247,7 @@
      * @param bubbleView the view to update in the stack.
      * @param entry the entry to update it with.
      */
-    public void updateBubble(BubbleView bubbleView, NotificationData.Entry entry) {
+    public void updateBubble(BubbleView bubbleView, NotificationEntry entry) {
         // TODO - move to top of bubble stack, make it show its update if it makes sense
         bubbleView.update(entry);
         if (bubbleView.equals(mExpandedBubble)) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 6c47aac..3307992 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -31,7 +31,7 @@
 
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
@@ -43,7 +43,7 @@
     private Context mContext;
     private View mIconView;
 
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private int mBubbleSize;
     private int mIconSize;
 
@@ -72,7 +72,7 @@
      *
      * @param entry the notification to display as a bubble.
      */
-    public void setNotif(NotificationData.Entry entry) {
+    public void setNotif(NotificationEntry entry) {
         removeAllViews();
         // TODO: migrate to inflater
         mIconView = new ImageView(mContext);
@@ -89,7 +89,7 @@
     /**
      * Updates the UI based on the entry.
      */
-    public void update(NotificationData.Entry entry) {
+    public void update(NotificationEntry entry) {
         mEntry = entry;
         Notification n = entry.notification.getNotification();
         Icon ic = n.getLargeIcon() != null ? n.getLargeIcon() : n.getSmallIcon();
@@ -112,7 +112,7 @@
     /**
      * @return the notification entry associated with this bubble.
      */
-    public NotificationData.Entry getEntry() {
+    public NotificationEntry getEntry() {
         return mEntry;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 67aa82d..7656564 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -124,6 +124,7 @@
                 unscheduleTimeTick();
                 break;
             case DOZE_REQUEST_PULSE:
+                scheduleTimeTick();
                 pulseWhileDozing(mMachine.getPulseReason());
                 break;
             case INITIALIZED:
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index dc11b4c..19a7cea 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -132,9 +132,9 @@
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
         final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
-        if (disabled == mDisabled) return;
+        if (displayId != mContext.getDisplayId() || disabled == mDisabled) return;
         mDisabled = disabled;
         if (disabled && mGlobalActions != null) {
             mGlobalActions.dismissDialog();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 6a0e8ad..c4c8bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -50,9 +50,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -68,7 +68,7 @@
  */
 public class KeyguardSliceProvider extends SliceProvider implements
         NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback,
-        NotificationMediaManager.MediaListener {
+        NotificationMediaManager.MediaListener, StatusBarStateController.StateListener {
 
     private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD);
     public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
@@ -109,7 +109,9 @@
     private AlarmManager.AlarmClockInfo mNextAlarmInfo;
     private PendingIntent mPendingIntent;
     protected NotificationMediaManager mMediaManager;
+    private StatusBarStateController mStatusBarStateController;
     protected MediaMetadata mMediaMetaData;
+    protected boolean mDozing;
 
     /**
      * Receiver responsible for time ticking and updating the date format.
@@ -167,9 +169,20 @@
         mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI);
     }
 
-    public void initDependencies() {
-        mMediaManager = Dependency.get(NotificationMediaManager.class);
+    /**
+     * Initialize dependencies that don't exist during {@link android.content.ContentProvider}
+     * instantiation.
+     *
+     * @param mediaManager {@link NotificationMediaManager} singleton.
+     * @param statusBarStateController {@link StatusBarStateController} singleton.
+     */
+    public void initDependencies(
+            NotificationMediaManager mediaManager,
+            StatusBarStateController statusBarStateController) {
+        mMediaManager = mediaManager;
         mMediaManager.addCallback(this);
+        mStatusBarStateController = statusBarStateController;
+        mStatusBarStateController.addCallback(this);
     }
 
     @AnyThread
@@ -179,7 +192,7 @@
         Slice slice;
         synchronized (this) {
             ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
-            if (mMediaMetaData != null) {
+            if (needsMediaLocked()) {
                 addMediaLocked(builder);
             } else {
                 builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
@@ -193,6 +206,10 @@
         return slice;
     }
 
+    protected boolean needsMediaLocked() {
+        return mMediaMetaData != null && mDozing;
+    }
+
     protected void addMediaLocked(ListBuilder listBuilder) {
         if (mMediaMetaData != null) {
             SpannableStringBuilder builder = new SpannableStringBuilder();
@@ -209,7 +226,7 @@
             }
 
             RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder);
-            Icon notificationIcon = mMediaManager.getMediaIcon();
+            Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
             if (notificationIcon != null) {
                 IconCompat icon = IconCompat.createFromIcon(notificationIcon);
                 mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE);
@@ -389,13 +406,35 @@
 
     @Override
     public void onMetadataChanged(MediaMetadata metadata) {
+        final boolean notify;
         synchronized (this) {
+            boolean neededMedia = needsMediaLocked();
             mMediaMetaData = metadata;
+            notify = neededMedia != needsMediaLocked();
         }
-        notifyChange();
+        if (notify) {
+            notifyChange();
+        }
     }
 
     protected void notifyChange() {
         mContentResolver.notifyChange(mSliceUri, null /* observer */);
     }
+
+    @Override
+    public void onDozingChanged(boolean isDozing) {
+        final boolean notify;
+        synchronized (this) {
+            boolean neededMedia = needsMediaLocked();
+            mDozing = isDozing;
+            notify = neededMedia != needsMediaLocked();
+        }
+        if (notify) {
+            notifyChange();
+        }
+    }
+
+    @Override
+    public void onStateChanged(int newState) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index 3991c19..01ee5ca 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -50,6 +50,7 @@
                     object : DialogInterface.OnClickListener {
                         val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE)
 
+                        @Suppress("DEPRECATION")
                         override fun onClick(dialog: DialogInterface?, which: Int) {
                             Dependency.get(ActivityStarter::class.java).startActivity(intent, false)
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index d5b807d..b218e80 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -46,10 +46,13 @@
     }
     private var privacyList = emptyList<PrivacyItem>()
 
+    @Suppress("DEPRECATION")
     private val appOpsController = Dependency.get(AppOpsController::class.java)
     private val userManager = context.getSystemService(UserManager::class.java)
     private var currentUserIds = emptyList<Int>()
+    @Suppress("DEPRECATION")
     private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
+    @Suppress("DEPRECATION")
     private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
     private var listening = false
     val systemApp = PrivacyApplication(context.getString(R.string.device_services), context)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
index dfd3f73..2365e67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
@@ -35,6 +35,8 @@
 import java.util.Collection;
 import java.util.Collections;
 
+import javax.inject.Inject;
+
 public class AutoAddTracker {
 
     private static final String[][] CONVERT_PREFS = {
@@ -48,6 +50,7 @@
     private final ArraySet<String> mAutoAdded;
     private final Context mContext;
 
+    @Inject
     public AutoAddTracker(Context context) {
         mContext = context;
         mAutoAdded = new ArraySet<>(getAdded());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 34d30fe..087a826 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -215,7 +215,10 @@
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != getContext().getDisplayId()) {
+            return;
+        }
         state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
 
         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 3a6b785..dfc3e66 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -56,6 +56,7 @@
 
 import javax.inject.Inject;
 import javax.inject.Named;
+import javax.inject.Provider;
 import javax.inject.Singleton;
 
 /** Platform implementation of the quick settings tile host **/
@@ -74,7 +75,7 @@
     private final PluginManager mPluginManager;
 
     private final List<Callback> mCallbacks = new ArrayList<>();
-    private final AutoTileManager mAutoTiles;
+    private AutoTileManager mAutoTiles;
     private final StatusBarIconController mIconController;
     private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
     private int mCurrentUser;
@@ -87,7 +88,8 @@
             @Named(Dependency.MAIN_HANDLER_NAME) Handler mainHandler,
             @Named(Dependency.BG_LOOPER_NAME) Looper bgLooper,
             PluginManager pluginManager,
-            TunerService tunerService) {
+            TunerService tunerService,
+            Provider<AutoTileManager> autoTiles) {
         mIconController = iconController;
         mContext = context;
         mTunerService = tunerService;
@@ -104,9 +106,9 @@
             // QSTileHost -> XXXTile -> QSTileHost. Posting ensures creation
             // finishes before creating any tiles.
             tunerService.addTunable(this, TILES_SETTING);
+            // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
+            mAutoTiles = autoTiles.get();
         });
-        // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
-        mAutoTiles = new AutoTileManager(context, this);
     }
 
     public StatusBarIconController getIconController() {
@@ -264,7 +266,7 @@
 
     @Override
     public void unmarkTileAsAutoAdded(String spec) {
-        mAutoTiles.unmarkTileAsAutoAdded(spec);
+        if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
     }
 
     public void addTile(String spec) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index f13b565..0fc4fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -54,8 +54,10 @@
     }
 
     @Override
-    public void appTransitionFinished() {
-        mImpl.onAppTransitionFinished();
+    public void appTransitionFinished(int displayId) {
+        if (mContext.getDisplayId() == displayId) {
+            mImpl.onAppTransitionFinished();
+        }
     }
 
     public void growRecents() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index bc38169..a776d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.notification.NotificationData.Entry;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Handler;
@@ -29,6 +27,7 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
 import java.util.stream.Stream;
@@ -48,7 +47,7 @@
      * NotificationManagerService side, but we keep it to prevent the UI from looking weird and
      * will remove when possible. See {@link NotificationLifetimeExtender}
      */
-    protected final ArraySet<Entry> mExtendedLifetimeAlertEntries = new ArraySet<>();
+    protected final ArraySet<NotificationEntry> mExtendedLifetimeAlertEntries = new ArraySet<>();
 
     protected NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
     protected int mMinimumDisplayTime;
@@ -61,7 +60,7 @@
      * Adds the notification to be managed.
      * @param entry entry to show
      */
-    public void showNotification(@NonNull Entry entry) {
+    public void showNotification(@NonNull NotificationEntry entry) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "showNotification");
         }
@@ -139,7 +138,7 @@
      * @return the entry
      */
     @Nullable
-    public Entry getEntry(@NonNull String key) {
+    public NotificationEntry getEntry(@NonNull String key) {
         AlertEntry entry = mAlertEntries.get(key);
         return entry != null ? entry.mEntry : null;
     }
@@ -149,7 +148,7 @@
      * @return all entries
      */
     @NonNull
-    public Stream<Entry> getAllEntries() {
+    public Stream<NotificationEntry> getAllEntries() {
         return mAlertEntries.values().stream().map(headsUpEntry -> headsUpEntry.mEntry);
     }
 
@@ -180,7 +179,7 @@
      * Add a new entry and begin managing it.
      * @param entry the entry to add
      */
-    protected final void addAlertEntry(@NonNull Entry entry) {
+    protected final void addAlertEntry(@NonNull NotificationEntry entry) {
         AlertEntry alertEntry = createAlertEntry();
         alertEntry.setEntry(entry);
         mAlertEntries.put(entry.key, alertEntry);
@@ -203,7 +202,7 @@
         if (alertEntry == null) {
             return;
         }
-        Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         mAlertEntries.remove(key);
         onAlertEntryRemoved(alertEntry);
         entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@@ -250,12 +249,12 @@
     }
 
     @Override
-    public boolean shouldExtendLifetime(Entry entry) {
+    public boolean shouldExtendLifetime(NotificationEntry entry) {
         return !canRemoveImmediately(entry.key);
     }
 
     @Override
-    public void setShouldManageLifetime(Entry entry, boolean shouldExtend) {
+    public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
         if (shouldExtend) {
             mExtendedLifetimeAlertEntries.add(entry);
         } else {
@@ -265,17 +264,17 @@
     ///////////////////////////////////////////////////////////////////////////////////////////////
 
     protected class AlertEntry implements Comparable<AlertEntry> {
-        @Nullable public Entry mEntry;
+        @Nullable public NotificationEntry mEntry;
         public long mPostTime;
         public long mEarliestRemovaltime;
 
         @Nullable protected Runnable mRemoveAlertRunnable;
 
-        public void setEntry(@NonNull final Entry entry) {
+        public void setEntry(@NonNull final NotificationEntry entry) {
             setEntry(entry, () -> removeAlertEntry(entry.key));
         }
 
-        public void setEntry(@NonNull final Entry entry,
+        public void setEntry(@NonNull final NotificationEntry entry,
                 @Nullable Runnable removeAlertRunnable) {
             mEntry = entry;
             mRemoveAlertRunnable = removeAlertRunnable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 9bfd4ee..a3beb96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -25,7 +25,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
 import javax.inject.Inject;
@@ -83,7 +83,7 @@
 
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setAmbientPulsing(true);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, true);
@@ -92,7 +92,7 @@
 
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setAmbientPulsing(false);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, false);
@@ -131,7 +131,7 @@
          * @param entry the entry that changed
          * @param isPulsing true if the entry is now pulsing, false otherwise
          */
-        void onAmbientStateChanged(NotificationData.Entry entry, boolean isPulsing);
+        void onAmbientStateChanged(NotificationEntry entry, boolean isPulsing);
     }
 
     private final class AmbientEntry extends AlertEntry {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 95019ee..6a01563 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -16,18 +16,30 @@
 
 package com.android.systemui.statusbar;
 
+import static android.app.StatusBarManager.DISABLE2_NONE;
+import static android.app.StatusBarManager.DISABLE_NONE;
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
 
 import android.app.StatusBarManager;
+import android.app.StatusBarManager.Disable2Flags;
+import android.app.StatusBarManager.DisableFlags;
+import android.app.StatusBarManager.WindowType;
+import android.app.StatusBarManager.WindowVisibleState;
 import android.content.ComponentName;
+import android.content.Context;
 import android.graphics.Rect;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.display.DisplayManager;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.util.Pair;
+import android.util.SparseArray;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -47,7 +59,8 @@
  * coalescing these calls so they don't stack up.  For the calls
  * are coalesced, note that they are all idempotent.
  */
-public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks> {
+public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks>,
+        DisplayManager.DisplayListener {
     private static final int INDEX_MASK = 0xffff;
     private static final int MSG_SHIFT  = 16;
     private static final int MSG_MASK   = 0xffff << MSG_SHIFT;
@@ -112,8 +125,8 @@
     private final Object mLock = new Object();
     private ArrayList<Callbacks> mCallbacks = new ArrayList<>();
     private Handler mHandler = new H(Looper.getMainLooper());
-    private int mDisable1;
-    private int mDisable2;
+    /** A map of display id - disable flag pair */
+    private SparseArray<Pair<Integer, Integer>> mDisplayDisabled = new SparseArray<>();
 
     /**
      * These methods are called back on the main thread.
@@ -121,17 +134,63 @@
     public interface Callbacks {
         default void setIcon(String slot, StatusBarIcon icon) { }
         default void removeIcon(String slot) { }
-        default void disable(int state1, int state2, boolean animate) { }
+
+        /**
+         * Called to notify that disable flags are updated.
+         * @see IStatusBar#disable(int, int, int).
+         *
+         * @param displayId The id of the display to notify.
+         * @param state1 The combination of following DISABLE_* flags:
+         * @param state2 The combination of following DISABLE2_* flags:
+         * @param animate {@code true} to show animations.
+         */
+        default void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2,
+                boolean animate) { }
         default void animateExpandNotificationsPanel() { }
         default void animateCollapsePanels(int flags, boolean force) { }
         default void togglePanel() { }
         default void animateExpandSettingsPanel(String obj) { }
-        default void setSystemUiVisibility(int vis, int fullscreenStackVis,
+
+        /**
+         * Called to notify visibility flag changes.
+         * @see IStatusBar#setSystemUiVisibility(int, int, int, int, int, Rect, Rect).
+         *
+         * @param displayId The id of the display to notify.
+         * @param vis The visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will
+         *            be reported separately in fullscreenStackVis and dockedStackVis.
+         * @param fullscreenStackVis The flags which only apply in the region of the fullscreen
+         *                           stack, which is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+         * @param dockedStackVis The flags that only apply in the region of the docked stack, which
+         *                       is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+         * @param mask Which flags to change.
+         * @param fullscreenStackBounds The current bounds of the fullscreen stack, in screen
+         *                              coordinates.
+         * @param dockedStackBounds The current bounds of the docked stack, in screen coordinates.
+         */
+        default void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
                 int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
         }
-        default void topAppWindowChanged(boolean visible) { }
-        default void setImeWindowStatus(IBinder token, int vis, int backDisposition,
-                boolean showImeSwitcher) { }
+
+        /**
+         * Called to notify top app window changes.
+         * @see IStatusBar#topAppWindowChanged(int, boolean)
+         *
+         * @param displayId The id of the display to notify.
+         * @param visible {@code true} to show menu button.
+         */
+        default void topAppWindowChanged(int displayId, boolean visible) { }
+
+        /**
+         * Called to notify IME window status changes.
+         *
+         * @param displayId The id of the display to notify.
+         * @param token IME token.
+         * @param vis IME visibility.
+         * @param backDisposition Disposition mode of back button. It should be one of below flags:
+         * @param showImeSwitcher {@code true} to show IME switch button.
+         */
+        default void setImeWindowStatus(int displayId, IBinder token,  int vis,
+                @BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
         default void showRecentApps(boolean triggeredFromAltTab) { }
         default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
         default void toggleRecentApps() { }
@@ -140,12 +199,56 @@
         default void dismissKeyboardShortcutsMenu() { }
         default void toggleKeyboardShortcutsMenu(int deviceId) { }
         default void cancelPreloadRecentApps() { }
-        default void setWindowState(int window, int state) { }
+
+        /**
+         * Called to notify window state changes.
+         * @see IStatusBar#setWindowState(int, int, int)
+         *
+         * @param displayId The id of the display to notify.
+         * @param window Window type. It should be one of {@link StatusBarManager#WINDOW_STATUS_BAR}
+         *               or {@link StatusBarManager#WINDOW_NAVIGATION_BAR}
+         * @param state Window visible state.
+         */
+        default void setWindowState(int displayId, @WindowType int window,
+                @WindowVisibleState int state) { }
         default void showScreenPinningRequest(int taskId) { }
-        default void appTransitionPending(boolean forced) { }
-        default void appTransitionCancelled() { }
-        default void appTransitionStarting(long startTime, long duration, boolean forced) { }
-        default void appTransitionFinished() { }
+
+        /**
+         * Called to notify System UI that an application transition is pending.
+         * @see IStatusBar#appTransitionPending(int).
+         *
+         * @param displayId The id of the display to notify.
+         * @param forced {@code true} to force transition pending.
+         */
+        default void appTransitionPending(int displayId, boolean forced) { }
+
+        /**
+         * Called to notify System UI that an application transition is canceled.
+         * @see IStatusBar#appTransitionCancelled(int).
+         *
+         * @param displayId The id of the display to notify.
+         */
+        default void appTransitionCancelled(int displayId) { }
+
+        /**
+         * Called to notify System UI that an application transition is starting.
+         * @see IStatusBar#appTransitionStarting(int, long, long).
+         *
+         * @param displayId The id of the display to notify.
+         * @param startTime Transition start time.
+         * @param duration Transition duration.
+         * @param forced {@code true} to force transition pending.
+         */
+        default void appTransitionStarting(
+                int displayId, long startTime, long duration, boolean forced) { }
+
+        /**
+         * Called to notify System UI that an application transition is finished.
+         * @see IStatusBar#appTransitionFinished(int)
+         *
+         * @param displayId The id of the display to notify.
+         */
+        default void appTransitionFinished(int displayId) { }
         default void showAssistDisclosure() { }
         default void startAssist(Bundle args) { }
         default void onCameraLaunchGestureDetected(int source) { }
@@ -176,18 +279,43 @@
     }
 
     @VisibleForTesting
-    public CommandQueue() {
+    public CommandQueue(Context context) {
+        context.getSystemService(DisplayManager.class).registerDisplayListener(this, mHandler);
+        // We always have default display.
+        setDisabled(DEFAULT_DISPLAY, DISABLE_NONE, DISABLE2_NONE);
     }
 
+    @Override
+    public void onDisplayAdded(int displayId) { }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        synchronized (mLock) {
+            mDisplayDisabled.remove(displayId);
+        }
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) { }
+
+    // TODO(b/118592525): add multi-display support if needed.
     public boolean panelsEnabled() {
-        return (mDisable1 & StatusBarManager.DISABLE_EXPAND) == 0
-                && (mDisable2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
+        final int disabled1 = getDisabled1(DEFAULT_DISPLAY);
+        final int disabled2 = getDisabled2(DEFAULT_DISPLAY);
+        return (disabled1 & StatusBarManager.DISABLE_EXPAND) == 0
+                && (disabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
                 && !ONLY_CORE_APPS;
     }
 
     public void addCallback(Callbacks callbacks) {
         mCallbacks.add(callbacks);
-        callbacks.disable(mDisable1, mDisable2, false /* animate */);
+        // TODO(b/117478341): find a better way to pass disable flags by display.
+        for (int i = 0; i < mDisplayDisabled.size(); i++) {
+            int displayId = mDisplayDisabled.keyAt(i);
+            int disabled1 = getDisabled1(displayId);
+            int disabled2 = getDisabled2(displayId);
+            callbacks.disable(displayId, disabled1, disabled2, false /* animate */);
+        }
     }
 
     public void removeCallback(Callbacks callbacks) {
@@ -209,12 +337,21 @@
         }
     }
 
-    public void disable(int state1, int state2, boolean animate) {
+    /**
+     * Called to notify that disable flags are updated.
+     * @see Callbacks#disable(int, int, int, boolean).
+     */
+    public void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2,
+            boolean animate) {
         synchronized (mLock) {
-            mDisable1 = state1;
-            mDisable2 = state2;
+            setDisabled(displayId, state1, state2);
             mHandler.removeMessages(MSG_DISABLE);
-            Message msg = mHandler.obtainMessage(MSG_DISABLE, state1, state2, animate);
+            final SomeArgs args = SomeArgs.obtain();
+            args.argi1 = displayId;
+            args.argi2 = state1;
+            args.argi3 = state2;
+            args.argi4 = animate ? 1 : 0;
+            Message msg = mHandler.obtainMessage(MSG_DISABLE, args);
             if (Looper.myLooper() == mHandler.getLooper()) {
                 // If its the right looper execute immediately so hides can be handled quickly.
                 mHandler.handleMessage(msg);
@@ -225,14 +362,42 @@
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
-    public void disable(int displayId, int state1, int state2) {
-        disable(state1, state2, true);
+    public void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2) {
+        disable(displayId, state1, state2, true);
     }
 
-    public void recomputeDisableFlags(boolean animate) {
-        disable(mDisable1, mDisable2, animate);
+    /**
+     * Apply current disable flags by {@link CommandQueue#disable(int, int, int, boolean)}.
+     *
+     * @param displayId The id of the display to notify.
+     * @param animate {@code true} to show animations.
+     */
+    public void recomputeDisableFlags(int displayId, boolean animate) {
+        int disabled1 = getDisabled1(displayId);
+        int disabled2 = getDisabled2(displayId);
+        disable(displayId, disabled1, disabled2, animate);
+    }
+
+    private void setDisabled(int displayId, int disabled1, int disabled2) {
+        mDisplayDisabled.put(displayId, new Pair<>(disabled1, disabled2));
+    }
+
+    private int getDisabled1(int displayId) {
+        return getDisabled(displayId).first;
+    }
+
+    private int getDisabled2(int displayId) {
+        return getDisabled(displayId).second;
+    }
+
+    private Pair<Integer, Integer> getDisabled(int displayId) {
+        Pair<Integer, Integer> disablePair = mDisplayDisabled.get(displayId);
+        if (disablePair == null) {
+            disablePair = new Pair<>(DISABLE_NONE, DISABLE2_NONE);
+            mDisplayDisabled.put(displayId, disablePair);
+        }
+        return disablePair;
     }
 
     public void animateExpandNotificationsPanel() {
@@ -270,7 +435,6 @@
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
             int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
@@ -278,34 +442,38 @@
             // Don't coalesce these, since it might have one time flags set such as
             // STATUS_BAR_UNHIDE which might get lost.
             SomeArgs args = SomeArgs.obtain();
-            args.argi1 = vis;
-            args.argi2 = fullscreenStackVis;
-            args.argi3 = dockedStackVis;
-            args.argi4 = mask;
+            args.argi1 = displayId;
+            args.argi2 = vis;
+            args.argi3 = fullscreenStackVis;
+            args.argi4 = dockedStackVis;
+            args.argi5 = mask;
             args.arg1 = fullscreenStackBounds;
             args.arg2 = dockedStackBounds;
             mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void topAppWindowChanged(int displayId, boolean menuVisible) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
-            mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0,
-                    null).sendToTarget();
+            mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED,
+                    displayId, menuVisible ? 1 : 0, null).sendToTarget();
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
-            Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token);
-            m.getData().putBoolean(SHOW_IME_SWITCHER_KEY, showImeSwitcher);
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = displayId;
+            args.argi2 = vis;
+            args.argi3 = backDisposition;
+            args.argi4 = showImeSwitcher ? 1 : 0;
+            args.arg1 = token;
+            Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, args);
             m.sendToTarget();
         }
     }
@@ -381,12 +549,11 @@
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void setWindowState(int displayId, int window, int state) {
         synchronized (mLock) {
             // don't coalesce these
-            mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget();
+            mHandler.obtainMessage(MSG_SET_WINDOW_STATE, displayId, window, state).sendToTarget();
         }
     }
 
@@ -397,44 +564,54 @@
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void appTransitionPending(int displayId) {
-        appTransitionPending(false /* forced */);
+        appTransitionPending(displayId, false /* forced */);
     }
 
-    public void appTransitionPending(boolean forced) {
+    /**
+     * Called to notify System UI that an application transition is pending.
+     * @see Callbacks#appTransitionPending(int, boolean)
+     */
+    public void appTransitionPending(int displayId, boolean forced) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_APP_TRANSITION_PENDING, forced ? 1 : 0, 0).sendToTarget();
+            mHandler.obtainMessage(MSG_APP_TRANSITION_PENDING, displayId, forced ? 1 : 0)
+                    .sendToTarget();
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void appTransitionCancelled(int displayId) {
         synchronized (mLock) {
-            mHandler.sendEmptyMessage(MSG_APP_TRANSITION_CANCELLED);
+            mHandler.obtainMessage(MSG_APP_TRANSITION_CANCELLED, displayId).sendToTarget();
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void appTransitionStarting(int displayId, long startTime, long duration) {
-        appTransitionStarting(startTime, duration, false /* forced */);
+        appTransitionStarting(displayId, startTime, duration, false /* forced */);
     }
 
-    public void appTransitionStarting(long startTime, long duration, boolean forced) {
+    /**
+     * Called to notify System UI that an application transition is starting.
+     * @see Callbacks#appTransitionStarting(int, long, long, boolean).
+     */
+    public void appTransitionStarting(int displayId, long startTime, long duration,
+            boolean forced) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_APP_TRANSITION_STARTING, forced ? 1 : 0, 0,
-                    Pair.create(startTime, duration)).sendToTarget();
+            final SomeArgs args = SomeArgs.obtain();
+            args.argi1 = displayId;
+            args.argi2 = forced ? 1 : 0;
+            args.arg1 = startTime;
+            args.arg2 = duration;
+            mHandler.obtainMessage(MSG_APP_TRANSITION_STARTING, args).sendToTarget();
         }
     }
 
-    // TODO(b/117478341): Add multi-display support.
     @Override
     public void appTransitionFinished(int displayId) {
         synchronized (mLock) {
-            mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
+            mHandler.obtainMessage(MSG_APP_TRANSITION_FINISHED, displayId).sendToTarget();
         }
     }
 
@@ -619,8 +796,10 @@
                     break;
                 }
                 case MSG_DISABLE:
+                    SomeArgs args = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).disable(msg.arg1, msg.arg2, (Boolean) msg.obj);
+                        mCallbacks.get(i).disable(args.argi1, args.argi2, args.argi3,
+                                args.argi4 != 0 /* animate */);
                     }
                     break;
                 case MSG_EXPAND_NOTIFICATIONS:
@@ -644,22 +823,23 @@
                     }
                     break;
                 case MSG_SET_SYSTEMUI_VISIBILITY:
-                    SomeArgs args = (SomeArgs) msg.obj;
+                    args = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
-                                args.argi4, (Rect) args.arg1, (Rect) args.arg2);
+                                args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2);
                     }
                     args.recycle();
                     break;
                 case MSG_TOP_APP_WINDOW_CHANGED:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).topAppWindowChanged(msg.arg1 != 0);
+                        mCallbacks.get(i).topAppWindowChanged(msg.arg1, msg.arg2 != 0);
                     }
                     break;
                 case MSG_SHOW_IME_BUTTON:
+                    args = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2,
-                                msg.getData().getBoolean(SHOW_IME_SWITCHER_KEY, false));
+                        mCallbacks.get(i).setImeWindowStatus(args.argi1, (IBinder) args.arg1,
+                                args.argi2, args.argi3, args.argi4 != 0 /* showImeSwitcher */);
                     }
                     break;
                 case MSG_SHOW_RECENT_APPS:
@@ -699,7 +879,7 @@
                     break;
                 case MSG_SET_WINDOW_STATE:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).setWindowState(msg.arg1, msg.arg2);
+                        mCallbacks.get(i).setWindowState(msg.arg1, msg.arg2, (int) msg.obj);
                     }
                     break;
                 case MSG_SHOW_SCREEN_PIN_REQUEST:
@@ -709,24 +889,24 @@
                     break;
                 case MSG_APP_TRANSITION_PENDING:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).appTransitionPending(msg.arg1 != 0);
+                        mCallbacks.get(i).appTransitionPending(msg.arg1, msg.arg2 != 0);
                     }
                     break;
                 case MSG_APP_TRANSITION_CANCELLED:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).appTransitionCancelled();
+                        mCallbacks.get(i).appTransitionCancelled(msg.arg1);
                     }
                     break;
                 case MSG_APP_TRANSITION_STARTING:
+                    args = (SomeArgs) msg.obj;
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        Pair<Long, Long> data = (Pair<Long, Long>) msg.obj;
-                        mCallbacks.get(i).appTransitionStarting(data.first, data.second,
-                                msg.arg1 != 0);
+                        mCallbacks.get(i).appTransitionStarting(args.argi1, (long) args.arg1,
+                                (long) args.arg2, args.argi2 != 0 /* forced */);
                     }
                     break;
                 case MSG_APP_TRANSITION_FINISHED:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).appTransitionFinished();
+                        mCallbacks.get(i).appTransitionFinished(msg.arg1);
                     }
                     break;
                 case MSG_ASSIST_DISCLOSURE:
@@ -858,7 +1038,7 @@
     public static class CommandQueueStart extends SystemUI {
         @Override
         public void start() {
-            putComponent(CommandQueue.class, new CommandQueue());
+            putComponent(CommandQueue.class, new CommandQueue(mContext));
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index e217777..3f1ff33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -32,7 +32,7 @@
 import com.android.keyguard.AlphaOptimizedLinearLayout;
 import com.android.systemui.R;
 import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.util.List;
 
@@ -50,7 +50,7 @@
     private int mEndMargin;
     private View mIconPlaceholder;
     private TextView mTextView;
-    private NotificationData.Entry mShowingEntry;
+    private NotificationEntry mShowingEntry;
     private Rect mLayoutedIconRect = new Rect();
     private int[] mTmpPosition = new int[2];
     private boolean mFirstLayout = true;
@@ -162,7 +162,7 @@
         mTextView = findViewById(R.id.text);
     }
 
-    public void setEntry(NotificationData.Entry entry) {
+    public void setEntry(NotificationEntry entry) {
         if (entry != null) {
             mShowingEntry = entry;
             CharSequence text = entry.headsUpStatusBarText;
@@ -261,7 +261,7 @@
         return super.fitSystemWindows(insets);
     }
 
-    public NotificationData.Entry getShowingEntry() {
+    public NotificationEntry getShowingEntry() {
         return mShowingEntry;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 43eaff4..9740d1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -34,6 +34,7 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.NavigationBarFragment;
@@ -132,12 +133,21 @@
             // Unfortunately, we still need it because status bar needs LightBarController
             // before notifications creation. We cannot directly use getLightBarController()
             // from NavigationBarFragment directly.
-            LightBarController controller = isOnDefaultDisplay
+            LightBarController lightBarController = isOnDefaultDisplay
                     ? Dependency.get(LightBarController.class)
                     : new LightBarController(context,
                             Dependency.get(DarkIconDispatcher.class),
                             Dependency.get(BatteryController.class));
-            navBar.setLightBarController(controller);
+            navBar.setLightBarController(lightBarController);
+
+            // TODO(b/118592525): to support multi-display, we start to add something which is
+            //                    per-display, while others may be global. I think it's time to add
+            //                    a new class maybe named DisplayDependency to solve per-display
+            //                    Dependency problem.
+            AutoHideController autoHideController = isOnDefaultDisplay
+                    ? Dependency.get(AutoHideController.class)
+                    : new AutoHideController(context, mHandler);
+            navBar.setAutoHideController(autoHideController);
             navBar.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
             mNavigationBars.append(displayId, navBar);
         });
@@ -197,15 +207,6 @@
         }
     }
 
-    /** @see NavigationBarFragment#isSemiTransparent() */
-    public boolean isSemiTransparent(int displayId) {
-        NavigationBarFragment navBar = mNavigationBars.get(displayId);
-        if (navBar != null) {
-            return navBar.isSemiTransparent();
-        }
-        return false;
-    }
-
     /** @see NavigationBarFragment#disableAnimationsDuringHide(long) */
     public void disableAnimationsDuringHide(int displayId, long delay) {
         NavigationBarFragment navBar = mNavigationBars.get(displayId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
index ecd9814..0f295ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
@@ -2,7 +2,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * Interface for anything that may need to keep notifications managed even after
@@ -24,7 +24,7 @@
      * @param entry the entry containing the notification to check
      * @return true if the notification lifetime should be extended
      */
-    boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry);
+    boolean shouldExtendLifetime(@NonNull NotificationEntry entry);
 
     /**
      * Sets whether or not the lifetime should be managed by the extender.  In practice, if
@@ -37,7 +37,7 @@
      * @param entry the entry that needs an extended lifetime
      * @param shouldManage true if the extender should manage the entry now, false otherwise
      */
-    void setShouldManageLifetime(@NonNull NotificationData.Entry entry, boolean shouldManage);
+    void setShouldManageLifetime(@NonNull NotificationEntry entry, boolean shouldManage);
 
     /**
      * The callback for when the notification is now safe to remove (i.e. its lifetime has ended).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index bc662e3..f46ded4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -18,7 +18,7 @@
 import android.service.notification.StatusBarNotification;
 import android.util.SparseArray;
 
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 public interface NotificationLockscreenUserManager {
     String PERMISSION_SELF = "com.android.systemui.permission.SELF";
@@ -55,7 +55,7 @@
 
     void updatePublicMode();
 
-    boolean needsRedaction(Entry entry);
+    boolean needsRedaction(NotificationEntry entry);
 
     boolean userAllowsPrivateNotificationsInPublic(int currentUserId);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index bba4369..d5f4d04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -46,9 +46,9 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
@@ -407,7 +407,7 @@
     }
 
     /** @return true if the entry needs redaction when on the lockscreen. */
-    public boolean needsRedaction(NotificationData.Entry ent) {
+    public boolean needsRedaction(NotificationEntry ent) {
         int userId = ent.notification.getUserId();
 
         boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 1bf101c..7412702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -37,7 +37,6 @@
 import android.os.Handler;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
@@ -47,9 +46,9 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.Interpolators;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -157,15 +156,10 @@
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
             public void onEntryRemoved(
-                    @Nullable Entry entry,
-                    String key,
-                    StatusBarNotification old,
+                    NotificationEntry entry,
                     NotificationVisibility visibility,
-                    boolean lifetimeExtended,
                     boolean removedByUser) {
-                if (!lifetimeExtended) {
-                    onNotificationRemoved(key);
-                }
+                onNotificationRemoved(entry.key);
             }
         });
     }
@@ -194,7 +188,7 @@
             return null;
         }
         synchronized (mEntryManager.getNotificationData()) {
-            Entry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey);
+            NotificationEntry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey);
             if (entry == null || entry.expandedIcon == null) {
                 return null;
             }
@@ -216,15 +210,15 @@
         boolean metaDataChanged = false;
 
         synchronized (mEntryManager.getNotificationData()) {
-            ArrayList<Entry> activeNotifications =
+            ArrayList<NotificationEntry> activeNotifications =
                     mEntryManager.getNotificationData().getActiveNotifications();
             final int N = activeNotifications.size();
 
             // Promote the media notification with a controller in 'playing' state, if any.
-            Entry mediaNotification = null;
+            NotificationEntry mediaNotification = null;
             MediaController controller = null;
             for (int i = 0; i < N; i++) {
-                final Entry entry = activeNotifications.get(i);
+                final NotificationEntry entry = activeNotifications.get(i);
 
                 if (entry.isMediaNotification()) {
                     final MediaSession.Token token =
@@ -264,7 +258,7 @@
                             final String pkg = aController.getPackageName();
 
                             for (int i = 0; i < N; i++) {
-                                final Entry entry = activeNotifications.get(i);
+                                final NotificationEntry entry = activeNotifications.get(i);
                                 if (entry.notification.getPackageName().equals(pkg)) {
                                     if (DEBUG_MEDIA) {
                                         Log.v(TAG, "DEBUG_MEDIA: found controller matching "
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 886d99e..7d6231f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -51,9 +51,9 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -103,7 +103,7 @@
      * Notifications that are already removed but are kept around because the remote input is
      * actively being used (i.e. user is typing in it).  See {@link RemoteInputActiveExtender}.
      */
-    protected final ArraySet<NotificationData.Entry> mEntriesKeptForRemoteInputActive =
+    protected final ArraySet<NotificationEntry> mEntriesKeptForRemoteInputActive =
             new ArraySet<>();
 
     // Dependencies:
@@ -253,14 +253,11 @@
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
             public void onEntryRemoved(
-                    @Nullable NotificationData.Entry entry,
-                    String key,
-                    StatusBarNotification old,
+                    @Nullable NotificationEntry entry,
                     NotificationVisibility visibility,
-                    boolean lifetimeExtended,
                     boolean removedByUser) {
                 if (removedByUser && entry != null) {
-                    onPerformRemoveNotification(entry, key);
+                    onPerformRemoveNotification(entry, entry.key);
                 }
             }
         });
@@ -272,7 +269,7 @@
         mRemoteInputController = new RemoteInputController(delegate);
         mRemoteInputController.addCallback(new RemoteInputController.Callback() {
             @Override
-            public void onRemoteInputSent(NotificationData.Entry entry) {
+            public void onRemoteInputSent(NotificationEntry entry) {
                 if (FORCE_REMOTE_INPUT_HISTORY
                         && isNotificationKeptForRemoteInputHistory(entry.key)) {
                     mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
@@ -416,7 +413,7 @@
     }
 
     @VisibleForTesting
-    void onPerformRemoveNotification(NotificationData.Entry entry, final String key) {
+    void onPerformRemoveNotification(NotificationEntry entry, final String key) {
         if (mKeysKeptForRemoteInputHistory.contains(key)) {
             mKeysKeptForRemoteInputHistory.remove(key);
         }
@@ -427,7 +424,7 @@
 
     public void onPanelCollapsed() {
         for (int i = 0; i < mEntriesKeptForRemoteInputActive.size(); i++) {
-            NotificationData.Entry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
+            NotificationEntry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
             mRemoteInputController.removeRemoteInput(entry, null);
             if (mNotificationLifetimeFinishedCallback != null) {
                 mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
@@ -440,7 +437,7 @@
         return mKeysKeptForRemoteInputHistory.contains(key);
     }
 
-    public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) {
+    public boolean shouldKeepForRemoteInputHistory(NotificationEntry entry) {
         if (entry.isDismissed()) {
             return false;
         }
@@ -450,7 +447,7 @@
         return (mRemoteInputController.isSpinning(entry.key) || entry.hasJustSentRemoteInput());
     }
 
-    public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) {
+    public boolean shouldKeepForSmartReplyHistory(NotificationEntry entry) {
         if (entry.isDismissed()) {
             return false;
         }
@@ -470,13 +467,13 @@
 
     @VisibleForTesting
     StatusBarNotification rebuildNotificationForCanceledSmartReplies(
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */,
                 false /* showSpinner */);
     }
 
     @VisibleForTesting
-    StatusBarNotification rebuildNotificationWithRemoteInput(NotificationData.Entry entry,
+    StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry,
             CharSequence remoteInputText, boolean showSpinner) {
         StatusBarNotification sbn = entry.notification;
 
@@ -533,7 +530,7 @@
     }
 
     @VisibleForTesting
-    public Set<NotificationData.Entry> getEntriesKeptForRemoteInputActive() {
+    public Set<NotificationEntry> getEntriesKeptForRemoteInputActive() {
         return mEntriesKeptForRemoteInputActive;
     }
 
@@ -556,12 +553,12 @@
      */
     protected class RemoteInputHistoryExtender extends RemoteInputExtender {
         @Override
-        public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
             return shouldKeepForRemoteInputHistory(entry);
         }
 
         @Override
-        public void setShouldManageLifetime(NotificationData.Entry entry,
+        public void setShouldManageLifetime(NotificationEntry entry,
                 boolean shouldExtend) {
             if (shouldExtend) {
                 CharSequence remoteInputText = entry.remoteInputText;
@@ -602,12 +599,12 @@
      */
     protected class SmartReplyHistoryExtender extends RemoteInputExtender {
         @Override
-        public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
             return shouldKeepForSmartReplyHistory(entry);
         }
 
         @Override
-        public void setShouldManageLifetime(NotificationData.Entry entry,
+        public void setShouldManageLifetime(NotificationEntry entry,
                 boolean shouldExtend) {
             if (shouldExtend) {
                 StatusBarNotification newSbn = rebuildNotificationForCanceledSmartReplies(entry);
@@ -640,7 +637,7 @@
      */
     protected class RemoteInputActiveExtender extends RemoteInputExtender {
         @Override
-        public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+        public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
             if (entry.isDismissed()) {
                 return false;
             }
@@ -648,7 +645,7 @@
         }
 
         @Override
-        public void setShouldManageLifetime(NotificationData.Entry entry,
+        public void setShouldManageLifetime(NotificationEntry entry,
                 boolean shouldExtend) {
             if (shouldExtend) {
                 if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index f23ae3f..f0d804d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -24,7 +24,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -52,7 +52,7 @@
     }
 
     public static NotificationUiAdjustment extractFromNotificationEntry(
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         return new NotificationUiAdjustment(
                 entry.key, entry.systemGeneratedSmartActions, entry.smartReplies);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 017a9c3..bf6caa0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -27,9 +27,9 @@
 
 import com.android.systemui.R;
 import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -150,13 +150,13 @@
      */
     //TODO: Rewrite this to focus on Entries, or some other data object instead of views
     public void updateNotificationViews() {
-        ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
+        ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
                 .getActiveNotifications();
         ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
-        ArrayList<NotificationData.Entry> toBubble = new ArrayList<>();
+        ArrayList<NotificationEntry> toBubble = new ArrayList<>();
         final int N = activeNotifications.size();
         for (int i = 0; i < N; i++) {
-            NotificationData.Entry ent = activeNotifications.get(i);
+            NotificationEntry ent = activeNotifications.get(i);
             if (ent.isRowDismissed() || ent.isRowRemoved()) {
                 // we don't want to update removed notifications because they could
                 // temporarily become children if they were isolated before.
@@ -187,7 +187,7 @@
             ent.getRow().setSensitive(sensitive, deviceSensitive);
             ent.getRow().setNeedsRedaction(needsRedaction);
             if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
-                NotificationData.Entry summary = mGroupManager.getGroupSummary(ent.notification);
+                NotificationEntry summary = mGroupManager.getGroupSummary(ent.notification);
                 List<ExpandableNotificationRow> orderedChildren =
                         mTmpChildOrderMap.get(summary.getRow());
                 if (orderedChildren == null) {
@@ -271,7 +271,7 @@
 
         for (int i = 0; i < toBubble.size(); i++) {
             // TODO: might make sense to leave them in the shade and just reposition them
-            NotificationData.Entry ent = toBubble.get(i);
+            NotificationEntry ent = toBubble.get(i);
             mBubbleController.addBubble(ent);
         }
 
@@ -385,7 +385,7 @@
         }
         while(!stack.isEmpty()) {
             ExpandableNotificationRow row = stack.pop();
-            NotificationData.Entry entry = row.getEntry();
+            NotificationEntry entry = row.getEntry();
             boolean isChildNotification =
                     mGroupManager.isChildInGroupWithSummary(entry.notification);
 
@@ -408,7 +408,7 @@
             if (!showOnKeyguard) {
                 // min priority notifications should show if their summary is showing
                 if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
-                    NotificationData.Entry summary = mGroupManager.getLogicalGroupSummary(
+                    NotificationEntry summary = mGroupManager.getLogicalGroupSummary(
                             entry.notification);
                     if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
                             summary.notification))         {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index e8abcc2..998cf52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -24,7 +24,7 @@
 import android.util.Pair;
 
 import com.android.internal.util.Preconditions;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.RemoteInputView;
 
 import java.lang.ref.WeakReference;
@@ -38,7 +38,7 @@
     private static final boolean ENABLE_REMOTE_INPUT =
             SystemProperties.getBoolean("debug.enable_remote_input", true);
 
-    private final ArrayList<Pair<WeakReference<NotificationData.Entry>, Object>> mOpen
+    private final ArrayList<Pair<WeakReference<NotificationEntry>, Object>> mOpen
             = new ArrayList<>();
     private final ArrayMap<String, Object> mSpinning = new ArrayMap<>();
     private final ArrayList<Callback> mCallbacks = new ArrayList<>(3);
@@ -101,7 +101,7 @@
      * @param entry the entry for which a remote input is now active.
      * @param token a token identifying the view that is managing the remote input
      */
-    public void addRemoteInput(NotificationData.Entry entry, Object token) {
+    public void addRemoteInput(NotificationEntry entry, Object token) {
         Preconditions.checkNotNull(entry);
         Preconditions.checkNotNull(token);
 
@@ -122,7 +122,7 @@
      *              the entry is only removed if the token matches the last added token for this
      *              entry. If null, the entry is removed regardless.
      */
-    public void removeRemoteInput(NotificationData.Entry entry, Object token) {
+    public void removeRemoteInput(NotificationEntry entry, Object token) {
         Preconditions.checkNotNull(entry);
 
         pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token);
@@ -173,7 +173,7 @@
         return mSpinning.get(key) == token;
     }
 
-    private void apply(NotificationData.Entry entry) {
+    private void apply(NotificationEntry entry) {
         mDelegate.setRemoteInputActive(entry, isRemoteInputActive(entry));
         boolean remoteInputActive = isRemoteInputActive();
         int N = mCallbacks.size();
@@ -185,7 +185,7 @@
     /**
      * @return true if {@param entry} has an active RemoteInput
      */
-    public boolean isRemoteInputActive(NotificationData.Entry entry) {
+    public boolean isRemoteInputActive(NotificationEntry entry) {
         return pruneWeakThenRemoveAndContains(entry /* contains */, null /* remove */,
                 null /* removeToken */);
     }
@@ -208,10 +208,10 @@
      * @return true if {@param contains} is in the set of active remote inputs
      */
     private boolean pruneWeakThenRemoveAndContains(
-            NotificationData.Entry contains, NotificationData.Entry remove, Object removeToken) {
+            NotificationEntry contains, NotificationEntry remove, Object removeToken) {
         boolean found = false;
         for (int i = mOpen.size() - 1; i >= 0; i--) {
-            NotificationData.Entry item = mOpen.get(i).first.get();
+            NotificationEntry item = mOpen.get(i).first.get();
             Object itemToken = mOpen.get(i).second;
             boolean removeTokenMatches = (removeToken == null || itemToken == removeToken);
 
@@ -235,7 +235,7 @@
         mCallbacks.add(callback);
     }
 
-    public void remoteInputSent(NotificationData.Entry entry) {
+    public void remoteInputSent(NotificationEntry entry) {
         int N = mCallbacks.size();
         for (int i = 0; i < N; i++) {
             mCallbacks.get(i).onRemoteInputSent(entry);
@@ -248,16 +248,16 @@
         }
 
         // Make a copy because closing the remote inputs will modify mOpen.
-        ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size());
+        ArrayList<NotificationEntry> list = new ArrayList<>(mOpen.size());
         for (int i = mOpen.size() - 1; i >= 0; i--) {
-            NotificationData.Entry entry = mOpen.get(i).first.get();
+            NotificationEntry 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 entry = list.get(i);
+            NotificationEntry entry = list.get(i);
             if (entry.rowExists()) {
                 entry.closeRemoteInput();
             }
@@ -268,31 +268,31 @@
         mDelegate.requestDisallowLongPressAndDismiss();
     }
 
-    public void lockScrollTo(NotificationData.Entry entry) {
+    public void lockScrollTo(NotificationEntry entry) {
         mDelegate.lockScrollTo(entry);
     }
 
     public interface Callback {
         default void onRemoteInputActive(boolean active) {}
 
-        default void onRemoteInputSent(NotificationData.Entry entry) {}
+        default void onRemoteInputSent(NotificationEntry entry) {}
     }
 
     public interface Delegate {
         /**
          * Activate remote input if necessary.
          */
-        void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive);
+        void setRemoteInputActive(NotificationEntry entry, boolean remoteInputActive);
 
-       /**
-        * Request that the view does not dismiss nor perform long press for the current touch.
-        */
-       void requestDisallowLongPressAndDismiss();
+        /**
+         * Request that the view does not dismiss nor perform long press for the current touch.
+         */
+        void requestDisallowLongPressAndDismiss();
 
-      /**
-       * Request that the view is made visible by scrolling to it, and keep the scroll locked until
-       * the user scrolls, or {@param v} loses focus or is detached.
-       */
-       void lockScrollTo(NotificationData.Entry entry);
+        /**
+         * Request that the view is made visible by scrolling to it, and keep the scroll locked until
+         * the user scrolls, or {@param entry} loses focus or is detached.
+         */
+        void lockScrollTo(NotificationEntry entry);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 9e91133..573c1f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -21,8 +21,8 @@
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.util.Set;
 
@@ -54,7 +54,7 @@
     /**
      * Notifies StatusBarService a smart reply is sent.
      */
-    public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply,
+    public void smartReplySent(NotificationEntry entry, int replyIndex, CharSequence reply,
             boolean generatedByAssistant) {
         mCallback.onSmartReplySent(entry, reply);
         mSendingKeys.add(entry.key);
@@ -70,7 +70,7 @@
      * Notifies StatusBarService a smart action is clicked.
      */
     public void smartActionClicked(
-            NotificationData.Entry entry, int actionIndex, Notification.Action action,
+            NotificationEntry entry, int actionIndex, Notification.Action action,
             boolean generatedByAssistant) {
         final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
         final int rank = mEntryManager.getNotificationData().getRank(entry.key);
@@ -95,7 +95,7 @@
     /**
      * Smart Replies and Actions have been added to the UI.
      */
-    public void smartSuggestionsAdded(final NotificationData.Entry entry, int replyCount,
+    public void smartSuggestionsAdded(final NotificationEntry entry, int replyCount,
             int actionCount, boolean generatedByAssistant) {
         try {
             mBarService.onNotificationSmartSuggestionsAdded(
@@ -105,7 +105,7 @@
         }
     }
 
-    public void stopSending(final NotificationData.Entry entry) {
+    public void stopSending(final NotificationEntry entry) {
         if (entry != null) {
             mSendingKeys.remove(entry.notification.getKey());
         }
@@ -121,6 +121,6 @@
          * @param entry the entry for the notification
          * @param reply the reply that was sent
          */
-        void onSmartReplySent(NotificationData.Entry entry, CharSequence reply);
+        void onSmartReplySent(NotificationEntry entry, CharSequence reply);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 7b42dd9..c24698d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -20,7 +20,6 @@
 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 android.annotation.Nullable;
 import android.app.Notification;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
@@ -30,6 +29,7 @@
 import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -72,24 +72,21 @@
 
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
-            public void onEntryInflated(NotificationData.Entry entry, int inflatedFlags) {
+            public void onEntryInflated(NotificationEntry entry, int inflatedFlags) {
                 showAlertingView(entry, inflatedFlags);
             }
 
             @Override
-            public void onEntryUpdated(NotificationData.Entry entry) {
+            public void onEntryUpdated(NotificationEntry entry) {
                 updateAlertState(entry);
             }
 
             @Override
             public void onEntryRemoved(
-                    @Nullable NotificationData.Entry entry,
-                    String key,
-                    StatusBarNotification old,
+                    NotificationEntry entry,
                     NotificationVisibility visibility,
-                    boolean lifetimeExtended,
                     boolean removedByUser) {
-                stopAlerting(key);
+                stopAlerting(entry.key);
             }
         });
     }
@@ -105,7 +102,7 @@
      * @param entry         entry to add
      * @param inflatedFlags flags representing content views that were inflated
      */
-    private void showAlertingView(NotificationData.Entry entry,
+    private void showAlertingView(NotificationEntry entry,
             @NotificationInflater.InflationFlag int inflatedFlags) {
         if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
             // Possible for shouldHeadsUp to change between the inflation starting and ending.
@@ -127,7 +124,7 @@
         }
     }
 
-    private void updateAlertState(NotificationData.Entry entry) {
+    private void updateAlertState(NotificationEntry entry) {
         boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
         AlertingNotificationManager alertManager;
         boolean shouldAlert;
@@ -154,7 +151,7 @@
     }
 
     private static boolean alertAgain(
-            NotificationData.Entry oldEntry, Notification newNotification) {
+            NotificationEntry oldEntry, Notification newNotification) {
         return oldEntry == null || !oldEntry.hasInterrupted()
                 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
deleted file mode 100644
index a51896e..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ /dev/null
@@ -1,1072 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import static android.app.Notification.CATEGORY_ALARM;
-import static android.app.Notification.CATEGORY_CALL;
-import static android.app.Notification.CATEGORY_EVENT;
-import static android.app.Notification.CATEGORY_MESSAGE;
-import static android.app.Notification.CATEGORY_REMINDER;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-
-import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.Person;
-import android.content.Context;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.SnoozeCriterion;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.InflationTask;
-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.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * The list of currently displaying notifications.
- */
-public class NotificationData {
-
-    private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
-
-    /**
-     * These dependencies are late init-ed
-     */
-    private KeyguardEnvironment mEnvironment;
-    private NotificationMediaManager mMediaManager;
-
-    private HeadsUpManager mHeadsUpManager;
-
-    public static final class Entry {
-        private static final long LAUNCH_COOLDOWN = 2000;
-        private static final long REMOTE_INPUT_COOLDOWN = 500;
-        private static final long INITIALIZATION_DELAY = 400;
-        private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
-        private static final int COLOR_INVALID = 1;
-        public final String key;
-        public StatusBarNotification notification;
-        public NotificationChannel channel;
-        public long lastAudiblyAlertedMs;
-        public boolean noisy;
-        public boolean ambient;
-        public int importance;
-        public StatusBarIconView icon;
-        public StatusBarIconView expandedIcon;
-        private boolean interruption;
-        public boolean autoRedacted; // whether the redacted notification was generated by us
-        public int targetSdk;
-        private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
-        public CharSequence remoteInputText;
-        public List<SnoozeCriterion> snoozeCriteria;
-        public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
-        /** Smart Actions provided by the NotificationAssistantService. */
-        @NonNull
-        public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
-        public CharSequence[] smartReplies = new CharSequence[0];
-        @VisibleForTesting
-        public int suppressedVisualEffects;
-        public boolean suspended;
-
-        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;
-        private Throwable mDebugThrowable;
-        public CharSequence remoteInputTextWhenReset;
-        public long lastRemoteInputSent = NOT_LAUNCHED_YET;
-        public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
-        public CharSequence headsUpStatusBarText;
-        public CharSequence headsUpStatusBarTextPublic;
-
-        private long initializationTime = -1;
-
-        /**
-         * Whether or not this row represents a system notification. Note that if this is
-         * {@code null}, that means we were either unable to retrieve the info or have yet to
-         * retrieve the info.
-         */
-        public Boolean mIsSystemNotification;
-
-        /**
-         * Has the user sent a reply through this Notification.
-         */
-        private boolean hasSentReply;
-
-        /**
-         * Whether this notification should be displayed as a bubble.
-         */
-        private boolean mIsBubble;
-
-        /**
-         * Whether the user has dismissed this notification when it was in bubble form.
-         */
-        private boolean mUserDismissedBubble;
-
-        public Entry(StatusBarNotification n) {
-            this(n, null);
-        }
-
-        public Entry(StatusBarNotification n, @Nullable Ranking ranking) {
-            this.key = n.getKey();
-            this.notification = n;
-            if (ranking != null) {
-                populateFromRanking(ranking);
-            }
-        }
-
-        public void populateFromRanking(@NonNull Ranking ranking) {
-            channel = ranking.getChannel();
-            lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
-            importance = ranking.getImportance();
-            ambient = ranking.isAmbient();
-            snoozeCriteria = ranking.getSnoozeCriteria();
-            userSentiment = ranking.getUserSentiment();
-            systemGeneratedSmartActions = ranking.getSmartActions() == null
-                    ? Collections.emptyList() : ranking.getSmartActions();
-            smartReplies = ranking.getSmartReplies() == null
-                    ? new CharSequence[0]
-                    : ranking.getSmartReplies().toArray(new CharSequence[0]);
-            suppressedVisualEffects = ranking.getSuppressedVisualEffects();
-            suspended = ranking.isSuspended();
-        }
-
-        public void setInterruption() {
-            interruption = true;
-        }
-
-        public boolean hasInterrupted() {
-            return interruption;
-        }
-
-        public void setIsBubble(boolean bubbleable) {
-            mIsBubble = bubbleable;
-        }
-
-        public boolean isBubble() {
-            return mIsBubble;
-        }
-
-        public void setBubbleDismissed(boolean userDismissed) {
-            mUserDismissedBubble = userDismissed;
-        }
-
-        public boolean isBubbleDismissed() {
-            return mUserDismissedBubble;
-        }
-
-        /**
-         * Resets the notification entry to be re-used.
-         */
-        public void reset() {
-            if (row != null) {
-                row.reset();
-            }
-        }
-
-        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();
-        }
-
-        public boolean hasJustLaunchedFullScreenIntent() {
-            return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN;
-        }
-
-        public boolean hasJustSentRemoteInput() {
-            return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN;
-        }
-
-        public boolean hasFinishedInitialization() {
-            return initializationTime == -1 ||
-                    SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
-        }
-
-        /**
-         * Create the icons for a notification
-         * @param context the context to create the icons with
-         * @param sbn the notification
-         * @throws InflationException
-         */
-        public void createIcons(Context context, StatusBarNotification sbn)
-                throws InflationException {
-            Notification n = sbn.getNotification();
-            final Icon smallIcon = n.getSmallIcon();
-            if (smallIcon == null) {
-                throw new InflationException("No small icon in notification from "
-                        + sbn.getPackageName());
-            }
-
-            // Construct the icon.
-            icon = new StatusBarIconView(context,
-                    sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
-            icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
-            // Construct the expanded icon.
-            expandedIcon = new StatusBarIconView(context,
-                    sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
-            expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-            final StatusBarIcon ic = new StatusBarIcon(
-                    sbn.getUser(),
-                    sbn.getPackageName(),
-                    smallIcon,
-                    n.iconLevel,
-                    n.number,
-                    StatusBarIconView.contentDescForNotification(context, n));
-            if (!icon.set(ic) || !expandedIcon.set(ic)) {
-                icon = null;
-                expandedIcon = null;
-                throw new InflationException("Couldn't create icon: " + ic);
-            }
-            expandedIcon.setVisibility(View.INVISIBLE);
-            expandedIcon.setOnVisibilityChangedListener(
-                    newVisibility -> {
-                        if (row != null) {
-                            row.setIconsVisible(newVisibility != View.VISIBLE);
-                        }
-                    });
-        }
-
-        public void setIconTag(int key, Object tag) {
-            if (icon != null) {
-                icon.setTag(key, tag);
-                expandedIcon.setTag(key, tag);
-            }
-        }
-
-        /**
-         * Update the notification icons.
-         *
-         * @param context the context to create the icons with.
-         * @param sbn the notification to read the icon from.
-         * @throws InflationException
-         */
-        public void updateIcons(Context context, StatusBarNotification sbn)
-                throws InflationException {
-            if (icon != null) {
-                // Update the icon
-                Notification n = sbn.getNotification();
-                final StatusBarIcon ic = new StatusBarIcon(
-                        notification.getUser(),
-                        notification.getPackageName(),
-                        n.getSmallIcon(),
-                        n.iconLevel,
-                        n.number,
-                        StatusBarIconView.contentDescForNotification(context, n));
-                icon.setNotification(sbn);
-                expandedIcon.setNotification(sbn);
-                if (!icon.set(ic) || !expandedIcon.set(ic)) {
-                    throw new InflationException("Couldn't update icon: " + ic);
-                }
-            }
-        }
-
-        public int getContrastedColor(Context context, boolean isLowPriority,
-                int backgroundColor) {
-            int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
-                    notification.getNotification().color;
-            if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
-                return mCachedContrastColor;
-            }
-            final int contrasted = ContrastColorUtil.resolveContrastColor(context, rawColor,
-                    backgroundColor);
-            mCachedContrastColorIsFor = rawColor;
-            mCachedContrastColor = contrasted;
-            return mCachedContrastColor;
-        }
-
-        /**
-         * Abort all existing inflation tasks
-         */
-        public void abortTask() {
-            if (mRunningTask != null) {
-                mRunningTask.abort();
-                mRunningTask = null;
-            }
-        }
-
-        public void setInflationTask(InflationTask abortableTask) {
-            // abort any existing inflation
-            InflationTask existing = mRunningTask;
-            abortTask();
-            mRunningTask = abortableTask;
-            if (existing != null && mRunningTask != null) {
-                mRunningTask.supersedeTask(existing);
-            }
-        }
-
-        public void onInflationTaskFinished() {
-            mRunningTask = null;
-        }
-
-        @VisibleForTesting
-        public InflationTask getRunningTask() {
-            return mRunningTask;
-        }
-
-        /**
-         * Set a throwable that is used for debugging
-         *
-         * @param debugThrowable the throwable to save
-         */
-        public void setDebugThrowable(Throwable debugThrowable) {
-            mDebugThrowable = debugThrowable;
-        }
-
-        public Throwable getDebugThrowable() {
-            return mDebugThrowable;
-        }
-
-        public void onRemoteInputInserted() {
-            lastRemoteInputSent = NOT_LAUNCHED_YET;
-            remoteInputTextWhenReset = null;
-        }
-
-        public void setHasSentReply() {
-            hasSentReply = true;
-        }
-
-        public boolean isLastMessageFromReply() {
-            if (!hasSentReply) {
-                return false;
-            }
-            Bundle extras = notification.getNotification().extras;
-            CharSequence[] replyTexts = extras.getCharSequenceArray(
-                    Notification.EXTRA_REMOTE_INPUT_HISTORY);
-            if (!ArrayUtils.isEmpty(replyTexts)) {
-                return true;
-            }
-            Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
-            if (messages != null && messages.length > 0) {
-                Parcelable message = messages[messages.length - 1];
-                if (message instanceof Bundle) {
-                    Notification.MessagingStyle.Message lastMessage =
-                            Notification.MessagingStyle.Message.getMessageFromBundle(
-                                    (Bundle) message);
-                    if (lastMessage != null) {
-                        Person senderPerson = lastMessage.getSenderPerson();
-                        if (senderPerson == null) {
-                            return true;
-                        }
-                        Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
-                        return Objects.equals(user, senderPerson);
-                    }
-                }
-            }
-            return false;
-        }
-
-        public void setInitializationTime(long time) {
-            if (initializationTime == -1) {
-                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() != 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();
-        }
-
-        boolean isExemptFromDndVisualSuppression() {
-            if (isNotificationBlockedByPolicy(notification.getNotification())) {
-                return false;
-            }
-
-            if ((notification.getNotification().flags
-                    & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
-                return true;
-            }
-            if (notification.getNotification().isMediaNotification()) {
-                return true;
-            }
-            if (mIsSystemNotification != null && mIsSystemNotification) {
-                return true;
-            }
-            return false;
-        }
-
-        private boolean shouldSuppressVisualEffect(int effect) {
-            if (isExemptFromDndVisualSuppression()) {
-                return false;
-            }
-            return (suppressedVisualEffects & effect) != 0;
-        }
-
-        /**
-         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
-         * is set for this entry.
-         */
-        public boolean shouldSuppressFullScreenIntent() {
-            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
-        }
-
-        /**
-         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}
-         * is set for this entry.
-         */
-        public boolean shouldSuppressPeek() {
-            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
-        }
-
-        /**
-         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_STATUS_BAR}
-         * is set for this entry.
-         */
-        public boolean shouldSuppressStatusBar() {
-            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
-        }
-
-        /**
-         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}
-         * is set for this entry.
-         */
-        public boolean shouldSuppressAmbient() {
-            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
-        }
-
-        /**
-         * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
-         * is set for this entry.
-         */
-        public boolean shouldSuppressNotificationList() {
-            return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
-        }
-    }
-
-    private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
-    private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>();
-    private final ArrayList<Entry> mFilteredForUser = new ArrayList<>();
-
-    private final NotificationGroupManager mGroupManager
-            = Dependency.get(NotificationGroupManager.class);
-
-    private RankingMap mRankingMap;
-    private final Ranking mTmpRanking = new Ranking();
-
-    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-    }
-
-    private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
-        private final Ranking mRankingA = new Ranking();
-        private final Ranking mRankingB = new Ranking();
-
-        @Override
-        public int compare(Entry a, Entry b) {
-            final StatusBarNotification na = a.notification;
-            final StatusBarNotification nb = b.notification;
-            int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
-            int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
-            int aRank = 0;
-            int bRank = 0;
-
-            if (mRankingMap != null) {
-                // RankingMap as received from NoMan
-                getRanking(a.key, mRankingA);
-                getRanking(b.key, mRankingB);
-                aImportance = mRankingA.getImportance();
-                bImportance = mRankingB.getImportance();
-                aRank = mRankingA.getRank();
-                bRank = mRankingB.getRank();
-            }
-
-            String mediaNotification = getMediaManager().getMediaNotificationKey();
-
-            // IMPORTANCE_MIN media streams are allowed to drift to the bottom
-            final boolean aMedia = a.key.equals(mediaNotification)
-                    && aImportance > NotificationManager.IMPORTANCE_MIN;
-            final boolean bMedia = b.key.equals(mediaNotification)
-                    && bImportance > NotificationManager.IMPORTANCE_MIN;
-
-            boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH &&
-                    isSystemNotification(na);
-            boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH &&
-                    isSystemNotification(nb);
-
-            boolean isHeadsUp = a.row.isHeadsUp();
-            if (isHeadsUp != b.row.isHeadsUp()) {
-                return isHeadsUp ? -1 : 1;
-            } else if (isHeadsUp) {
-                // Provide consistent ranking with headsUpManager
-                return mHeadsUpManager.compare(a, b);
-            } else if (a.row.isAmbientPulsing() != b.row.isAmbientPulsing()) {
-                return a.row.isAmbientPulsing() ? -1 : 1;
-            } else if (aMedia != bMedia) {
-                // Upsort current media notification.
-                return aMedia ? -1 : 1;
-            } else if (aSystemMax != bSystemMax) {
-                // Upsort PRIORITY_MAX system notifications
-                return aSystemMax ? -1 : 1;
-            } else if (aRank != bRank) {
-                return aRank - bRank;
-            } else {
-                return Long.compare(nb.getNotification().when, na.getNotification().when);
-            }
-        }
-    };
-
-    private KeyguardEnvironment getEnvironment() {
-        if (mEnvironment == null) {
-            mEnvironment = Dependency.get(KeyguardEnvironment.class);
-        }
-        return mEnvironment;
-    }
-
-    private NotificationMediaManager getMediaManager() {
-        if (mMediaManager == null) {
-            mMediaManager = Dependency.get(NotificationMediaManager.class);
-        }
-        return mMediaManager;
-    }
-
-    /**
-     * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
-     *
-     * <p>
-     * This call doesn't update the list of active notifications. Call {@link #filterAndSort()}
-     * when the environment changes.
-     * <p>
-     * Don't hold on to or modify the returned list.
-     */
-    public ArrayList<Entry> getActiveNotifications() {
-        return mSortedAndFiltered;
-    }
-
-    public ArrayList<Entry> getNotificationsForCurrentUser() {
-        mFilteredForUser.clear();
-
-        synchronized (mEntries) {
-            final int N = mEntries.size();
-            for (int i = 0; i < N; i++) {
-                Entry entry = mEntries.valueAt(i);
-                final StatusBarNotification sbn = entry.notification;
-                if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
-                    continue;
-                }
-                mFilteredForUser.add(entry);
-            }
-        }
-        return mFilteredForUser;
-    }
-
-    public Entry get(String key) {
-        return mEntries.get(key);
-    }
-
-    public void add(Entry entry) {
-        synchronized (mEntries) {
-            mEntries.put(entry.notification.getKey(), entry);
-        }
-        mGroupManager.onEntryAdded(entry);
-
-        updateRankingAndSort(mRankingMap);
-    }
-
-    public Entry remove(String key, RankingMap ranking) {
-        Entry removed;
-        synchronized (mEntries) {
-            removed = mEntries.remove(key);
-        }
-        if (removed == null) return null;
-        mGroupManager.onEntryRemoved(removed);
-        updateRankingAndSort(ranking);
-        return removed;
-    }
-
-    /** Updates the given notification entry with the provided ranking. */
-    public void update(Entry entry, RankingMap ranking, StatusBarNotification notification) {
-        updateRanking(ranking);
-        final StatusBarNotification oldNotification = entry.notification;
-        entry.notification = notification;
-        mGroupManager.onEntryUpdated(entry, oldNotification);
-    }
-
-    public void updateRanking(RankingMap ranking) {
-        updateRankingAndSort(ranking);
-    }
-
-    public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
-        synchronized (mEntries) {
-            final int N = mEntries.size();
-            for (int i = 0; i < N; i++) {
-                Entry entry = mEntries.valueAt(i);
-                if (uid == entry.notification.getUid()
-                        && pkg.equals(entry.notification.getPackageName())
-                        && key.equals(entry.key)) {
-                    if (showIcon) {
-                        entry.mActiveAppOps.add(appOp);
-                    } else {
-                        entry.mActiveAppOps.remove(appOp);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns true if this notification should be displayed in the high-priority notifications
-     * section (and on the lockscreen and status bar).
-     */
-    public boolean isHighPriority(StatusBarNotification statusBarNotification) {
-        if (mRankingMap != null) {
-            getRanking(statusBarNotification.getKey(), mTmpRanking);
-            if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
-                    || statusBarNotification.getNotification().isForegroundService()
-                    || statusBarNotification.getNotification().hasMediaSession()) {
-                return true;
-            }
-            if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
-                for (Entry child : mGroupManager.getLogicalChildren(statusBarNotification)) {
-                    if (isHighPriority(child.notification)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    public boolean isAmbient(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.isAmbient();
-        }
-        return false;
-    }
-
-    public int getVisibilityOverride(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getVisibilityOverride();
-        }
-        return Ranking.VISIBILITY_NO_OVERRIDE;
-    }
-
-    /**
-     * Categories that are explicitly called out on DND settings screens are always blocked, if
-     * DND has flagged them, even if they are foreground or system notifications that might
-     * otherwise visually bypass DND.
-     */
-    private static boolean isNotificationBlockedByPolicy(Notification n) {
-        if (isCategory(CATEGORY_CALL, n)
-                || isCategory(CATEGORY_MESSAGE, n)
-                || isCategory(CATEGORY_ALARM, n)
-                || isCategory(CATEGORY_EVENT, n)
-                || isCategory(CATEGORY_REMINDER, n)) {
-            return true;
-        }
-        return false;
-    }
-
-    private static boolean isCategory(String category, Notification n) {
-        return Objects.equals(n.category, category);
-    }
-
-    public int getImportance(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getImportance();
-        }
-        return NotificationManager.IMPORTANCE_UNSPECIFIED;
-    }
-
-    public String getOverrideGroupKey(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getOverrideGroupKey();
-        }
-        return null;
-    }
-
-    public List<SnoozeCriterion> getSnoozeCriteria(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getSnoozeCriteria();
-        }
-        return null;
-    }
-
-    public NotificationChannel getChannel(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getChannel();
-        }
-        return null;
-    }
-
-    public int getRank(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.getRank();
-        }
-        return 0;
-    }
-
-    public boolean shouldHide(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return mTmpRanking.isSuspended();
-        }
-        return false;
-    }
-
-    private void updateRankingAndSort(RankingMap ranking) {
-        if (ranking != null) {
-            mRankingMap = ranking;
-            synchronized (mEntries) {
-                final int N = mEntries.size();
-                for (int i = 0; i < N; i++) {
-                    Entry entry = mEntries.valueAt(i);
-                    if (!getRanking(entry.key, mTmpRanking)) {
-                        continue;
-                    }
-                    final StatusBarNotification oldSbn = entry.notification.cloneLight();
-                    final String overrideGroupKey = getOverrideGroupKey(entry.key);
-                    if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
-                        entry.notification.setOverrideGroupKey(overrideGroupKey);
-                        mGroupManager.onEntryUpdated(entry, oldSbn);
-                    }
-                    entry.populateFromRanking(mTmpRanking);
-                }
-            }
-        }
-        filterAndSort();
-    }
-
-    /**
-     * Get the ranking from the current ranking map.
-     *
-     * @param key the key to look up
-     * @param outRanking the ranking to populate
-     *
-     * @return {@code true} if the ranking was properly obtained.
-     */
-    @VisibleForTesting
-    protected boolean getRanking(String key, Ranking outRanking) {
-        return mRankingMap.getRanking(key, outRanking);
-    }
-
-    // TODO: This should not be public. Instead the Environment should notify this class when
-    // anything changed, and this class should call back the UI so it updates itself.
-    public void filterAndSort() {
-        mSortedAndFiltered.clear();
-
-        synchronized (mEntries) {
-            final int N = mEntries.size();
-            for (int i = 0; i < N; i++) {
-                Entry entry = mEntries.valueAt(i);
-
-                if (mNotificationFilter.shouldFilterOut(entry)) {
-                    continue;
-                }
-
-                mSortedAndFiltered.add(entry);
-            }
-        }
-
-        Collections.sort(mSortedAndFiltered, mRankingComparator);
-    }
-
-    public void dump(PrintWriter pw, String indent) {
-        int N = mSortedAndFiltered.size();
-        pw.print(indent);
-        pw.println("active notifications: " + N);
-        int active;
-        for (active = 0; active < N; active++) {
-            NotificationData.Entry e = mSortedAndFiltered.get(active);
-            dumpEntry(pw, indent, active, e);
-        }
-        synchronized (mEntries) {
-            int M = mEntries.size();
-            pw.print(indent);
-            pw.println("inactive notifications: " + (M - active));
-            int inactiveCount = 0;
-            for (int i = 0; i < M; i++) {
-                Entry entry = mEntries.valueAt(i);
-                if (!mSortedAndFiltered.contains(entry)) {
-                    dumpEntry(pw, indent, inactiveCount, entry);
-                    inactiveCount++;
-                }
-            }
-        }
-    }
-
-    private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) {
-        getRanking(e.key, mTmpRanking);
-        pw.print(indent);
-        pw.println("  [" + i + "] key=" + e.key + " icon=" + e.icon);
-        StatusBarNotification n = e.notification;
-        pw.print(indent);
-        pw.println("      pkg=" + n.getPackageName() + " id=" + n.getId() + " importance=" +
-                mTmpRanking.getImportance());
-        pw.print(indent);
-        pw.println("      notification=" + n.getNotification());
-    }
-
-    private static boolean isSystemNotification(StatusBarNotification sbn) {
-        String sbnPackage = sbn.getPackageName();
-        return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
-    }
-
-    /**
-     * Provides access to keyguard state and user settings dependent data.
-     */
-    public interface KeyguardEnvironment {
-        boolean isDeviceProvisioned();
-        boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index 1d06ce0..c640760 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -19,6 +19,7 @@
 import android.service.notification.StatusBarNotification;
 
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
 
 /**
@@ -29,25 +30,25 @@
      * Called when a new notification is posted. At this point, the notification is "pending": its
      * views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
      */
-    default void onPendingEntryAdded(NotificationData.Entry entry) {
+    default void onPendingEntryAdded(NotificationEntry entry) {
     }
 
     /**
      * Called when a new entry is created.
      */
-    default void onNotificationAdded(NotificationData.Entry entry) {
+    default void onNotificationAdded(NotificationEntry entry) {
     }
 
     /**
      * Called when a notification was updated.
      */
-    default void onEntryUpdated(NotificationData.Entry entry) {
+    default void onEntryUpdated(NotificationEntry entry) {
     }
 
     /**
      * Called when a notification's views are inflated for the first time.
      */
-    default void onEntryInflated(NotificationData.Entry entry,
+    default void onEntryInflated(NotificationEntry entry,
             @NotificationInflater.InflationFlag int inflatedFlags) {
     }
 
@@ -57,7 +58,7 @@
      *
      * @param entry notification data entry that was reinflated.
      */
-    default void onEntryReinflated(NotificationData.Entry entry) {
+    default void onEntryReinflated(NotificationEntry entry) {
     }
 
     /**
@@ -71,20 +72,14 @@
      * because the developer retracted it).
      * @param entry notification data entry that was removed.  Null if no entry existed for the
      *              removed key at the time of removal.
-     * @param key key of notification that was removed
-     * @param old StatusBarNotification of the notification before it was removed
      * @param visibility logging data related to the visibility of the notification at the time of
      *                   removal, if it was removed by a user action.  Null if it was not removed by
      *                   a user action.
-     * @param lifetimeExtended true if something is artificially extending how long the notification
      * @param removedByUser true if the notification was removed by a user action
      */
     default void onEntryRemoved(
-            @Nullable NotificationData.Entry entry,
-            String key,
-            StatusBarNotification old,
+            NotificationEntry entry,
             @Nullable NotificationVisibility visibility,
-            boolean lifetimeExtended,
             boolean removedByUser) {
     }
 }
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 e0fa723..a4e3f33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -15,13 +15,10 @@
  */
 package com.android.systemui.statusbar.notification;
 
-import static com.android.systemui.bubbles.BubbleController.DEBUG_DEMOTE_TO_NOTIF;
-
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.content.Context;
 import android.os.Handler;
-import android.os.PowerManager;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
@@ -34,14 +31,14 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationUiAdjustment;
 import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
@@ -66,15 +63,14 @@
         Dumpable,
         NotificationInflater.InflationCallback,
         NotificationUpdateHandler,
-        VisualStabilityManager.Callback,
-        BubbleController.BubbleDismissListener {
+        VisualStabilityManager.Callback {
     private static final String TAG = "NotificationEntryMgr";
     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
 
     protected final Context mContext;
-    protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
+    protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
 
     private final NotificationGutsManager mGutsManager =
             Dependency.get(NotificationGutsManager.class);
@@ -82,7 +78,6 @@
             Dependency.get(DeviceProvisionedController.class);
     private final ForegroundServiceController mForegroundServiceController =
             Dependency.get(ForegroundServiceController.class);
-    private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
 
     // Lazily retrieved dependencies
     private NotificationRemoteInputManager mRemoteInputManager;
@@ -92,9 +87,7 @@
     private Runnable mUpdateNotificationViewsCallback;
 
     private NotificationPresenter mPresenter;
-    protected PowerManager mPowerManager;
     private NotificationListenerService.RankingMap mLatestRankingMap;
-    protected HeadsUpManager mHeadsUpManager;
     protected NotificationData mNotificationData;
     protected NotificationListContainer mListContainer;
     @VisibleForTesting
@@ -122,7 +115,7 @@
         if (mPendingNotifications.size() == 0) {
             pw.println("null");
         } else {
-            for (NotificationData.Entry entry : mPendingNotifications.values()) {
+            for (NotificationEntry entry : mPendingNotifications.values()) {
                 pw.println(entry.notification);
             }
         }
@@ -130,8 +123,6 @@
 
     public NotificationEntryManager(Context context) {
         mContext = context;
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        mBubbleController.setDismissListener(this /* bubbleEventListener */);
         mNotificationData = new NotificationData();
         mDeferredNotificationViewUpdateHandler = new Handler();
     }
@@ -158,13 +149,18 @@
         return mNotificationRowBinder;
     }
 
+    // TODO: Remove this once we can always use a mocked row binder in our tests
+    @VisibleForTesting
+    void setRowBinder(NotificationRowBinder notificationRowBinder) {
+        mNotificationRowBinder = notificationRowBinder;
+    }
+
     public void setUpWithPresenter(NotificationPresenter presenter,
             NotificationListContainer listContainer,
             HeadsUpManager headsUpManager) {
         mPresenter = presenter;
         mUpdateNotificationViewsCallback = mPresenter::updateNotificationViews;
-        mHeadsUpManager = headsUpManager;
-        mNotificationData.setHeadsUpManager(mHeadsUpManager);
+        mNotificationData.setHeadsUpManager(headsUpManager);
         mListContainer = listContainer;
 
         mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
@@ -195,10 +191,6 @@
         return mPresenter;
     }
 
-    public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
-        return getRowBinder().getNotificationLongClicker();
-    }
-
     @Override
     public void onReorderingAllowed() {
         updateNotifications();
@@ -213,30 +205,13 @@
                 n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */);
     }
 
-    @Override
-    public void onStackDismissed() {
-        updateNotifications();
-    }
-
-    @Override
-    public void onBubbleDismissed(String key) {
-        NotificationData.Entry entry = mNotificationData.get(key);
-        if (entry != null) {
-            entry.setBubbleDismissed(true);
-            if (!DEBUG_DEMOTE_TO_NOTIF) {
-                performRemoveNotification(entry.notification);
-            }
-        }
-        updateNotifications();
-    }
-
     private void abortExistingInflation(String key) {
         if (mPendingNotifications.containsKey(key)) {
-            NotificationData.Entry entry = mPendingNotifications.get(key);
+            NotificationEntry entry = mPendingNotifications.get(key);
             entry.abortTask();
             mPendingNotifications.remove(key);
         }
-        NotificationData.Entry addedEntry = mNotificationData.get(key);
+        NotificationEntry addedEntry = mNotificationData.get(key);
         if (addedEntry != null) {
             addedEntry.abortTask();
         }
@@ -257,22 +232,7 @@
         }
     }
 
-    private void addEntry(NotificationData.Entry shadeEntry) {
-        if (shadeEntry == null) {
-            return;
-        }
-        // Add the expanded view and icon.
-        mNotificationData.add(shadeEntry);
-        tagForeground(shadeEntry.notification);
-        updateNotifications();
-        for (NotificationEntryListener listener : mNotificationEntryListeners) {
-            listener.onNotificationAdded(shadeEntry);
-        }
-
-        maybeScheduleUpdateNotificationViews(shadeEntry);
-    }
-
-    private void maybeScheduleUpdateNotificationViews(NotificationData.Entry entry) {
+    private void maybeScheduleUpdateNotificationViews(NotificationEntry entry) {
         long audibleAlertTimeout = RECENTLY_ALERTED_THRESHOLD_MS
                 - (System.currentTimeMillis() - entry.lastAudiblyAlertedMs);
         if (audibleAlertTimeout > 0) {
@@ -282,7 +242,7 @@
     }
 
     @Override
-    public void onAsyncInflationFinished(NotificationData.Entry entry,
+    public void onAsyncInflationFinished(NotificationEntry entry,
             @InflationFlag int inflatedFlags) {
         mPendingNotifications.remove(entry.key);
         // If there was an async task started after the removal, we don't want to add it back to
@@ -293,7 +253,13 @@
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
                     listener.onEntryInflated(entry, inflatedFlags);
                 }
-                addEntry(entry);
+                mNotificationData.add(entry);
+                tagForeground(entry.notification);
+                updateNotifications();
+                for (NotificationEntryListener listener : mNotificationEntryListeners) {
+                    listener.onNotificationAdded(entry);
+                }
+                maybeScheduleUpdateNotificationViews(entry);
             } else {
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
                     listener.onEntryReinflated(entry);
@@ -315,11 +281,10 @@
             @Nullable NotificationVisibility visibility,
             boolean forceRemove,
             boolean removedByUser) {
-        final NotificationData.Entry entry = mNotificationData.get(key);
+        final NotificationEntry entry = mNotificationData.get(key);
 
         abortExistingInflation(key);
 
-        StatusBarNotification old = null;
         boolean lifetimeExtended = false;
 
         if (entry != null) {
@@ -344,8 +309,6 @@
                     extender.setShouldManageLifetime(entry, false /* shouldManage */);
                 }
 
-                mForegroundServiceController.removeNotification(entry.notification);
-
                 if (entry.rowExists()) {
                     entry.removeRow();
                     mListContainer.cleanUpViewStateForEntry(entry);
@@ -354,25 +317,15 @@
                 // Let's remove the children if this was a summary
                 handleGroupSummaryRemoved(key);
 
-                old = removeNotificationViews(key, ranking);
+                mNotificationData.remove(key, ranking);
+                updateNotifications();
+                Dependency.get(LeakDetector.class).trackGarbage(entry);
+
+                for (NotificationEntryListener listener : mNotificationEntryListeners) {
+                    listener.onEntryRemoved(entry, visibility, removedByUser);
+                }
             }
         }
-
-        for (NotificationEntryListener listener : mNotificationEntryListeners) {
-            listener.onEntryRemoved(entry, key, old, visibility, lifetimeExtended, removedByUser);
-        }
-    }
-
-    private StatusBarNotification removeNotificationViews(String key,
-            NotificationListenerService.RankingMap ranking) {
-        NotificationData.Entry entry = mNotificationData.remove(key, ranking);
-        if (entry == null) {
-            Log.w(TAG, "removeNotification for unknown key: " + key);
-            return null;
-        }
-        updateNotifications();
-        Dependency.get(LeakDetector.class).trackGarbage(entry);
-        return entry.notification;
     }
 
     /**
@@ -386,19 +339,19 @@
      *
      */
     private void handleGroupSummaryRemoved(String key) {
-        NotificationData.Entry entry = mNotificationData.get(key);
+        NotificationEntry entry = mNotificationData.get(key);
         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<NotificationData.Entry> childEntries = entry.getChildren();
+            List<NotificationEntry> childEntries = entry.getChildren();
             if (childEntries == null) {
                 return;
             }
             for (int i = 0; i < childEntries.size(); i++) {
-                NotificationData.Entry childEntry = childEntries.get(i);
+                NotificationEntry childEntry = childEntries.get(i);
                 boolean isForeground = (entry.notification.getNotification().flags
                         & Notification.FLAG_FOREGROUND_SERVICE) != 0;
                 boolean keepForReply =
@@ -418,10 +371,10 @@
     }
 
     public void updateNotificationsOnDensityOrFontScaleChanged() {
-        ArrayList<NotificationData.Entry> userNotifications =
+        ArrayList<NotificationEntry> userNotifications =
                 mNotificationData.getNotificationsForCurrentUser();
         for (int i = 0; i < userNotifications.size(); i++) {
-            NotificationData.Entry entry = userNotifications.get(i);
+            NotificationEntry entry = userNotifications.get(i);
             entry.onDensityOrFontScaleChanged();
             boolean exposedGuts = entry.areGutsExposed();
             if (exposedGuts) {
@@ -430,26 +383,6 @@
         }
     }
 
-    private NotificationData.Entry createNotificationEntry(
-            StatusBarNotification sbn, NotificationListenerService.Ranking ranking)
-            throws InflationException {
-        if (DEBUG) {
-            Log.d(TAG, "createNotificationEntry(notification=" + sbn + " " + ranking);
-        }
-
-        NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking);
-        if (BubbleController.shouldAutoBubble(getContext(), entry)) {
-            entry.setIsBubble(true);
-        }
-
-        Dependency.get(LeakDetector.class).trackInstance(entry);
-        entry.createIcons(mContext, sbn);
-        // Construct the expanded view.
-        getRowBinder().inflateViews(entry, () -> performRemoveNotification(sbn),
-                mNotificationData.get(entry.key) != null);
-        return entry;
-    }
-
     private void addNotificationInternal(StatusBarNotification notification,
             NotificationListenerService.RankingMap rankingMap) throws InflationException {
         String key = notification.getKey();
@@ -460,11 +393,15 @@
         mNotificationData.updateRanking(rankingMap);
         NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
         rankingMap.getRanking(key, ranking);
-        NotificationData.Entry entry = createNotificationEntry(notification, ranking);
-        abortExistingInflation(key);
 
-        mForegroundServiceController.addNotification(notification,
-                mNotificationData.getImportance(key));
+        NotificationEntry entry = new NotificationEntry(notification, ranking);
+
+        Dependency.get(LeakDetector.class).trackInstance(entry);
+        // Construct the expanded view.
+        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
+                mNotificationData.get(entry.key) != null);
+
+        abortExistingInflation(key);
 
         mPendingNotifications.put(key, entry);
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
@@ -510,7 +447,7 @@
 
         final String key = notification.getKey();
         abortExistingInflation(key);
-        NotificationData.Entry entry = mNotificationData.get(key);
+        NotificationEntry entry = mNotificationData.get(key);
         if (entry == null) {
             return;
         }
@@ -523,21 +460,11 @@
 
         mNotificationData.update(entry, ranking, notification);
 
-        entry.updateIcons(mContext, notification);
         getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
                 mNotificationData.get(entry.key) != null);
 
-        mForegroundServiceController.updateNotification(notification,
-                mNotificationData.getImportance(key));
-
         updateNotifications();
 
-        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);
-        }
-
         if (DEBUG) {
             // Is this for you?
             boolean isForCurrentUser = Dependency.get(KeyguardEnvironment.class)
@@ -569,14 +496,14 @@
     }
 
     public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
-        List<NotificationData.Entry> entries = new ArrayList<>();
+        List<NotificationEntry> entries = new ArrayList<>();
         entries.addAll(mNotificationData.getActiveNotifications());
         entries.addAll(mPendingNotifications.values());
 
         // Has a copy of the current UI adjustments.
         ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
         ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
-        for (NotificationData.Entry entry : entries) {
+        for (NotificationEntry entry : entries) {
             NotificationUiAdjustment adjustment =
                     NotificationUiAdjustment.extractFromNotificationEntry(entry);
             oldAdjustments.put(entry.key, adjustment);
@@ -588,7 +515,7 @@
         updateRankingOfPendingNotifications(rankingMap);
 
         // By comparing the old and new UI adjustments, reinflate the view accordingly.
-        for (NotificationData.Entry entry : entries) {
+        for (NotificationEntry entry : entries) {
             getRowBinder().onNotificationRankingUpdated(
                     entry,
                     oldImportances.get(entry.key),
@@ -606,7 +533,7 @@
             return;
         }
         NotificationListenerService.Ranking tmpRanking = new NotificationListenerService.Ranking();
-        for (NotificationData.Entry pendingNotification : mPendingNotifications.values()) {
+        for (NotificationEntry pendingNotification : mPendingNotifications.values()) {
             rankingMap.getRanking(pendingNotification.key, tmpRanking);
             pendingNotification.populateFromRanking(tmpRanking);
         }
@@ -617,7 +544,7 @@
      * notifications whose views have not yet been inflated. In general, the system pretends like
      * these don't exist, although there are a couple exceptions.
      */
-    public Iterable<NotificationData.Entry> getPendingNotificationsIterator() {
+    public Iterable<NotificationEntry> getPendingNotificationsIterator() {
         return mPendingNotifications.values();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index 5e99c38..e199ead 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -28,6 +28,8 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -82,7 +84,7 @@
     /**
      * @return true if the provided notification should NOT be shown right now.
      */
-    public boolean shouldFilterOut(NotificationData.Entry entry) {
+    public boolean shouldFilterOut(NotificationEntry entry) {
         final StatusBarNotification sbn = entry.notification;
         if (!(getEnvironment().isDeviceProvisioned()
                 || showNotificationEvenIfUnprovisioned(sbn))) {
@@ -117,8 +119,8 @@
             return true;
         }
 
-        if (getFsc().isDungeonNotification(sbn)
-                && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
+        if (getFsc().isDisclosureNotification(sbn)
+                && !getFsc().isDisclosureNeededForUser(sbn.getUserId())) {
             // this is a foreground-service disclosure for a user that does not need to show one
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
index 8bd0e9a..fc7a2b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -37,6 +37,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -139,7 +140,7 @@
      * @param entry the entry to check
      * @return true if the entry should heads up, false otherwise
      */
-    public boolean shouldHeadsUp(NotificationData.Entry entry) {
+    public boolean shouldHeadsUp(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
 
         if (getShadeController().isDozing()) {
@@ -227,7 +228,7 @@
      * @param entry the entry to check
      * @return true if the entry should ambient pulse, false otherwise
      */
-    public boolean shouldPulse(NotificationData.Entry entry) {
+    public boolean shouldPulse(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
 
         if (!getShadeController().isDozing()) {
@@ -273,14 +274,14 @@
 
     /**
      * Common checks between heads up alerting and ambient pulse alerting.  See
-     * {@link #shouldHeadsUp(NotificationData.Entry)} and
-     * {@link #shouldPulse(NotificationData.Entry)}.  Notifications that fail any of these checks
+     * {@link #shouldHeadsUp(NotificationEntry)} and
+     * {@link #shouldPulse(NotificationEntry)}.  Notifications that fail any of these checks
      * should not alert at all.
      *
      * @param entry the entry to check
      * @return true if these checks pass, false if the notification should not alert
      */
-    protected boolean canAlertCommon(NotificationData.Entry entry) {
+    protected boolean canAlertCommon(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
 
         if (mNotificationFilter.shouldFilterOut(entry)) {
@@ -325,7 +326,7 @@
          * @param sbn   notification that might be heads upped
          * @return false if the notification can not be heads upped
          */
-        boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
+        boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn);
 
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
index b241b8a..cc302b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
 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;
@@ -41,6 +42,7 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationUiAdjustment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
@@ -51,6 +53,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 import javax.inject.Singleton;
 
 /** Handles inflating and updating views for notifications. */
@@ -72,6 +75,7 @@
     private final NotificationMessagingUtil mMessagingUtil;
     private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
             this::logNotificationExpansion;
+    private final boolean mAllowLongPress;
 
     private NotificationRemoteInputManager mRemoteInputManager;
     private NotificationPresenter mPresenter;
@@ -83,9 +87,11 @@
     private NotificationClicker mNotificationClicker;
 
     @Inject
-    public NotificationRowBinder(Context context) {
+    public NotificationRowBinder(Context context,
+            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress) {
         mContext = context;
         mMessagingUtil = new NotificationMessagingUtil(context);
+        mAllowLongPress = allowLongPress;
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
     }
@@ -120,17 +126,22 @@
     /**
      * Inflates the views for the given entry (possibly asynchronously).
      */
-    public void inflateViews(NotificationData.Entry entry, Runnable onDismissRunnable,
-            boolean isUpdate) {
+    public void inflateViews(
+            NotificationEntry entry,
+            Runnable onDismissRunnable,
+            boolean isUpdate)
+            throws InflationException {
         ViewGroup parent = mListContainer.getViewParentForNotification(entry);
         PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
                 entry.notification.getUser().getIdentifier());
 
         final StatusBarNotification sbn = entry.notification;
         if (entry.rowExists()) {
+            entry.updateIcons(mContext, sbn);
             entry.reset();
             updateNotification(entry, pmUser, sbn, entry.getRow(), isUpdate);
         } else {
+            entry.createIcons(mContext, sbn);
             new RowInflaterTask().inflate(mContext, parent, entry,
                     row -> {
                         bindRow(entry, pmUser, sbn, row, onDismissRunnable);
@@ -139,7 +150,7 @@
         }
     }
 
-    private void bindRow(NotificationData.Entry entry, PackageManager pmUser,
+    private void bindRow(NotificationEntry entry, PackageManager pmUser,
             StatusBarNotification sbn, ExpandableNotificationRow row,
             Runnable onDismissRunnable) {
         row.setExpansionLogger(mExpansionLogger, entry.notification.getKey());
@@ -147,7 +158,9 @@
         row.setHeadsUpManager(mHeadsUpManager);
         row.setOnExpandClickListener(mPresenter);
         row.setInflationCallback(mInflationCallback);
-        row.setLongPressListener(getNotificationLongClicker());
+        if (mAllowLongPress) {
+            row.setLongPressListener(mGutsManager::openGuts);
+        }
         mListContainer.bindRow(row);
         getRemoteInputManager().bindRow(row);
 
@@ -185,7 +198,7 @@
      * reinflating them.
      */
     public void onNotificationRankingUpdated(
-            NotificationData.Entry entry,
+            NotificationEntry entry,
             @Nullable Integer oldImportance,
             NotificationUiAdjustment oldAdjustment,
             NotificationUiAdjustment newAdjustment,
@@ -212,7 +225,7 @@
 
     //TODO: This method associates a row with an entry, but eventually needs to not do that
     private void updateNotification(
-            NotificationData.Entry entry,
+            NotificationEntry entry,
             PackageManager pmUser,
             StatusBarNotification sbn,
             ExpandableNotificationRow row,
@@ -260,10 +273,6 @@
         row.inflateViews();
     }
 
-    ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
-        return mGutsManager::openGuts;
-    }
-
     private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
         mUiOffloadThread.submit(() -> {
             try {
@@ -284,7 +293,7 @@
          * @param sbn    notification
          * @param row    row for the notification
          */
-        void onBindRow(NotificationData.Entry entry, PackageManager pmUser,
+        void onBindRow(NotificationEntry entry, PackageManager pmUser,
                 StatusBarNotification sbn, ExpandableNotificationRow row);
     }
 }
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 a194eef..f09c57d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification;
 
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
 /**
  * An object that can determine the visibility of a Notification.
  */
@@ -27,5 +29,5 @@
      * @param entry
      * @return true if row is in a visible location
      */
-    boolean isInVisibleLocation(NotificationData.Entry entry);
+    boolean isInVisibleLocation(NotificationEntry 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 8e6a93d..c886685 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -21,6 +21,7 @@
 import androidx.collection.ArraySet;
 
 import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
@@ -52,7 +53,7 @@
     public VisualStabilityManager(NotificationEntryManager notificationEntryManager) {
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
-            public void onEntryReinflated(NotificationData.Entry entry) {
+            public void onEntryReinflated(NotificationEntry entry) {
                 if (entry.hasLowPriorityStateUpdated()) {
                     onLowPriorityUpdated(entry);
                     if (mPresenter != null) {
@@ -163,7 +164,7 @@
     }
 
     @Override
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         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.
@@ -171,7 +172,7 @@
         }
     }
 
-    private void onLowPriorityUpdated(NotificationData.Entry entry) {
+    private void onLowPriorityUpdated(NotificationEntry entry) {
         mLowPriorityReorderingViews.add(entry.getRow());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
new file mode 100644
index 0000000..8c29fb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The list of currently displaying notifications.
+ */
+public class NotificationData {
+
+    private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
+    /**
+     * These dependencies are late init-ed
+     */
+    private KeyguardEnvironment mEnvironment;
+    private NotificationMediaManager mMediaManager;
+
+    private HeadsUpManager mHeadsUpManager;
+
+    private final ArrayMap<String, NotificationEntry> mEntries = new ArrayMap<>();
+    private final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
+    private final ArrayList<NotificationEntry> mFilteredForUser = new ArrayList<>();
+
+    private final NotificationGroupManager mGroupManager =
+            Dependency.get(NotificationGroupManager.class);
+
+    private RankingMap mRankingMap;
+    private final Ranking mTmpRanking = new Ranking();
+
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
+    private final Comparator<NotificationEntry> mRankingComparator =
+            new Comparator<NotificationEntry>() {
+        private final Ranking mRankingA = new Ranking();
+        private final Ranking mRankingB = new Ranking();
+
+        @Override
+        public int compare(NotificationEntry a, NotificationEntry b) {
+            final StatusBarNotification na = a.notification;
+            final StatusBarNotification nb = b.notification;
+            int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
+            int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
+            int aRank = 0;
+            int bRank = 0;
+
+            if (mRankingMap != null) {
+                // RankingMap as received from NoMan
+                getRanking(a.key, mRankingA);
+                getRanking(b.key, mRankingB);
+                aImportance = mRankingA.getImportance();
+                bImportance = mRankingB.getImportance();
+                aRank = mRankingA.getRank();
+                bRank = mRankingB.getRank();
+            }
+
+            String mediaNotification = getMediaManager().getMediaNotificationKey();
+
+            // IMPORTANCE_MIN media streams are allowed to drift to the bottom
+            final boolean aMedia = a.key.equals(mediaNotification)
+                    && aImportance > NotificationManager.IMPORTANCE_MIN;
+            final boolean bMedia = b.key.equals(mediaNotification)
+                    && bImportance > NotificationManager.IMPORTANCE_MIN;
+
+            boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH
+                    && isSystemNotification(na);
+            boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH
+                    && isSystemNotification(nb);
+
+            boolean isHeadsUp = a.getRow().isHeadsUp();
+            if (isHeadsUp != b.getRow().isHeadsUp()) {
+                return isHeadsUp ? -1 : 1;
+            } else if (isHeadsUp) {
+                // Provide consistent ranking with headsUpManager
+                return mHeadsUpManager.compare(a, b);
+            } else if (a.getRow().isAmbientPulsing() != b.getRow().isAmbientPulsing()) {
+                return a.getRow().isAmbientPulsing() ? -1 : 1;
+            } else if (aMedia != bMedia) {
+                // Upsort current media notification.
+                return aMedia ? -1 : 1;
+            } else if (aSystemMax != bSystemMax) {
+                // Upsort PRIORITY_MAX system notifications
+                return aSystemMax ? -1 : 1;
+            } else if (aRank != bRank) {
+                return aRank - bRank;
+            } else {
+                return Long.compare(nb.getNotification().when, na.getNotification().when);
+            }
+        }
+    };
+
+    private KeyguardEnvironment getEnvironment() {
+        if (mEnvironment == null) {
+            mEnvironment = Dependency.get(KeyguardEnvironment.class);
+        }
+        return mEnvironment;
+    }
+
+    private NotificationMediaManager getMediaManager() {
+        if (mMediaManager == null) {
+            mMediaManager = Dependency.get(NotificationMediaManager.class);
+        }
+        return mMediaManager;
+    }
+
+    /**
+     * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
+     *
+     * <p>
+     * This call doesn't update the list of active notifications. Call {@link #filterAndSort()}
+     * when the environment changes.
+     * <p>
+     * Don't hold on to or modify the returned list.
+     */
+    public ArrayList<NotificationEntry> getActiveNotifications() {
+        return mSortedAndFiltered;
+    }
+
+    public ArrayList<NotificationEntry> getNotificationsForCurrentUser() {
+        mFilteredForUser.clear();
+
+        synchronized (mEntries) {
+            final int len = mEntries.size();
+            for (int i = 0; i < len; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+                final StatusBarNotification sbn = entry.notification;
+                if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
+                    continue;
+                }
+                mFilteredForUser.add(entry);
+            }
+        }
+        return mFilteredForUser;
+    }
+
+    public NotificationEntry get(String key) {
+        return mEntries.get(key);
+    }
+
+    public void add(NotificationEntry entry) {
+        synchronized (mEntries) {
+            mEntries.put(entry.notification.getKey(), entry);
+        }
+        mGroupManager.onEntryAdded(entry);
+
+        updateRankingAndSort(mRankingMap);
+    }
+
+    public NotificationEntry remove(String key, RankingMap ranking) {
+        NotificationEntry removed;
+        synchronized (mEntries) {
+            removed = mEntries.remove(key);
+        }
+        if (removed == null) return null;
+        mGroupManager.onEntryRemoved(removed);
+        updateRankingAndSort(ranking);
+        return removed;
+    }
+
+    /** Updates the given notification entry with the provided ranking. */
+    public void update(
+            NotificationEntry entry,
+            RankingMap ranking,
+            StatusBarNotification notification) {
+        updateRanking(ranking);
+        final StatusBarNotification oldNotification = entry.notification;
+        entry.notification = notification;
+        mGroupManager.onEntryUpdated(entry, oldNotification);
+    }
+
+    public void updateRanking(RankingMap ranking) {
+        updateRankingAndSort(ranking);
+    }
+
+    public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
+        synchronized (mEntries) {
+            final int len = mEntries.size();
+            for (int i = 0; i < len; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+                if (uid == entry.notification.getUid()
+                        && pkg.equals(entry.notification.getPackageName())
+                        && key.equals(entry.key)) {
+                    if (showIcon) {
+                        entry.mActiveAppOps.add(appOp);
+                    } else {
+                        entry.mActiveAppOps.remove(appOp);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns true if this notification should be displayed in the high-priority notifications
+     * section (and on the lockscreen and status bar).
+     */
+    public boolean isHighPriority(StatusBarNotification statusBarNotification) {
+        if (mRankingMap != null) {
+            getRanking(statusBarNotification.getKey(), mTmpRanking);
+            if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
+                    || statusBarNotification.getNotification().isForegroundService()
+                    || statusBarNotification.getNotification().hasMediaSession()) {
+                return true;
+            }
+            if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
+                final ArrayList<NotificationEntry> logicalChildren =
+                        mGroupManager.getLogicalChildren(statusBarNotification);
+                for (NotificationEntry child : logicalChildren) {
+                    if (isHighPriority(child.notification)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    public boolean isAmbient(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.isAmbient();
+        }
+        return false;
+    }
+
+    public int getVisibilityOverride(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getVisibilityOverride();
+        }
+        return Ranking.VISIBILITY_NO_OVERRIDE;
+    }
+
+    public int getImportance(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getImportance();
+        }
+        return NotificationManager.IMPORTANCE_UNSPECIFIED;
+    }
+
+    public String getOverrideGroupKey(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getOverrideGroupKey();
+        }
+        return null;
+    }
+
+    public List<SnoozeCriterion> getSnoozeCriteria(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getSnoozeCriteria();
+        }
+        return null;
+    }
+
+    public NotificationChannel getChannel(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getChannel();
+        }
+        return null;
+    }
+
+    public int getRank(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.getRank();
+        }
+        return 0;
+    }
+
+    public boolean shouldHide(String key) {
+        if (mRankingMap != null) {
+            getRanking(key, mTmpRanking);
+            return mTmpRanking.isSuspended();
+        }
+        return false;
+    }
+
+    private void updateRankingAndSort(RankingMap ranking) {
+        if (ranking != null) {
+            mRankingMap = ranking;
+            synchronized (mEntries) {
+                final int len = mEntries.size();
+                for (int i = 0; i < len; i++) {
+                    NotificationEntry entry = mEntries.valueAt(i);
+                    if (!getRanking(entry.key, mTmpRanking)) {
+                        continue;
+                    }
+                    final StatusBarNotification oldSbn = entry.notification.cloneLight();
+                    final String overrideGroupKey = getOverrideGroupKey(entry.key);
+                    if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
+                        entry.notification.setOverrideGroupKey(overrideGroupKey);
+                        mGroupManager.onEntryUpdated(entry, oldSbn);
+                    }
+                    entry.populateFromRanking(mTmpRanking);
+                }
+            }
+        }
+        filterAndSort();
+    }
+
+    /**
+     * Get the ranking from the current ranking map.
+     *
+     * @param key the key to look up
+     * @param outRanking the ranking to populate
+     *
+     * @return {@code true} if the ranking was properly obtained.
+     */
+    @VisibleForTesting
+    protected boolean getRanking(String key, Ranking outRanking) {
+        return mRankingMap.getRanking(key, outRanking);
+    }
+
+    // TODO: This should not be public. Instead the Environment should notify this class when
+    // anything changed, and this class should call back the UI so it updates itself.
+    public void filterAndSort() {
+        mSortedAndFiltered.clear();
+
+        synchronized (mEntries) {
+            final int len = mEntries.size();
+            for (int i = 0; i < len; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+
+                if (mNotificationFilter.shouldFilterOut(entry)) {
+                    continue;
+                }
+
+                mSortedAndFiltered.add(entry);
+            }
+        }
+
+        Collections.sort(mSortedAndFiltered, mRankingComparator);
+    }
+
+    public void dump(PrintWriter pw, String indent) {
+        int filteredLen = mSortedAndFiltered.size();
+        pw.print(indent);
+        pw.println("active notifications: " + filteredLen);
+        int active;
+        for (active = 0; active < filteredLen; active++) {
+            NotificationEntry e = mSortedAndFiltered.get(active);
+            dumpEntry(pw, indent, active, e);
+        }
+        synchronized (mEntries) {
+            int totalLen = mEntries.size();
+            pw.print(indent);
+            pw.println("inactive notifications: " + (totalLen - active));
+            int inactiveCount = 0;
+            for (int i = 0; i < totalLen; i++) {
+                NotificationEntry entry = mEntries.valueAt(i);
+                if (!mSortedAndFiltered.contains(entry)) {
+                    dumpEntry(pw, indent, inactiveCount, entry);
+                    inactiveCount++;
+                }
+            }
+        }
+    }
+
+    private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
+        getRanking(e.key, mTmpRanking);
+        pw.print(indent);
+        pw.println("  [" + i + "] key=" + e.key + " icon=" + e.icon);
+        StatusBarNotification n = e.notification;
+        pw.print(indent);
+        pw.println("      pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
+                + mTmpRanking.getImportance());
+        pw.print(indent);
+        pw.println("      notification=" + n.getNotification());
+    }
+
+    private static boolean isSystemNotification(StatusBarNotification sbn) {
+        String sbnPackage = sbn.getPackageName();
+        return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
+    }
+
+    /**
+     * Provides access to keyguard state and user settings dependent data.
+     */
+    public interface KeyguardEnvironment {
+        boolean isDeviceProvisioned();
+        boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
new file mode 100644
index 0000000..58aa02c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -0,0 +1,701 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static android.app.Notification.CATEGORY_ALARM;
+import static android.app.Notification.CATEGORY_CALL;
+import static android.app.Notification.CATEGORY_EVENT;
+import static android.app.Notification.CATEGORY_MESSAGE;
+import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager.Policy;
+import android.app.Person;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a notification that the system UI knows about
+ *
+ * Whenever the NotificationManager tells us about the existence of a new notification, we wrap it
+ * in a NotificationEntry. Thus, every notification has an associated NotificationEntry, even if
+ * that notification is never displayed to the user (for example, if it's filtered out for some
+ * reason).
+ *
+ * Entries store information about the current state of the notification. Essentially:
+ * anything that needs to persist or be modifiable even when the notification's views don't
+ * exist. Any other state should be stored on the views/view controllers themselves.
+ *
+ * At the moment, there are many things here that shouldn't be and vice-versa. Hopefully we can
+ * clean this up in the future.
+ */
+public final class NotificationEntry {
+    private static final long LAUNCH_COOLDOWN = 2000;
+    private static final long REMOTE_INPUT_COOLDOWN = 500;
+    private static final long INITIALIZATION_DELAY = 400;
+    private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
+    private static final int COLOR_INVALID = 1;
+    public final String key;
+    public StatusBarNotification notification;
+    public NotificationChannel channel;
+    public long lastAudiblyAlertedMs;
+    public boolean noisy;
+    public boolean ambient;
+    public int importance;
+    public StatusBarIconView icon;
+    public StatusBarIconView expandedIcon;
+    private boolean interruption;
+    public boolean autoRedacted; // whether the redacted notification was generated by us
+    public int targetSdk;
+    private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
+    public CharSequence remoteInputText;
+    public List<SnoozeCriterion> snoozeCriteria;
+    public int userSentiment = NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+    /** Smart Actions provided by the NotificationAssistantService. */
+    @NonNull
+    public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
+    public CharSequence[] smartReplies = new CharSequence[0];
+    @VisibleForTesting
+    public int suppressedVisualEffects;
+    public boolean suspended;
+
+    private NotificationEntry parent; // our parent (if we're in a group)
+    private ArrayList<NotificationEntry> children = new ArrayList<NotificationEntry>();
+    private ExpandableNotificationRow row; // the outer expanded view
+
+    private int mCachedContrastColor = COLOR_INVALID;
+    private int mCachedContrastColorIsFor = COLOR_INVALID;
+    private InflationTask mRunningTask = null;
+    private Throwable mDebugThrowable;
+    public CharSequence remoteInputTextWhenReset;
+    public long lastRemoteInputSent = NOT_LAUNCHED_YET;
+    public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
+    public CharSequence headsUpStatusBarText;
+    public CharSequence headsUpStatusBarTextPublic;
+
+    private long initializationTime = -1;
+
+    /**
+     * Whether or not this row represents a system notification. Note that if this is
+     * {@code null}, that means we were either unable to retrieve the info or have yet to
+     * retrieve the info.
+     */
+    public Boolean mIsSystemNotification;
+
+    /**
+     * Has the user sent a reply through this Notification.
+     */
+    private boolean hasSentReply;
+
+    /**
+     * Whether this notification should be displayed as a bubble.
+     */
+    private boolean mIsBubble;
+
+    /**
+     * Whether the user has dismissed this notification when it was in bubble form.
+     */
+    private boolean mUserDismissedBubble;
+
+    public NotificationEntry(StatusBarNotification n) {
+        this(n, null);
+    }
+
+    public NotificationEntry(
+            StatusBarNotification n,
+            @Nullable NotificationListenerService.Ranking ranking) {
+        this.key = n.getKey();
+        this.notification = n;
+        if (ranking != null) {
+            populateFromRanking(ranking);
+        }
+    }
+
+    public void populateFromRanking(@NonNull NotificationListenerService.Ranking ranking) {
+        channel = ranking.getChannel();
+        lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
+        importance = ranking.getImportance();
+        ambient = ranking.isAmbient();
+        snoozeCriteria = ranking.getSnoozeCriteria();
+        userSentiment = ranking.getUserSentiment();
+        systemGeneratedSmartActions = ranking.getSmartActions() == null
+                ? Collections.emptyList() : ranking.getSmartActions();
+        smartReplies = ranking.getSmartReplies() == null
+                ? new CharSequence[0]
+                : ranking.getSmartReplies().toArray(new CharSequence[0]);
+        suppressedVisualEffects = ranking.getSuppressedVisualEffects();
+        suspended = ranking.isSuspended();
+    }
+
+    public void setInterruption() {
+        interruption = true;
+    }
+
+    public boolean hasInterrupted() {
+        return interruption;
+    }
+
+    public void setIsBubble(boolean bubbleable) {
+        mIsBubble = bubbleable;
+    }
+
+    public boolean isBubble() {
+        return mIsBubble;
+    }
+
+    public void setBubbleDismissed(boolean userDismissed) {
+        mUserDismissedBubble = userDismissed;
+    }
+
+    public boolean isBubbleDismissed() {
+        return mUserDismissedBubble;
+    }
+
+    /**
+     * Resets the notification entry to be re-used.
+     */
+    public void reset() {
+        if (row != null) {
+            row.reset();
+        }
+    }
+
+    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<NotificationEntry> getChildren() {
+        if (children.size() <= 0) {
+            return null;
+        }
+
+        return children;
+    }
+
+    public void notifyFullScreenIntentLaunched() {
+        setInterruption();
+        lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
+    }
+
+    public boolean hasJustLaunchedFullScreenIntent() {
+        return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN;
+    }
+
+    public boolean hasJustSentRemoteInput() {
+        return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN;
+    }
+
+    public boolean hasFinishedInitialization() {
+        return initializationTime == -1
+                || SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
+    }
+
+    /**
+     * Create the icons for a notification
+     * @param context the context to create the icons with
+     * @param sbn the notification
+     * @throws InflationException Exception if required icons are not valid or specified
+     */
+    public void createIcons(Context context, StatusBarNotification sbn)
+            throws InflationException {
+        Notification n = sbn.getNotification();
+        final Icon smallIcon = n.getSmallIcon();
+        if (smallIcon == null) {
+            throw new InflationException("No small icon in notification from "
+                    + sbn.getPackageName());
+        }
+
+        // Construct the icon.
+        icon = new StatusBarIconView(context,
+                sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
+        icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+
+        // Construct the expanded icon.
+        expandedIcon = new StatusBarIconView(context,
+                sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
+        expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+        final StatusBarIcon ic = new StatusBarIcon(
+                sbn.getUser(),
+                sbn.getPackageName(),
+                smallIcon,
+                n.iconLevel,
+                n.number,
+                StatusBarIconView.contentDescForNotification(context, n));
+        if (!icon.set(ic) || !expandedIcon.set(ic)) {
+            icon = null;
+            expandedIcon = null;
+            throw new InflationException("Couldn't create icon: " + ic);
+        }
+        expandedIcon.setVisibility(View.INVISIBLE);
+        expandedIcon.setOnVisibilityChangedListener(
+                newVisibility -> {
+                    if (row != null) {
+                        row.setIconsVisible(newVisibility != View.VISIBLE);
+                    }
+                });
+    }
+
+    public void setIconTag(int key, Object tag) {
+        if (icon != null) {
+            icon.setTag(key, tag);
+            expandedIcon.setTag(key, tag);
+        }
+    }
+
+    /**
+     * Update the notification icons.
+     *
+     * @param context the context to create the icons with.
+     * @param sbn the notification to read the icon from.
+     * @throws InflationException Exception if required icons are not valid or specified
+     */
+    public void updateIcons(Context context, StatusBarNotification sbn)
+            throws InflationException {
+        if (icon != null) {
+            // Update the icon
+            Notification n = sbn.getNotification();
+            final StatusBarIcon ic = new StatusBarIcon(
+                    notification.getUser(),
+                    notification.getPackageName(),
+                    n.getSmallIcon(),
+                    n.iconLevel,
+                    n.number,
+                    StatusBarIconView.contentDescForNotification(context, n));
+            icon.setNotification(sbn);
+            expandedIcon.setNotification(sbn);
+            if (!icon.set(ic) || !expandedIcon.set(ic)) {
+                throw new InflationException("Couldn't update icon: " + ic);
+            }
+        }
+    }
+
+    public int getContrastedColor(Context context, boolean isLowPriority,
+            int backgroundColor) {
+        int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
+                notification.getNotification().color;
+        if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
+            return mCachedContrastColor;
+        }
+        final int contrasted = ContrastColorUtil.resolveContrastColor(context, rawColor,
+                backgroundColor);
+        mCachedContrastColorIsFor = rawColor;
+        mCachedContrastColor = contrasted;
+        return mCachedContrastColor;
+    }
+
+    /**
+     * Abort all existing inflation tasks
+     */
+    public void abortTask() {
+        if (mRunningTask != null) {
+            mRunningTask.abort();
+            mRunningTask = null;
+        }
+    }
+
+    public void setInflationTask(InflationTask abortableTask) {
+        // abort any existing inflation
+        InflationTask existing = mRunningTask;
+        abortTask();
+        mRunningTask = abortableTask;
+        if (existing != null && mRunningTask != null) {
+            mRunningTask.supersedeTask(existing);
+        }
+    }
+
+    public void onInflationTaskFinished() {
+        mRunningTask = null;
+    }
+
+    @VisibleForTesting
+    public InflationTask getRunningTask() {
+        return mRunningTask;
+    }
+
+    /**
+     * Set a throwable that is used for debugging
+     *
+     * @param debugThrowable the throwable to save
+     */
+    public void setDebugThrowable(Throwable debugThrowable) {
+        mDebugThrowable = debugThrowable;
+    }
+
+    public Throwable getDebugThrowable() {
+        return mDebugThrowable;
+    }
+
+    public void onRemoteInputInserted() {
+        lastRemoteInputSent = NOT_LAUNCHED_YET;
+        remoteInputTextWhenReset = null;
+    }
+
+    public void setHasSentReply() {
+        hasSentReply = true;
+    }
+
+    public boolean isLastMessageFromReply() {
+        if (!hasSentReply) {
+            return false;
+        }
+        Bundle extras = notification.getNotification().extras;
+        CharSequence[] replyTexts = extras.getCharSequenceArray(
+                Notification.EXTRA_REMOTE_INPUT_HISTORY);
+        if (!ArrayUtils.isEmpty(replyTexts)) {
+            return true;
+        }
+        Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+        if (messages != null && messages.length > 0) {
+            Parcelable message = messages[messages.length - 1];
+            if (message instanceof Bundle) {
+                Notification.MessagingStyle.Message lastMessage =
+                        Notification.MessagingStyle.Message.getMessageFromBundle(
+                                (Bundle) message);
+                if (lastMessage != null) {
+                    Person senderPerson = lastMessage.getSenderPerson();
+                    if (senderPerson == null) {
+                        return true;
+                    }
+                    Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
+                    return Objects.equals(user, senderPerson);
+                }
+            }
+        }
+        return false;
+    }
+
+    public void setInitializationTime(long time) {
+        if (initializationTime == -1) {
+            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(@NotificationInflater.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() != 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++) {
+                NotificationEntry child =  children.get(i);
+                if (!child.isClearable()) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public boolean canViewBeDismissed() {
+        if (row == null) return true;
+        return row.canViewBeDismissed();
+    }
+
+    @VisibleForTesting
+    boolean isExemptFromDndVisualSuppression() {
+        if (isNotificationBlockedByPolicy(notification.getNotification())) {
+            return false;
+        }
+
+        if ((notification.getNotification().flags
+                & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+            return true;
+        }
+        if (notification.getNotification().isMediaNotification()) {
+            return true;
+        }
+        if (mIsSystemNotification != null && mIsSystemNotification) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean shouldSuppressVisualEffect(int effect) {
+        if (isExemptFromDndVisualSuppression()) {
+            return false;
+        }
+        return (suppressedVisualEffects & effect) != 0;
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressFullScreenIntent() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_PEEK}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressPeek() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_STATUS_BAR}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressStatusBar() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_AMBIENT}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressAmbient() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
+    }
+
+    /**
+     * Returns whether {@link Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
+     * is set for this entry.
+     */
+    public boolean shouldSuppressNotificationList() {
+        return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+    }
+
+    /**
+     * Categories that are explicitly called out on DND settings screens are always blocked, if
+     * DND has flagged them, even if they are foreground or system notifications that might
+     * otherwise visually bypass DND.
+     */
+    private static boolean isNotificationBlockedByPolicy(Notification n) {
+        return isCategory(CATEGORY_CALL, n)
+                || isCategory(CATEGORY_MESSAGE, n)
+                || isCategory(CATEGORY_ALARM, n)
+                || isCategory(CATEGORY_EVENT, n)
+                || isCategory(CATEGORY_REMINDER, n);
+    }
+
+    private static boolean isCategory(String category, Notification n) {
+        return Objects.equals(n.category, category);
+    }
+}
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 610d300..3eec38e 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
@@ -15,7 +15,6 @@
  */
 package com.android.systemui.statusbar.notification.logging;
 
-import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -34,9 +33,9 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -116,11 +115,11 @@
             //    notifications.
             // 3. Report newly visible and no-longer visible notifications.
             // 4. Keep currently visible notifications for next report.
-            ArrayList<NotificationData.Entry> activeNotifications = mEntryManager
+            ArrayList<NotificationEntry> activeNotifications = mEntryManager
                     .getNotificationData().getActiveNotifications();
             int N = activeNotifications.size();
             for (int i = 0; i < N; i++) {
-                NotificationData.Entry entry = activeNotifications.get(i);
+                NotificationEntry entry = activeNotifications.get(i);
                 String key = entry.notification.getKey();
                 boolean isVisible = mListContainer.isInVisibleLocation(entry);
                 NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
@@ -168,14 +167,11 @@
         entryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
             public void onEntryRemoved(
-                    @Nullable NotificationData.Entry entry,
-                    String key,
-                    StatusBarNotification old,
+                    NotificationEntry entry,
                     NotificationVisibility visibility,
-                    boolean lifetimeExtended,
                     boolean removedByUser) {
-                if (removedByUser && visibility != null && entry != null) {
-                    logNotificationClear(key, entry.notification, visibility);
+                if (removedByUser && visibility != null) {
+                    logNotificationClear(entry.key, entry.notification, visibility);
                 }
             }
 
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 a58c7cd..9e0dd84 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
@@ -86,10 +86,10 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
@@ -199,7 +199,7 @@
     private ExpansionLogger mLogger;
     private String mLoggingKey;
     private NotificationGuts mGuts;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private StatusBarNotification mStatusBarNotification;
     private String mAppName;
 
@@ -451,7 +451,7 @@
      *
      * @param entry the entry this row is tied to
      */
-    public void setEntry(@NonNull NotificationData.Entry entry) {
+    public void setEntry(@NonNull NotificationEntry entry) {
         mEntry = entry;
         mStatusBarNotification = entry.notification;
         cacheIsSystemNotification();
@@ -685,7 +685,7 @@
         return mStatusBarNotification;
     }
 
-    public NotificationData.Entry getEntry() {
+    public NotificationEntry getEntry() {
         return mEntry;
     }
 
@@ -1422,7 +1422,7 @@
 
     public void performDismiss(boolean fromAccessibility) {
         if (isOnlyChildInGroup()) {
-            NotificationData.Entry groupSummary =
+            NotificationEntry 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
@@ -2538,7 +2538,7 @@
     /**
      * @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 NotificationData.Entry#isClearable()}.
+     *         see {@link NotificationEntry#isClearable()}.
      */
     public boolean canViewBeDismissed() {
         return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
@@ -2969,7 +2969,7 @@
     }
 
     public interface OnExpandClickListener {
-        void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
+        void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded);
     }
 
     @Override
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 02a310c..bf30cf9 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
@@ -46,8 +46,8 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -1231,7 +1231,7 @@
         updateAllSingleLineViews();
     }
 
-    public void onNotificationUpdated(NotificationData.Entry entry) {
+    public void onNotificationUpdated(NotificationEntry entry) {
         mStatusBarNotification = entry.notification;
         mOnContentViewInactiveListeners.clear();
         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
@@ -1292,7 +1292,7 @@
         }
     }
 
-    private void applyRemoteInputAndSmartReply(final NotificationData.Entry entry) {
+    private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
         if (mRemoteInputController == null) {
             return;
         }
@@ -1313,7 +1313,7 @@
     @VisibleForTesting
     static SmartRepliesAndActions chooseSmartRepliesAndActions(
             SmartReplyConstants smartReplyConstants,
-            final NotificationData.Entry entry) {
+            final NotificationEntry entry) {
         boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled()
                 && (!smartReplyConstants.requiresTargetingP()
                 || entry.targetSdk >= Build.VERSION_CODES.P));
@@ -1370,7 +1370,7 @@
                 smartReplies, smartActions, freeformRemoteInputActionPair != null);
     }
 
-    private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
+    private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) {
         View bigContentView = mExpandedChild;
         if (bigContentView != null) {
             mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasFreeformRemoteInput,
@@ -1402,7 +1402,7 @@
         mCachedHeadsUpRemoteInput = null;
     }
 
-    private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
+    private RemoteInputView applyRemoteInput(View view, NotificationEntry entry,
             boolean hasRemoteInput, PendingIntent existingPendingIntent,
             RemoteInputView cachedView, NotificationViewWrapper wrapper) {
         View actionContainerCandidate = view.findViewById(
@@ -1470,7 +1470,7 @@
     }
 
     private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions,
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         if (mExpandedChild != null) {
             mExpandedSmartReplyView =
                     applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
@@ -1496,7 +1496,7 @@
     }
 
     private SmartReplyView applySmartReplyView(View view,
-            SmartRepliesAndActions smartRepliesAndActions, NotificationData.Entry entry) {
+            SmartRepliesAndActions smartRepliesAndActions, NotificationEntry entry) {
         View smartReplyContainerCandidate = view.findViewById(
                 com.android.internal.R.id.smart_reply_container);
         if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
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 ac4e583..bd1dfb1 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
@@ -48,7 +48,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -116,7 +116,7 @@
         mNotificationActivityStarter = notificationActivityStarter;
     }
 
-    public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
+    public void onDensityOrFontScaleChanged(NotificationEntry entry) {
         setExposedGuts(entry.getGuts());
         bindGuts(entry.getRow());
     }
@@ -429,7 +429,7 @@
     }
 
     @Override
-    public boolean shouldExtendLifetime(NotificationData.Entry entry) {
+    public boolean shouldExtendLifetime(NotificationEntry entry) {
         return entry != null
                 &&(mNotificationGutsExposed != null
                     && entry.getGuts() != null
@@ -438,7 +438,7 @@
     }
 
     @Override
-    public void setShouldManageLifetime(NotificationData.Entry entry, boolean shouldExtend) {
+    public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
         if (shouldExtend) {
             mKeyToRemoveOnGutsClosed = entry.key;
             if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index 9908049..42ebfce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -37,7 +37,7 @@
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.Assert;
@@ -614,7 +614,7 @@
             @Nullable InflationCallback endListener, ExpandableNotificationRow row,
             boolean redactAmbient) {
         Assert.isMainThread();
-        NotificationData.Entry entry = row.getEntry();
+        NotificationEntry entry = row.getEntry();
         NotificationContentView privateLayout = row.getPrivateLayout();
         NotificationContentView publicLayout = row.getPublicLayout();
         if (runningInflations.isEmpty()) {
@@ -724,7 +724,7 @@
          * @param entry the entry with the content views set
          * @param inflatedFlags the flags associated with the content views that were inflated
          */
-        void onAsyncInflationFinished(NotificationData.Entry entry,
+        void onAsyncInflationFinished(NotificationEntry entry,
                 @InflationFlag int inflatedFlags);
 
         /**
@@ -782,7 +782,7 @@
             mRedactAmbient = redactAmbient;
             mRemoteViewClickHandler = remoteViewClickHandler;
             mCallback = callback;
-            NotificationData.Entry entry = row.getEntry();
+            NotificationEntry entry = row.getEntry();
             entry.setInflationTask(this);
         }
 
@@ -857,7 +857,7 @@
         }
 
         @Override
-        public void onAsyncInflationFinished(NotificationData.Entry entry,
+        public void onAsyncInflationFinished(NotificationEntry entry,
                 @InflationFlag int inflatedFlags) {
             mRow.getEntry().onInflationTaskFinished();
             mRow.onNotificationUpdated();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index 1741a0b..0160c547 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -25,7 +25,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * An inflater task that asynchronously inflates a ExpandableNotificationRow
@@ -36,14 +36,14 @@
     private static final boolean TRACE_ORIGIN = true;
 
     private RowInflationFinishedListener mListener;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private boolean mCancelled;
     private Throwable mInflateOrigin;
 
     /**
      * Inflates a new notificationView. This should not be called twice on this object
      */
-    public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
+    public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
             RowInflationFinishedListener listener) {
         if (TRACE_ORIGIN) {
             mInflateOrigin = new Throwable("inflate requested here");
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 670908f..cbec37e 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
@@ -25,7 +25,7 @@
 import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -358,7 +358,7 @@
         mPulsing = hasPulsing;
     }
 
-    public boolean isPulsing(NotificationData.Entry entry) {
+    public boolean isPulsing(NotificationEntry entry) {
         if (!mPulsing || mAmbientPulseManager == null) {
             return false;
         }
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 f0a2653..f771be0 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
@@ -16,15 +16,14 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
-        .ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -113,19 +112,12 @@
     void setMaxDisplayedNotifications(int maxNotifications);
 
     /**
-     * Handle snapping a non-dismissable row back if the user tried to dismiss it.
-     *
-     * @param entry the entry whose row needs to snap back
-     */
-    void snapViewIfNeeded(NotificationData.Entry entry);
-
-    /**
      * Get the view parent for a notification entry. For example, NotificationStackScrollLayout.
      *
      * @param entry entry to get the view parent for
      * @return the view parent for entry
      */
-    ViewGroup getViewParentForNotification(NotificationData.Entry entry);
+    ViewGroup getViewParentForNotification(NotificationEntry entry);
 
     /**
      * Resets the currently exposed menu view.
@@ -148,7 +140,7 @@
      *
      * @param entry the entry whose view's view state needs to be cleaned up (say that 5 times fast)
      */
-    void cleanUpViewStateForEntry(NotificationData.Entry entry);
+    void cleanUpViewStateForEntry(NotificationEntry 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 4f0831f1..9418601 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
@@ -18,7 +18,7 @@
 
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
 
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -49,12 +49,12 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry headsUp) {
+    public void onHeadsUpPinned(NotificationEntry headsUp) {
         updateView(headsUp.getRow(), false /* animate */);
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry headsUp) {
+    public void onHeadsUpUnPinned(NotificationEntry headsUp) {
         updateView(headsUp.getRow(), true /* animate */);
     }
 
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 f982ecf..e4b00dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,9 +16,11 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
 import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -94,12 +96,13 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.FakeShadowView;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.ShadeViewRefactor;
 import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -136,6 +139,9 @@
 import java.util.List;
 import java.util.function.BiConsumer;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
 /**
  * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
  */
@@ -165,6 +171,7 @@
     private final Paint mBackgroundPaint = new Paint();
     private final boolean mShouldDrawNotificationBackground;
     private boolean mLowPriorityBeforeSpeedBump;
+    private final boolean mAllowLongPress;
 
     private float mExpandedHeight;
     private int mOwnScrollY;
@@ -452,27 +459,16 @@
     private final NotificationGutsManager
             mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
 
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public NotificationStackScrollLayout(Context context) {
-        this(context, null);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
+    @Inject
+    public NotificationStackScrollLayout(
+            @Named(VIEW_CONTEXT) Context context,
+            AttributeSet attrs,
+            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress) {
+        super(context, attrs, 0, 0);
         Resources res = getResources();
 
+        mAllowLongPress = allowLongPress;
+
         for (int i = 0; i < NUM_SECTIONS; i++) {
             mSections[i] = new NotificationSection(this);
         }
@@ -522,6 +518,17 @@
                 mLowPriorityBeforeSpeedBump = "1".equals(newValue);
             }
         }, LOW_PRIORITY);
+
+        mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+            @Override
+            public void onEntryUpdated(NotificationEntry entry) {
+                if (!entry.notification.isClearable()) {
+                    // The user may have performed a dismiss action on the notification, since it's
+                    // not clearable we should snap it back.
+                    snapViewIfNeeded(entry);
+                }
+            }
+        });
     }
 
     @Override
@@ -532,7 +539,9 @@
         inflateEmptyShadeView();
         inflateFooterView();
         mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
-        setLongPressListener(mEntryManager.getNotificationLongClicker());
+        if (mAllowLongPress) {
+            setLongPressListener(mGutsManager::openGuts);
+        }
     }
 
     @Override
@@ -593,14 +602,14 @@
   @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
   public RemoteInputController.Delegate createDelegate() {
         return new RemoteInputController.Delegate() {
-            public void setRemoteInputActive(NotificationData.Entry entry,
+            public void setRemoteInputActive(NotificationEntry entry,
                     boolean remoteInputActive) {
                 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
                 entry.notifyHeightChanged(true /* needsAnimation */);
                 updateFooter();
             }
 
-            public void lockScrollTo(NotificationData.Entry entry) {
+            public void lockScrollTo(NotificationEntry entry) {
                 NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
             }
 
@@ -913,7 +922,7 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
-    public boolean isInVisibleLocation(NotificationData.Entry entry) {
+    public boolean isInVisibleLocation(NotificationEntry entry) {
         ExpandableNotificationRow row = entry.getRow();
         ExpandableViewState childViewState = row.getViewState();
 
@@ -1227,13 +1236,13 @@
      */
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int getTopHeadsUpPinnedHeight() {
-        NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
+        NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
         if (topEntry == null) {
             return 0;
         }
         ExpandableNotificationRow row = topEntry.getRow();
         if (row.isChildInGroup()) {
-            final NotificationData.Entry groupSummary
+            final NotificationEntry groupSummary
                     = mGroupManager.getGroupSummary(row.getStatusBarNotification());
             if (groupSummary != null) {
                 row = groupSummary.getRow();
@@ -1408,7 +1417,7 @@
                     && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                 if (slidingChild instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
-                    NotificationData.Entry entry = row.getEntry();
+                    NotificationEntry entry = row.getEntry();
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
                             && mHeadsUpManager.getTopEntry().getRow() != row
                             && mGroupManager.getGroupSummary(
@@ -1541,9 +1550,8 @@
                 true /* isDismissAll */);
     }
 
-    @Override
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void snapViewIfNeeded(NotificationData.Entry entry) {
+    private void snapViewIfNeeded(NotificationEntry entry) {
         ExpandableNotificationRow child = entry.getRow();
         boolean animate = mIsExpanded || isPinnedHeadsUp(child);
         // If the child is showing the notification menu snap to that
@@ -1553,7 +1561,7 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.ADAPTER)
-    public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
+    public ViewGroup getViewParentForNotification(NotificationEntry entry) {
         return this;
     }
 
@@ -2056,7 +2064,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private boolean isPulsing(NotificationData.Entry entry) {
+    private boolean isPulsing(NotificationEntry entry) {
         return mAmbientState.isPulsing(entry);
     }
 
@@ -2561,7 +2569,7 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     @Override
-    public void cleanUpViewStateForEntry(NotificationData.Entry entry) {
+    public void cleanUpViewStateForEntry(NotificationEntry entry) {
         View child = entry.getRow();
         if (child == mSwipeHelper.getTranslatingParentView()) {
             mSwipeHelper.clearTranslatingParentView();
@@ -2689,7 +2697,7 @@
     private boolean isChildInInvisibleGroup(View child) {
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            NotificationData.Entry groupSummary =
+            NotificationEntry groupSummary =
                     mGroupManager.getGroupSummary(row.getStatusBarNotification());
             if (groupSummary != null && groupSummary.getRow() != row) {
                 return row.getVisibility() == View.INVISIBLE;
@@ -4708,7 +4716,7 @@
         mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
     }
 
-    public void generateHeadsUpAnimation(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
         ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
         generateHeadsUpAnimation(row, isHeadsUp);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index f1d9549..975aee5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -189,10 +189,13 @@
         boolean isFastNonDismissGesture =
                 gestureFastEnough && !gestureTowardsMenu && !isDismissGesture;
         boolean isMenuRevealingGestureAwayFromMenu = slowSwipedFarEnough || isFastNonDismissGesture;
-        if (isNonDismissGestureTowardsMenu
-                || (!isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu)) {
+        int menuSnapTarget = menuRow.getMenuSnapTarget();
+        boolean isNonFalseMenuRevealingGesture =
+                !isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu;
+        if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture)
+                && menuSnapTarget != 0) {
             // Menu has not been snapped to previously and this is menu revealing gesture
-            snapOpen(animView, menuRow.getMenuSnapTarget(), velocity);
+            snapOpen(animView, menuSnapTarget, velocity);
             menuRow.onSnapOpen();
         } else if (isDismissGesture(ev) && !gestureTowardsMenu) {
             dismiss(animView, velocity);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
new file mode 100644
index 0000000..b9425d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IWindowManager;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/** A controller to control all auto-hide things. */
+public class AutoHideController implements CommandQueue.Callbacks {
+    private static final String TAG = "AutoHideController";
+
+    private final IWindowManager mWindowManagerService;
+
+    private final Handler mHandler;
+    private final NotificationRemoteInputManager mRemoteInputManager;
+    private final CommandQueue mCommandQueue;
+    private StatusBar mStatusBar;
+    private NavigationBarFragment mNavigationBar;
+
+    private int mDisplayId;
+    private int mSystemUiVisibility;
+    // last value sent to window manager
+    private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
+
+    private boolean mAutoHideSuspended;
+
+    private static final long AUTOHIDE_TIMEOUT_MS = 2250;
+
+    private final Runnable mAutoHide = () -> {
+        int requested = mSystemUiVisibility & ~getTransientMask();
+        if (mSystemUiVisibility != requested) {
+            notifySystemUiVisibilityChanged(requested);
+        }
+    };
+
+    @Inject
+    public AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
+        mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
+        mCommandQueue.addCallback(this);
+        mHandler = handler;
+        mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
+        mWindowManagerService = Dependency.get(IWindowManager.class);
+
+        mDisplayId = context.getDisplayId();
+    }
+
+    void setStatusBar(StatusBar statusBar) {
+        mStatusBar = statusBar;
+    }
+
+    void setNavigationBar(NavigationBarFragment navigationBar) {
+        mNavigationBar = navigationBar;
+    }
+
+    @Override
+    public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+        if (displayId != mDisplayId) {
+            return;
+        }
+        int oldVal = mSystemUiVisibility;
+        int newVal = (oldVal & ~mask) | (vis & mask);
+        int diff = newVal ^ oldVal;
+
+        if (diff != 0) {
+            mSystemUiVisibility = newVal;
+
+            // ready to unhide
+            if (hasStatusBar() && (vis & View.STATUS_BAR_UNHIDE) != 0) {
+                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
+            }
+
+            if (hasNavigationBar() && (vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
+                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
+            }
+
+            // Re-send setSystemUiVisibility to update un-hide status.
+            if (mSystemUiVisibility != newVal) {
+                mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility,
+                        fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds,
+                        dockedStackBounds);
+            }
+
+            notifySystemUiVisibilityChanged(mSystemUiVisibility);
+        }
+    }
+
+    private void notifySystemUiVisibilityChanged(int vis) {
+        try {
+            if (mLastDispatchedSystemUiVisibility != vis) {
+                mWindowManagerService.statusBarVisibilityChanged(mDisplayId, vis);
+                mLastDispatchedSystemUiVisibility = vis;
+            }
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Cannot get WindowManager");
+        }
+    }
+
+    void resumeSuspendedAutoHide() {
+        if (mAutoHideSuspended) {
+            scheduleAutoHide();
+            Runnable checkBarModesRunnable = getCheckBarModesRunnable();
+            if (checkBarModesRunnable != null) {
+                mHandler.postDelayed(checkBarModesRunnable, 500); // longer than home -> launcher
+            }
+        }
+    }
+
+    void suspendAutoHide() {
+        mHandler.removeCallbacks(mAutoHide);
+        Runnable checkBarModesRunnable = getCheckBarModesRunnable();
+        if (checkBarModesRunnable != null) {
+            mHandler.removeCallbacks(checkBarModesRunnable);
+        }
+        mAutoHideSuspended = (mSystemUiVisibility & getTransientMask()) != 0;
+    }
+
+    void touchAutoHide() {
+        // update transient bar auto hide
+        if ((hasStatusBar() && mStatusBar.getStatusBarMode() == MODE_SEMI_TRANSPARENT)
+                || hasNavigationBar() && mNavigationBar.isSemiTransparent()) {
+            scheduleAutoHide();
+        } else {
+            cancelAutoHide();
+        }
+    }
+
+    private Runnable getCheckBarModesRunnable() {
+        if (hasStatusBar()) {
+            return () -> mStatusBar.checkBarModes();
+        } else if (hasNavigationBar()) {
+            return () -> mNavigationBar.checkNavBarModes();
+        } else {
+            return null;
+        }
+    }
+
+    private void cancelAutoHide() {
+        mAutoHideSuspended = false;
+        mHandler.removeCallbacks(mAutoHide);
+    }
+
+    private void scheduleAutoHide() {
+        cancelAutoHide();
+        mHandler.postDelayed(mAutoHide, AUTOHIDE_TIMEOUT_MS);
+    }
+
+    void checkUserAutoHide(MotionEvent event) {
+        boolean shouldAutoHide =
+                (mSystemUiVisibility & getTransientMask()) != 0  // a transient bar is revealed.
+                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar.
+                && event.getX() == 0 && event.getY() == 0;
+        if (hasStatusBar()) {
+            // a touch outside both bars
+            shouldAutoHide &= !mRemoteInputManager.getController().isRemoteInputActive();
+        }
+        if (shouldAutoHide) {
+            userAutoHide();
+        }
+    }
+
+    private void userAutoHide() {
+        cancelAutoHide();
+        mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear
+    }
+
+    private int getTransientMask() {
+        int mask = 0;
+        if (hasStatusBar()) {
+            mask |= View.STATUS_BAR_TRANSIENT;
+        }
+        if (hasNavigationBar()) {
+            mask |= View.NAVIGATION_BAR_TRANSIENT;
+        }
+        return mask;
+    }
+
+    private boolean hasNavigationBar() {
+        return mNavigationBar != null;
+    }
+
+    private boolean hasStatusBar() {
+        return mStatusBar != null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 1d7e899..fac4dbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -30,6 +30,9 @@
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
 /**
  * Manages which tiles should be automatically added to QS.
  */
@@ -44,24 +47,31 @@
     private final QSTileHost mHost;
     private final Handler mHandler;
     private final AutoAddTracker mAutoTracker;
+    private final HotspotController mHotspotController;
+    private final DataSaverController mDataSaverController;
+    private final ManagedProfileController mManagedProfileController;
+    private final ColorDisplayController mColorDisplayController;
 
-    public AutoTileManager(Context context, QSTileHost host) {
-        this(context, new AutoAddTracker(context), host,
-            new Handler(Dependency.get(Dependency.BG_LOOPER)));
-    }
-
-    @VisibleForTesting
-    AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
-            Handler handler) {
+    @Inject
+    public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
+            @Named(Dependency.BG_HANDLER_NAME) Handler handler,
+            HotspotController hotspotController,
+            DataSaverController dataSaverController,
+            ManagedProfileController managedProfileController,
+            ColorDisplayController colorDisplayController) {
         mAutoTracker = autoAddTracker;
         mContext = context;
         mHost = host;
         mHandler = handler;
+        mHotspotController = hotspotController;
+        mDataSaverController = dataSaverController;
+        mManagedProfileController = managedProfileController;
+        mColorDisplayController = colorDisplayController;
         if (!mAutoTracker.isAdded(HOTSPOT)) {
-            Dependency.get(HotspotController.class).addCallback(mHotspotCallback);
+            hotspotController.addCallback(mHotspotCallback);
         }
         if (!mAutoTracker.isAdded(SAVER)) {
-            Dependency.get(DataSaverController.class).addCallback(mDataSaverListener);
+            dataSaverController.addCallback(mDataSaverListener);
         }
         if (!mAutoTracker.isAdded(INVERSION)) {
             mColorsSetting = new SecureSetting(mContext, mHandler,
@@ -79,11 +89,11 @@
             mColorsSetting.setListening(true);
         }
         if (!mAutoTracker.isAdded(WORK)) {
-            Dependency.get(ManagedProfileController.class).addCallback(mProfileCallback);
+            managedProfileController.addCallback(mProfileCallback);
         }
         if (!mAutoTracker.isAdded(NIGHT)
                 && ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            Dependency.get(ColorDisplayController.class).setListener(mColorDisplayCallback);
+            colorDisplayController.setListener(mColorDisplayCallback);
         }
     }
 
@@ -92,11 +102,11 @@
             mColorsSetting.setListening(false);
         }
         mAutoTracker.destroy();
-        Dependency.get(HotspotController.class).removeCallback(mHotspotCallback);
-        Dependency.get(DataSaverController.class).removeCallback(mDataSaverListener);
-        Dependency.get(ManagedProfileController.class).removeCallback(mProfileCallback);
+        mHotspotController.removeCallback(mHotspotCallback);
+        mDataSaverController.removeCallback(mDataSaverListener);
+        mManagedProfileController.removeCallback(mProfileCallback);
         if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            Dependency.get(ColorDisplayController.class).setListener(null);
+            mColorDisplayController.setListener(null);
         }
     }
 
@@ -109,7 +119,7 @@
                 @Override
                 public void onManagedProfileChanged() {
                     if (mAutoTracker.isAdded(WORK)) return;
-                    if (Dependency.get(ManagedProfileController.class).hasActiveProfile()) {
+                    if (mManagedProfileController.hasActiveProfile()) {
                         mHost.addTile(WORK);
                         mAutoTracker.setTileAdded(WORK);
                     }
@@ -129,8 +139,7 @@
             if (isDataSaving) {
                 mHost.addTile(SAVER);
                 mAutoTracker.setTileAdded(SAVER);
-                mHandler.post(() -> Dependency.get(DataSaverController.class).removeCallback(
-                        mDataSaverListener));
+                mHandler.post(() -> mDataSaverController.removeCallback(mDataSaverListener));
             }
         }
     };
@@ -142,8 +151,7 @@
             if (enabled) {
                 mHost.addTile(HOTSPOT);
                 mAutoTracker.setTileAdded(HOTSPOT);
-                mHandler.post(() -> Dependency.get(HotspotController.class)
-                        .removeCallback(mHotspotCallback));
+                mHandler.post(() -> mHotspotController.removeCallback(mHotspotCallback));
             }
         }
     };
@@ -170,8 +178,7 @@
             if (mAutoTracker.isAdded(NIGHT)) return;
             mHost.addTile(NIGHT);
             mAutoTracker.setTileAdded(NIGHT);
-            mHandler.post(() -> Dependency.get(ColorDisplayController.class)
-                    .setListener(null));
+            mHandler.post(() -> mColorDisplayController.setListener(null));
         }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 24570ae..f907b65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -70,7 +70,7 @@
     private SignalCallback mSignalCallback = new SignalCallback() {
         @Override
         public void setIsAirplaneMode(NetworkController.IconState icon) {
-            mCommandQueue.recomputeDisableFlags(true /* animate */);
+            mCommandQueue.recomputeDisableFlags(getContext().getDisplayId(), true /* animate */);
         }
     };
 
@@ -155,7 +155,10 @@
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != getContext().getDisplayId()) {
+            return;
+        }
         state1 = adjustDisableFlags(state1);
         final int old1 = mDisabled1;
         final int diff1 = state1 ^ old1;
@@ -362,6 +365,6 @@
 
     @Override
     public void onDozingChanged(boolean isDozing) {
-        disable(mDisabled1, mDisabled1, false /* animate */);
+        disable(getContext().getDisplayId(), mDisabled1, mDisabled1, false /* animate */);
     }
 }
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 d1e488a..876b902 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -28,7 +28,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -143,7 +143,7 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
         updateTopEntry();
         updateHeader(entry);
     }
@@ -206,11 +206,11 @@
     }
 
     private void updateTopEntry() {
-        NotificationData.Entry newEntry = null;
+        NotificationEntry newEntry = null;
         if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) {
             newEntry = mHeadsUpManager.getTopEntry();
         }
-        NotificationData.Entry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
+        NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
         mHeadsUpStatusBarView.setEntry(newEntry);
         if (newEntry != previousEntry) {
             boolean animateIsolation = false;
@@ -298,7 +298,7 @@
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry entry) {
         updateTopEntry();
         updateHeader(entry);
     }
@@ -338,7 +338,7 @@
         });
     }
 
-    public void updateHeader(NotificationData.Entry entry) {
+    public void updateHeader(NotificationEntry entry) {
         ExpandableNotificationRow row = entry.getRow();
         float headerVisibleAmount = 1.0f;
         if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
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 f4cfd41..0fada66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -41,8 +41,8 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -72,8 +72,8 @@
     private int mDisplayCutoutTouchableRegionSize;
     private boolean mTrackingHeadsUp;
     private HashSet<String> mSwipedOutKeys = new HashSet<>();
-    private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
-    private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
+    private HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
+    private ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
             = new ArraySet<>();
     private boolean mIsExpanded;
     private int[] mTmpTwoArray = new int[2];
@@ -187,7 +187,7 @@
             releaseAllImmediately();
             mReleaseOnExpandFinish = false;
         } else {
-            for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+            for (NotificationEntry entry : mEntriesToRemoveAfterExpand) {
                 if (isAlerting(entry.key)) {
                     // Maybe the heads-up was removed already
                     removeAlertEntry(entry.key);
@@ -252,7 +252,7 @@
      * @param remoteInputActive True to notify active, False to notify inactive.
      */
     public void setRemoteInputActive(
-            @NonNull NotificationData.Entry entry, boolean remoteInputActive) {
+            @NonNull NotificationEntry entry, boolean remoteInputActive) {
         HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key);
         if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
             headsUpEntry.remoteInputActive = remoteInputActive;
@@ -268,7 +268,7 @@
      * Sets whether an entry's menu row is exposed and therefore it should stick in the heads up
      * area if it's pinned until it's hidden again.
      */
-    public void setMenuShown(@NonNull NotificationData.Entry entry, boolean menuShown) {
+    public void setMenuShown(@NonNull NotificationEntry entry, boolean menuShown) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
         if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) {
             ((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown);
@@ -315,9 +315,9 @@
             return;
         }
         if (hasPinnedHeadsUp()) {
-            NotificationData.Entry topEntry = getTopEntry();
+            NotificationEntry topEntry = getTopEntry();
             if (topEntry.isChildInGroup()) {
-                final NotificationData.Entry groupSummary
+                final NotificationEntry groupSummary
                         = mGroupManager.getGroupSummary(topEntry.notification);
                 if (groupSummary != null) {
                     topEntry = groupSummary;
@@ -374,7 +374,7 @@
     @Override
     public void onReorderingAllowed() {
         mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
-        for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
+        for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
             if (isAlerting(entry.key)) {
                 // Maybe the heads-up was removed already
                 removeAlertEntry(entry.key);
@@ -399,7 +399,7 @@
     }
 
     @Override
-    protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
+    protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) {
           return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
                   || super.shouldHeadsUpBecomePinned(entry);
     }
@@ -488,7 +488,7 @@
             return super.isSticky() || mMenuShownPinned;
         }
 
-        public void setEntry(@NonNull final NotificationData.Entry entry) {
+        public void setEntry(@NonNull final NotificationEntry entry) {
            Runnable removeHeadsUpRunnable = () -> {
                 if (!mVisualStabilityManager.isReorderingAllowed()) {
                     mEntriesToRemoveWhenReorderingAllowed.add(entry);
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 9c1c71a..dd200da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -21,7 +21,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.systemui.Gefingerpoken;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 
@@ -83,7 +83,7 @@
                 } else if (child == null && !mCallback.isExpanded()) {
                     // We might touch above the visible heads up child, but then we still would
                     // like to capture it.
-                    NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
+                    NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
                     if (topEntry != null && topEntry.isRowPinned()) {
                         mPickedChild = topEntry.getRow();
                         mTouchingHeadsUpView = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
index 927228e..925a19d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
@@ -22,7 +22,7 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 public class KeyguardEnvironmentImpl implements KeyguardEnvironment {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index dd07ec4..1944c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -25,8 +25,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.util.MathUtils;
 import android.provider.Settings;
+import android.util.MathUtils;
 import android.util.TimeUtils;
 
 import com.android.systemui.Dependency;
@@ -66,6 +66,7 @@
     private float mDarkIntensity;
     private float mNextDarkIntensity;
     private float mDozeAmount;
+    private int mDisplayId;
     private final Runnable mTransitionDeferringDoneRunnable = new Runnable() {
         @Override
         public void run() {
@@ -85,6 +86,7 @@
         mStatusBarStateController.addCallback(this);
         mDozeAmount = mStatusBarStateController.getDozeAmount();
         mContext = context;
+        mDisplayId = mContext.getDisplayId();
     }
 
     public void destroy(Context context) {
@@ -104,15 +106,18 @@
     }
 
     @Override
-    public void appTransitionPending(boolean forced) {
-        if (mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
+    public void appTransitionPending(int displayId, boolean forced) {
+        if (mDisplayId != displayId || mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
             return;
         }
         mTransitionPending = true;
     }
 
     @Override
-    public void appTransitionCancelled() {
+    public void appTransitionCancelled(int displayId) {
+        if (mDisplayId != displayId) {
+            return;
+        }
         if (mTransitionPending && mTintChangePending) {
             mTintChangePending = false;
             animateIconTint(mPendingDarkIntensity, 0 /* delay */, getTintAnimationDuration());
@@ -121,8 +126,9 @@
     }
 
     @Override
-    public void appTransitionStarting(long startTime, long duration, boolean forced) {
-        if (mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
+    public void appTransitionStarting(int displayId, long startTime, long duration,
+            boolean forced) {
+        if (mDisplayId != displayId || mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
             return;
         }
         if (mTransitionPending && mTintChangePending) {
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 270565b..6d97d67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -18,6 +18,8 @@
 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.WindowType;
+import static android.app.StatusBarManager.WindowVisibleState;
 import static android.app.StatusBarManager.windowStateToString;
 
 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
@@ -60,7 +62,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Display;
-import android.view.IWindowManager;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -69,7 +70,6 @@
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
-import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
@@ -121,7 +121,7 @@
 
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
-    private static final long AUTOHIDE_TIMEOUT_MS = 2250;
+    private static final long AUTODIM_TIMEOUT_MS = 2250;
 
     private final AccessibilityManagerWrapper mAccessibilityManagerWrapper;
     protected final AssistManager mAssistManager;
@@ -130,14 +130,13 @@
 
     protected NavigationBarView mNavigationBarView = null;
 
-    private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
+    private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
 
     private int mNavigationIconHints = 0;
     private @TransitionMode int mNavigationBarMode;
     private AccessibilityManager mAccessibilityManager;
     private MagnificationContentObserver mMagnificationObserver;
     private ContentResolver mContentResolver;
-    private IWindowManager mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
 
     private int mDisabledFlags1;
     private int mDisabledFlags2;
@@ -153,6 +152,7 @@
 
     private int mSystemUiVisibility;
     private LightBarController mLightBarController;
+    private AutoHideController mAutoHideController;
 
     private OverviewProxyService mOverviewProxyService;
 
@@ -162,9 +162,6 @@
 
     private Handler mHandler = Dependency.get(Dependency.MAIN_HANDLER);
 
-    // last value sent to window manager
-    private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
-
     private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
         @Override
         public void onConnectionChanged(boolean isConnected) {
@@ -205,19 +202,12 @@
         if (visible) {
             // If the button will actually become visible and the navbar is about to hide,
             // tell the statusbar to keep it around for longer
-            touchAutoHide();
+            mAutoHideController.touchAutoHide();
         }
     };
 
     private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true);
 
-    private final Runnable mAutoHide = () -> {
-        int requested = mSystemUiVisibility & ~View.NAVIGATION_BAR_TRANSIENT;
-        if (mSystemUiVisibility != requested) {
-            notifySystemUiVisibilityChanged(requested);
-        }
-    };
-
     @Inject
     public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper,
             DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger,
@@ -376,8 +366,11 @@
     // ----- CommandQueue Callbacks -----
 
     @Override
-    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
         int hints = mNavigationIconHints;
         switch (backDisposition) {
@@ -410,15 +403,17 @@
     }
 
     @Override
-    public void topAppWindowChanged(boolean showMenu) {
-        if (mNavigationBarView != null) {
+    public void topAppWindowChanged(int displayId, boolean showMenu) {
+        if (displayId == mDisplayId && mNavigationBarView != null) {
             mNavigationBarView.setMenuVisibility(showMenu);
         }
     }
 
     @Override
-    public void setWindowState(int window, int state) {
-        if (mNavigationBarView != null
+    public void setWindowState(
+            int displayId, @WindowType int window, @WindowVisibleState int state) {
+        if (displayId == mDisplayId
+                && mNavigationBarView != null
                 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
                 && mNavigationBarWindowState != state) {
             mNavigationBarWindowState = state;
@@ -463,15 +458,18 @@
             mNavigationBarMode = barMode;
         }
         checkNavBarModes();
-        touchAutoHide();
+        mAutoHideController.touchAutoHide();
 
         mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
-                    true /* nbModeChanged */, mNavigationBarMode);
+                true /* nbModeChanged */, mNavigationBarMode);
     }
 
     @Override
-    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
-            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+    public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         final int oldVal = mSystemUiVisibility;
         final int newVal = (oldVal & ~mask) | (vis & mask);
         final int diff = newVal ^ oldVal;
@@ -492,16 +490,7 @@
                     mNavigationBarMode = nbMode;
                     checkNavBarModes();
                 }
-                touchAutoHide();
-            }
-            if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
-                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
-            }
-
-
-            // On the default display, just make StatusBar do this job.
-            if (!mIsOnDefaultDisplay) {
-                notifySystemUiVisibilityChanged(mSystemUiVisibility);
+                mAutoHideController.touchAutoHide();
             }
         }
         mLightBarController.onNavigationVisibilityChanged(
@@ -536,7 +525,10 @@
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         // Navigation bar flags are in both state1 and state2.
         final int masked = state1 & (StatusBarManager.DISABLE_HOME
                 | StatusBarManager.DISABLE_RECENT
@@ -664,7 +656,7 @@
     }
 
     private boolean onNavigationTouch(View v, MotionEvent event) {
-        checkUserAutoHide(event);
+        mAutoHideController.checkUserAutoHide(event);
         return false;
     }
 
@@ -854,77 +846,15 @@
         mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
     }
 
-    private void touchAutoHide() {
-        // There is status bar on default display. Thus the hide animations should apply on both
-        // status/navigation bar.
-        if (mIsOnDefaultDisplay) {
-            mStatusBar.touchAutoHide();
-        } else {
-            touchAutoHideInternal();
-        }
-    }
-
-    private void touchAutoHideInternal() {
-        // update transient bar autoHide.
-        if (isSemiTransparent()) {
-            scheduleAutoHide();
-        } else {
-            cancelAutoHide();
-        }
-    }
-
-    private void checkUserAutoHide(MotionEvent event) {
-        // There is status bar on default display. Thus the hide animations should apply on both
-        // status/navigation bar.
-        if (mIsOnDefaultDisplay) {
-            mStatusBar.checkUserAutoHide(event);
-        } else {
-            checkUserAutoHideInternal(event);
-        }
-    }
-
-    private void checkUserAutoHideInternal(MotionEvent event) {
-        if ((mSystemUiVisibility & View.NAVIGATION_BAR_TRANSIENT) != 0
-                // a transient bar is revealed
-                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar.
-                && event.getX() == 0 && event.getY() == 0) { // a touch outside both bars.
-            userAutoHide();
-        }
-    }
-
-    private void userAutoHide() {
-        cancelAutoHide();
-        mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear.
-    }
-
-    private void cancelAutoHide() {
-        mHandler.removeCallbacks(mAutoHide);
-    }
-
-    private void scheduleAutoHide() {
-        cancelAutoHide();
-        mHandler.postDelayed(mAutoHide, AUTOHIDE_TIMEOUT_MS);
-    }
-
-    private void notifySystemUiVisibilityChanged(int vis) {
-        try {
-            if (mLastDispatchedSystemUiVisibility != vis) {
-                mWindowManagerService.statusBarVisibilityChanged(mDisplayId, vis);
-                mLastDispatchedSystemUiVisibility = vis;
-            }
-        } catch (RemoteException ex) {
-        }
-    }
-
     // ----- Methods that DisplayNavigationBarController talks to -----
 
-    /** Applys auto dimming animation on navigation bar when touched. */
+    /** Applies auto dimming animation on navigation bar when touched. */
     public void touchAutoDim() {
         getBarTransitions().setAutoDim(false);
         mHandler.removeCallbacks(mAutoDim);
         int state = Dependency.get(StatusBarStateController.class).getState();
         if (state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED) {
-            mHandler.postDelayed(mAutoDim, AUTOHIDE_TIMEOUT_MS);
+            mHandler.postDelayed(mAutoDim, AUTODIM_TIMEOUT_MS);
         }
     }
 
@@ -933,6 +863,12 @@
         mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController());
     }
 
+    /** Sets {@link AutoHideController} to the navigation bar. */
+    public void setAutoHideController(AutoHideController autoHideController) {
+        mAutoHideController = autoHideController;
+        mAutoHideController.setNavigationBar(this);
+    }
+
     public boolean isSemiTransparent() {
         return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
     }
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 3839ed5..d364356 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -31,9 +31,9 @@
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
@@ -108,7 +108,7 @@
      * @param entry notification to check
      * @return true if the entry was transferred to and should inflate + alert
      */
-    public boolean isAlertTransferPending(@NonNull Entry entry) {
+    public boolean isAlertTransferPending(@NonNull NotificationEntry entry) {
         PendingAlertInfo alertInfo = mPendingAlerts.get(entry.key);
         return alertInfo != null && alertInfo.isStillValid();
     }
@@ -172,16 +172,16 @@
     };
 
     @Override
-    public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+    public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
         onAlertStateChanged(entry, isAmbient, mAmbientPulseManager);
     }
 
     @Override
-    public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
     }
 
-    private void onAlertStateChanged(Entry entry, boolean isAlerting,
+    private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting,
             AlertingNotificationManager alertManager) {
         if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.notification)) {
             handleSuppressedSummaryAlerted(entry, alertManager);
@@ -193,7 +193,7 @@
         // Called when a new notification has been posted but is not inflated yet. We use this to
         // see as early as we can if we need to abort a transfer.
         @Override
-        public void onPendingEntryAdded(Entry entry) {
+        public void onPendingEntryAdded(NotificationEntry entry) {
             String groupKey = mGroupManager.getGroupKey(entry.notification);
             GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
             if (groupAlertEntry != null) {
@@ -204,7 +204,7 @@
         // Called when the entry's reinflation has finished. If there is an alert pending, we
         // then show the alert.
         @Override
-        public void onEntryReinflated(Entry entry) {
+        public void onEntryReinflated(NotificationEntry entry) {
             PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.key);
             if (alertInfo != null) {
                 if (alertInfo.isStillValid()) {
@@ -219,16 +219,13 @@
 
         @Override
         public void onEntryRemoved(
-                @Nullable Entry entry,
-                String key,
-                StatusBarNotification old,
+                @Nullable NotificationEntry entry,
                 NotificationVisibility visibility,
-                boolean lifetimeExtended,
                 boolean removedByUser) {
             // Removes any alerts pending on this entry. Note that this will not stop any inflation
             // tasks started by a transfer, so this should only be used as clean-up for when
             // inflation is stopped and the pending alert no longer needs to happen.
-            mPendingAlerts.remove(key);
+            mPendingAlerts.remove(entry.key);
         }
     };
 
@@ -244,8 +241,8 @@
             return 0;
         }
         int number = 0;
-        Iterable<Entry> values = mEntryManager.getPendingNotificationsIterator();
-        for (Entry entry : values) {
+        Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator();
+        for (NotificationEntry entry : values) {
             if (isPendingNotificationInGroup(entry, group) && onlySummaryAlerts(entry)) {
                 number++;
             }
@@ -263,8 +260,8 @@
         if (mEntryManager == null) {
             return false;
         }
-        Iterable<Entry> values = mEntryManager.getPendingNotificationsIterator();
-        for (Entry entry : values) {
+        Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator();
+        for (NotificationEntry entry : values) {
             if (isPendingNotificationInGroup(entry, group)) {
                 return true;
             }
@@ -279,7 +276,7 @@
      * @param group group to check
      * @return true if the notification will add to the group, false o/w
      */
-    private boolean isPendingNotificationInGroup(@NonNull Entry entry,
+    private boolean isPendingNotificationInGroup(@NonNull NotificationEntry entry,
             @NonNull NotificationGroup group) {
         String groupKey = mGroupManager.getGroupKey(group.summary.notification);
         return mGroupManager.isGroupChild(entry.notification)
@@ -296,7 +293,7 @@
      * @param summary the summary that is suppressed and alerting
      * @param alertManager the alert manager that manages the alerting summary
      */
-    private void handleSuppressedSummaryAlerted(@NonNull Entry summary,
+    private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
             @NonNull AlertingNotificationManager alertManager) {
         StatusBarNotification sbn = summary.notification;
         GroupAlertEntry groupAlertEntry =
@@ -312,7 +309,7 @@
             return;
         }
 
-        Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
+        NotificationEntry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
         if (child != null) {
             if (child.getRow().keepInParent()
                     || child.isRowRemoved()
@@ -336,7 +333,7 @@
      * @param toEntry entry to transfer to
      * @param alertManager alert manager for the alert type
      */
-    private void transferAlertState(@NonNull Entry fromEntry, @NonNull Entry toEntry,
+    private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry,
             @NonNull AlertingNotificationManager alertManager) {
         alertManager.removeNotification(fromEntry.key, true /* releaseImmediately */);
         alertNotificationWhenPossible(toEntry, alertManager);
@@ -356,13 +353,13 @@
     private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
         if (SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
                 < ALERT_TRANSFER_TIMEOUT) {
-            Entry summary = groupAlertEntry.mGroup.summary;
+            NotificationEntry summary = groupAlertEntry.mGroup.summary;
             AlertingNotificationManager alertManager = getActiveAlertManager();
 
             if (!onlySummaryAlerts(summary)) {
                 return;
             }
-            ArrayList<Entry> children = mGroupManager.getLogicalChildren(summary.notification);
+            ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.notification);
             int numChildren = children.size();
             int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
             numChildren += numPendingChildren;
@@ -371,7 +368,7 @@
             }
             boolean releasedChild = false;
             for (int i = 0; i < children.size(); i++) {
-                Entry entry = children.get(i);
+                NotificationEntry entry = children.get(i);
                 if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) {
                     releasedChild = true;
                     alertManager.removeNotification(entry.key, true /* releaseImmediately */);
@@ -402,7 +399,7 @@
      * @param entry entry to show
      * @param alertManager alert manager for the alert type
      */
-    private void alertNotificationWhenPossible(@NonNull Entry entry,
+    private void alertNotificationWhenPossible(@NonNull NotificationEntry entry,
             @NonNull AlertingNotificationManager alertManager) {
         @InflationFlag int contentFlag = alertManager.getContentFlag();
         if (!entry.getRow().isInflationFlagSet(contentFlag)) {
@@ -422,7 +419,7 @@
         return mIsDozing ? mAmbientPulseManager : mHeadsUpManager;
     }
 
-    private boolean onlySummaryAlerts(Entry entry) {
+    private boolean onlySummaryAlerts(NotificationEntry entry) {
         return entry.notification.getNotification().getGroupAlertBehavior()
                 == Notification.GROUP_ALERT_SUMMARY;
     }
@@ -442,7 +439,7 @@
          * the transfer is still valid if the notification is updated.
          */
         final StatusBarNotification mOriginalNotification;
-        final Entry mEntry;
+        final NotificationEntry mEntry;
 
         /**
          * The notification is still pending inflation but we've decided that we no longer need
@@ -453,7 +450,7 @@
          */
         boolean mAbortOnInflation;
 
-        PendingAlertInfo(Entry entry, AlertingNotificationManager alertManager) {
+        PendingAlertInfo(NotificationEntry entry, AlertingNotificationManager alertManager) {
             mOriginalNotification = entry.notification;
             mEntry = entry;
             mAlertManager = alertManager;
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 3c1c076..bb9e418 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -27,7 +27,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -97,7 +97,7 @@
         }
     }
 
-    public void onEntryRemoved(NotificationData.Entry removed) {
+    public void onEntryRemoved(NotificationEntry removed) {
         onEntryRemovedInternal(removed, removed.notification);
         mIsolatedEntries.remove(removed.key);
     }
@@ -109,7 +109,7 @@
      * @param sbn the notification the entry has, which doesn't need to be the same as it's internal
      *            notification
      */
-    private void onEntryRemovedInternal(NotificationData.Entry removed,
+    private void onEntryRemovedInternal(NotificationEntry removed,
             final StatusBarNotification sbn) {
         String groupKey = getGroupKey(sbn);
         final NotificationGroup group = mGroupMap.get(groupKey);
@@ -136,7 +136,7 @@
         }
     }
 
-    public void onEntryAdded(final NotificationData.Entry added) {
+    public void onEntryAdded(final NotificationEntry added) {
         if (added.isRowRemoved()) {
             added.setDebugThrowable(new Throwable());
         }
@@ -152,7 +152,7 @@
             }
         }
         if (isGroupChild) {
-            NotificationData.Entry existing = group.children.get(added.key);
+            NotificationEntry existing = group.children.get(added.key);
             if (existing != null && existing != added) {
                 Throwable existingThrowable = existing.getDebugThrowable();
                 Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key
@@ -169,9 +169,9 @@
             group.expanded = added.areChildrenExpanded();
             updateSuppression(group);
             if (!group.children.isEmpty()) {
-                ArrayList<NotificationData.Entry> childrenCopy
+                ArrayList<NotificationEntry> childrenCopy
                         = new ArrayList<>(group.children.values());
-                for (NotificationData.Entry child : childrenCopy) {
+                for (NotificationEntry child : childrenCopy) {
                     onEntryBecomingChild(child);
                 }
                 for (OnGroupChangeListener listener : mListeners) {
@@ -181,7 +181,7 @@
         }
     }
 
-    private void onEntryBecomingChild(NotificationData.Entry entry) {
+    private void onEntryBecomingChild(NotificationEntry entry) {
         if (shouldIsolate(entry)) {
             isolateNotification(entry);
         }
@@ -221,7 +221,7 @@
         return count;
     }
 
-    private NotificationData.Entry getIsolatedChild(String groupKey) {
+    private NotificationEntry getIsolatedChild(String groupKey) {
         for (StatusBarNotification sbn : mIsolatedEntries.values()) {
             if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) {
                 return mGroupMap.get(sbn.getKey()).summary;
@@ -230,7 +230,7 @@
         return null;
     }
 
-    public void onEntryUpdated(NotificationData.Entry entry,
+    public void onEntryUpdated(NotificationEntry entry,
             StatusBarNotification oldNotification) {
         String oldKey = oldNotification.getGroupKey();
         String newKey = entry.notification.getGroupKey();
@@ -267,7 +267,7 @@
         if (!isOnlyChild(sbn)) {
             return false;
         }
-        NotificationData.Entry logicalGroupSummary = getLogicalGroupSummary(sbn);
+        NotificationEntry logicalGroupSummary = getLogicalGroupSummary(sbn);
         return logicalGroupSummary != null
                 && !logicalGroupSummary.notification.equals(sbn);
     }
@@ -343,7 +343,7 @@
      * Get the summary of a specified status bar notification. For isolated notification this return
      * itself.
      */
-    public NotificationData.Entry getGroupSummary(StatusBarNotification sbn) {
+    public NotificationEntry getGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(getGroupKey(sbn));
     }
 
@@ -352,12 +352,12 @@
      * but the logical summary, i.e when a child is isolated, it still returns the summary as if
      * it wasn't isolated.
      */
-    public NotificationData.Entry getLogicalGroupSummary(StatusBarNotification sbn) {
+    public NotificationEntry getLogicalGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(sbn.getGroupKey());
     }
 
     @Nullable
-    private NotificationData.Entry getGroupSummary(String groupKey) {
+    private NotificationEntry getGroupSummary(String groupKey) {
         NotificationGroup group = mGroupMap.get(groupKey);
         //TODO: see if this can become an Entry
         return group == null ? null
@@ -371,13 +371,13 @@
      * @param summary summary of a group
      * @return list of the children
      */
-    public ArrayList<NotificationData.Entry> getLogicalChildren(StatusBarNotification summary) {
+    public ArrayList<NotificationEntry> getLogicalChildren(StatusBarNotification summary) {
         NotificationGroup group = mGroupMap.get(summary.getGroupKey());
         if (group == null) {
             return null;
         }
-        ArrayList<NotificationData.Entry> children = new ArrayList<>(group.children.values());
-        NotificationData.Entry isolatedChild = getIsolatedChild(summary.getGroupKey());
+        ArrayList<NotificationEntry> children = new ArrayList<>(group.children.values());
+        NotificationEntry isolatedChild = getIsolatedChild(summary.getGroupKey());
         if (isolatedChild != null) {
             children.add(isolatedChild);
         }
@@ -443,24 +443,24 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry entry) {
     }
 
     @Override
-    public void onAmbientStateChanged(NotificationData.Entry entry, boolean isAmbient) {
+    public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
         onAlertStateChanged(entry, isAmbient);
     }
 
     @Override
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         onAlertStateChanged(entry, isHeadsUp);
     }
 
-    private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting) {
+    private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting) {
         if (isAlerting) {
             if (shouldIsolate(entry)) {
                 isolateNotification(entry);
@@ -479,7 +479,7 @@
      * @return true if the entry should be isolated
      */
 
-    private boolean shouldIsolate(NotificationData.Entry entry) {
+    private boolean shouldIsolate(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
         NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
         if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
@@ -499,7 +499,7 @@
      *
      * @param entry the notification to isolate
      */
-    private void isolateNotification(NotificationData.Entry entry) {
+    private void isolateNotification(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
 
         // We will be isolated now, so lets update the groups
@@ -523,7 +523,7 @@
      *
      * @param entry the notification to un-isolate
      */
-    private void stopIsolatingNotification(NotificationData.Entry entry) {
+    private void stopIsolatingNotification(NotificationEntry entry) {
         StatusBarNotification sbn = entry.notification;
         if (mIsolatedEntries.containsKey(sbn.getKey())) {
             // not isolated anymore, we need to update the groups
@@ -564,8 +564,8 @@
     }
 
     public static class NotificationGroup {
-        public final HashMap<String, NotificationData.Entry> children = new HashMap<>();
-        public NotificationData.Entry summary;
+        public final HashMap<String, NotificationEntry> children = new HashMap<>();
+        public NotificationEntry summary;
         public boolean expanded;
         /**
          * Is this notification group suppressed, i.e its summary is hidden
@@ -580,7 +580,7 @@
                             ? Log.getStackTraceString(summary.getDebugThrowable())
                             : "");
             result += "\n    children size: " + children.size();
-            for (NotificationData.Entry child : children.values()) {
+            for (NotificationEntry child : children.values()) {
                 result += "\n      " + child.notification
                 + (child.getDebugThrowable() != null
                         ? Log.getStackTraceString(child.getDebugThrowable())
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 056c8a7..1fb5484 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -23,9 +23,9 @@
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.tuner.TunerService;
 
@@ -182,7 +182,7 @@
         return mStatusBar.getStatusBarHeight();
     }
 
-    protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
+    protected boolean shouldShowNotificationIcon(NotificationEntry entry,
             boolean showAmbient, boolean showLowPriority, boolean hideDismissed,
             boolean hideRepliedMessages) {
         if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
@@ -247,7 +247,7 @@
      * @param hideDismissed should dismissed icons be hidden
      * @param hideRepliedMessages should messages that have been replied to be hidden
      */
-    private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
+    private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function,
             NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,
             boolean hideDismissed, boolean hideRepliedMessages) {
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(
@@ -257,7 +257,7 @@
         for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
             View view = mNotificationScrollLayout.getChildAt(i);
             if (view instanceof ExpandableNotificationRow) {
-                NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
+                NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry();
                 if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,
                         hideRepliedMessages)) {
                     toShow.add(function.apply(ent));
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 fd34ac5..d873b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -76,9 +76,9 @@
 import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -312,6 +312,7 @@
             Dependency.get(NotificationLockscreenUserManager.class);
     private final ShadeController mShadeController =
             Dependency.get(ShadeController.class);
+    private int mDisplayId;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -323,6 +324,7 @@
         mAlphaPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
         setPanelAlpha(255, false /* animate */);
         mCommandQueue = getComponent(context, CommandQueue.class);
+        mDisplayId = context.getDisplayId();
     }
 
     private void setStatusBar(StatusBar bar) {
@@ -2487,12 +2489,12 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
         mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry 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
@@ -2505,7 +2507,7 @@
     }
 
     @Override
-    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
     }
 
@@ -2596,7 +2598,7 @@
         }
         if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
             mShowIconsWhenExpanded = showIconsWhenExpanded;
-            mCommandQueue.recomputeDisableFlags(false);
+            mCommandQueue.recomputeDisableFlags(mDisplayId, false);
         }
     }
 
@@ -2861,7 +2863,7 @@
             if (hideIcons != mHideIconsDuringNotificationLaunch) {
                 mHideIconsDuringNotificationLaunch = hideIcons;
                 if (!hideIcons) {
-                    mCommandQueue.recomputeDisableFlags(true /* animate */);
+                    mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index d6f2fd7..43c35f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -736,9 +736,12 @@
             };
 
     @Override
-    public void appTransitionStarting(long startTime, long duration, boolean forced) {
-        updateManagedProfile();
-        updateForegroundInstantApps();
+    public void appTransitionStarting(int displayId, long startTime, long duration,
+            boolean forced) {
+        if (mContext.getDisplayId() == displayId) {
+            updateManagedProfile();
+            updateForegroundInstantApps();
+        }
     }
 
     @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 9f0eec4..6d78f72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -20,9 +20,10 @@
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.WindowType;
+import static android.app.StatusBarManager.WindowVisibleState;
 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;
@@ -79,6 +80,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -190,12 +192,11 @@
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
 import com.android.systemui.statusbar.notification.NotificationClicker;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -289,10 +290,6 @@
 
     protected static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
 
-    private static final int STATUS_OR_NAV_TRANSIENT =
-            View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
-    private static final long AUTOHIDE_TIMEOUT_MS = 2250;
-
     /**
      * The delay to reset the hint text when the hint animation is finished running.
      */
@@ -341,6 +338,8 @@
     protected BiometricUnlockController mBiometricUnlockController;
     private LightBarController mLightBarController;
     protected LockscreenWallpaper mLockscreenWallpaper;
+    @VisibleForTesting
+    protected AutoHideController mAutoHideController;
 
     private int mNaturalBarHeight = -1;
 
@@ -405,9 +404,6 @@
     private final Rect mLastFullscreenStackBounds = new Rect();
     private final Rect mLastDockedStackBounds = new Rect();
 
-    // last value sent to window manager
-    private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
-
     private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
 
     // XXX: gesture research
@@ -447,7 +443,6 @@
     protected final H mHandler = createHandler();
 
     private int mInteractingWindows;
-    private boolean mAutoHideSuspended;
     private @TransitionMode int mStatusBarMode;
 
     private ViewMediatorCallback mKeyguardViewMediatorCallback;
@@ -455,13 +450,6 @@
     protected DozeScrimController mDozeScrimController;
     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
 
-    private final Runnable mAutoHide = () -> {
-        int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
-        if (mSystemUiVisibility != requested) {
-            notifySystemUiVisibilityChanged(requested);
-        }
-    };
-
     protected boolean mDozing;
     private boolean mDozingRequested;
 
@@ -500,7 +488,7 @@
 
     private Runnable mLaunchTransitionEndRunnable;
     protected boolean mLaunchTransitionFadingAway;
-    private NotificationData.Entry mDraggedDownEntry;
+    private NotificationEntry mDraggedDownEntry;
     private boolean mLaunchCameraOnScreenTurningOn;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
     private int mLastCameraLaunchSource;
@@ -584,6 +572,7 @@
                     mEntryManager.updateNotifications();
                 }
             };
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
 
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
     private boolean mVibrateOnOpening;
@@ -643,7 +632,7 @@
         mBubbleController.setExpandListener(mBubbleExpandListener);
         KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
         if (sliceProvider != null) {
-            sliceProvider.initDependencies();
+            sliceProvider.initDependencies(mMediaManager, mStatusBarStateController);
         } else {
             Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
         }
@@ -656,6 +645,7 @@
                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
 
         mDisplay = mWindowManager.getDefaultDisplay();
+        mDisplayId = mDisplay.getDisplayId();
         updateDisplaySize();
 
         Resources res = mContext.getResources();
@@ -711,11 +701,11 @@
         // Set up the initial notification state. This needs to happen before CommandQueue.disable()
         setUpPresenter();
 
-        setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
+        setSystemUiVisibility(mDisplayId, switches[1], switches[7], switches[8], 0xffffffff,
                 fullscreenStackBounds, dockedStackBounds);
-        topAppWindowChanged(switches[2] != 0);
+        topAppWindowChanged(mDisplayId, switches[2] != 0);
         // StatusBarManagerService has a back up of IME token and it's restored here.
-        setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
+        setImeWindowStatus(mDisplayId, binders.get(0), switches[3], switches[4], switches[5] != 0);
 
         // Set up the initial icon state
         int N = iconSlots.size();
@@ -902,6 +892,9 @@
             }
         });
 
+        mAutoHideController = Dependency.get(AutoHideController.class);
+        mAutoHideController.setStatusBar(this);
+
         mLightBarController = Dependency.get(LightBarController.class);
 
         ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
@@ -1061,7 +1054,7 @@
      * @param state2 disable2 flags
      */
     protected void setUpDisableFlags(int state1, int state2) {
-        mCommandQueue.disable(state1, state2, false /* animate */);
+        mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */);
     }
 
     @Override
@@ -1100,7 +1093,7 @@
      */
     protected View.OnTouchListener getStatusBarWindowTouchListener() {
         return (v, event) -> {
-            checkUserAutoHide(event);
+            mAutoHideController.checkUserAutoHide(event);
             mRemoteInputManager.checkRemoteInputOutside(event);
             if (event.getAction() == MotionEvent.ACTION_DOWN) {
                 if (mExpandedVisible) {
@@ -1235,8 +1228,7 @@
         }
         int dockSide = WindowManagerProxy.getInstance().getDockSide();
         if (dockSide == WindowManager.DOCKED_INVALID) {
-            final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(
-                    mDisplay.getDisplayId());
+            final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
             if (navbarPos == NAV_BAR_POS_INVALID) {
                 return false;
             }
@@ -1345,7 +1337,10 @@
      * State is one or more of the DISABLE constants from StatusBarManager.
      */
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
 
         animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
@@ -1514,21 +1509,21 @@
     }
 
     @Override
-    public void onHeadsUpPinned(NotificationData.Entry entry) {
+    public void onHeadsUpPinned(NotificationEntry entry) {
         dismissVolumeDialog();
     }
 
     @Override
-    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+    public void onHeadsUpUnPinned(NotificationEntry entry) {
     }
 
     @Override
-    public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         mEntryManager.updateNotificationRanking(null /* rankingMap */);
     }
 
     @Override
-    public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+    public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
         mEntryManager.updateNotificationRanking(null);
         if (isAmbient) {
             mDozeServiceHost.fireNotificationPulse();
@@ -1623,10 +1618,10 @@
                 mWereIconsJustHidden = true;
                 mHandler.postDelayed(() -> {
                     mWereIconsJustHidden = false;
-                    mCommandQueue.recomputeDisableFlags(true);
+                    mCommandQueue.recomputeDisableFlags(mDisplayId, true);
                 }, 500);
             } else {
-                mCommandQueue.recomputeDisableFlags(animate);
+                mCommandQueue.recomputeDisableFlags(mDisplayId, animate);
             }
         }
         if (shouldHideIconsForBouncer) {
@@ -1792,7 +1787,7 @@
         mStatusBarWindowController.setPanelVisible(true);
 
         visibilityChanged(true);
-        mCommandQueue.recomputeDisableFlags(!force /* animate */);
+        mCommandQueue.recomputeDisableFlags(mDisplayId, !force /* animate */);
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
     }
 
@@ -1965,7 +1960,7 @@
             Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
         }
         mCommandQueue.recomputeDisableFlags(
-                mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
+                mDisplayId, mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
 
         // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
         // the bouncer appear animation.
@@ -2022,7 +2017,11 @@
     }
 
     @Override // CommandQueue
-    public void setWindowState(int window, int state) {
+    public void setWindowState(
+            int displayId, @WindowType int window, @WindowVisibleState int state) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         boolean showing = state == WINDOW_STATE_SHOWING;
         if (mStatusBarWindow != null
                 && window == StatusBarManager.WINDOW_STATUS_BAR
@@ -2041,14 +2040,17 @@
     }
 
     @Override // CommandQueue
-    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
-            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+    public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+        if (displayId != mDisplayId) {
+            return;
+        }
         final int oldVal = mSystemUiVisibility;
         final int newVal = (oldVal&~mask) | (vis&mask);
         final int diff = newVal ^ oldVal;
         if (DEBUG) Log.d(TAG, String.format(
-                "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
-                Integer.toHexString(vis), Integer.toHexString(mask),
+                "setSystemUiVisibility displayId=%d vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
+                displayId, Integer.toHexString(vis), Integer.toHexString(mask),
                 Integer.toHexString(oldVal), Integer.toHexString(newVal),
                 Integer.toHexString(diff)));
         boolean sbModeChanged = false;
@@ -2062,7 +2064,6 @@
 
             // ready to unhide
             if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
-                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
                 mNoAnimationOnNextBarModeChange = true;
             }
 
@@ -2073,17 +2074,9 @@
             if (sbModeChanged && sbMode != mStatusBarMode) {
                 mStatusBarMode = sbMode;
                 checkBarModes();
-                touchAutoHide();
+                mAutoHideController.touchAutoHide();
             }
-
-            if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
-                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
-            }
-
-            // send updated sysui visibility to window manager
-            notifySystemUiVisibilityChanged(mSystemUiVisibility);
         }
-
         mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
                 mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode);
     }
@@ -2111,16 +2104,6 @@
         }
     }
 
-    void touchAutoHide() {
-        // update transient bar auto hide
-        if (mStatusBarMode == MODE_SEMI_TRANSPARENT
-                || mNavigationBarController.isSemiTransparent(DEFAULT_DISPLAY)) {
-            scheduleAutoHide();
-        } else {
-            cancelAutoHide();
-        }
-    }
-
     protected @TransitionMode int computeStatusBarMode(int oldVal, int newVal) {
         return computeBarMode(oldVal, newVal);
     }
@@ -2159,7 +2142,7 @@
         if (mDemoMode) return;
         if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState,
                 getStatusBarTransitions());
-        mNavigationBarController.checkNavBarModes(DEFAULT_DISPLAY);
+        mNavigationBarController.checkNavBarModes(mDisplayId);
         mNoAnimationOnNextBarModeChange = false;
     }
 
@@ -2168,7 +2151,8 @@
         mNotificationPanel.setQsScrimEnabled(scrimEnabled);
     }
 
-    void checkBarMode(@TransitionMode int mode, int windowState, BarTransitions transitions) {
+    void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
+            BarTransitions transitions) {
         final boolean anim = !mNoAnimationOnNextBarModeChange && mDeviceInteractive
                 && windowState != WINDOW_STATE_HIDDEN;
         transitions.transitionTo(mode, anim);
@@ -2178,7 +2162,7 @@
         if (mStatusBarView != null) {
             mStatusBarView.getBarTransitions().finishAnimations();
         }
-        mNavigationBarController.finishBarAnimations(DEFAULT_DISPLAY);
+        mNavigationBarController.finishBarAnimations(mDisplayId);
     }
 
     private final Runnable mCheckBarModes = this::checkBarModes;
@@ -2189,13 +2173,13 @@
                 ? (mInteractingWindows | barWindow)
                 : (mInteractingWindows & ~barWindow);
         if (mInteractingWindows != 0) {
-            suspendAutoHide();
+            mAutoHideController.suspendAutoHide();
         } else {
-            resumeSuspendedAutoHide();
+            mAutoHideController.resumeSuspendedAutoHide();
         }
         // manually dismiss the volume panel when interacting with the nav bar
         if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) {
-            mNavigationBarController.touchAutoDim(DEFAULT_DISPLAY);
+            mNavigationBarController.touchAutoDim(mDisplayId);
             dismissVolumeDialog();
         }
         checkBarModes();
@@ -2207,44 +2191,6 @@
         }
     }
 
-    private void resumeSuspendedAutoHide() {
-        if (mAutoHideSuspended) {
-            scheduleAutoHide();
-            mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
-        }
-    }
-
-    private void suspendAutoHide() {
-        mHandler.removeCallbacks(mAutoHide);
-        mHandler.removeCallbacks(mCheckBarModes);
-        mAutoHideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
-    }
-
-    private void cancelAutoHide() {
-        mAutoHideSuspended = false;
-        mHandler.removeCallbacks(mAutoHide);
-    }
-
-    private void scheduleAutoHide() {
-        cancelAutoHide();
-        mHandler.postDelayed(mAutoHide, AUTOHIDE_TIMEOUT_MS);
-    }
-
-    void checkUserAutoHide(MotionEvent event) {
-        if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0  // a transient bar is revealed
-                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
-                && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
-                && !mRemoteInputManager.getController()
-                        .isRemoteInputActive()) { // not due to typing in IME
-            userAutoHide();
-        }
-    }
-
-    private void userAutoHide() {
-        cancelAutoHide();
-        mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear
-    }
-
     private boolean areLightsOn() {
         return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
     }
@@ -2252,29 +2198,21 @@
     public void setLightsOn(boolean on) {
         Log.v(TAG, "setLightsOn(" + on + ")");
         if (on) {
-            setSystemUiVisibility(0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
+            setSystemUiVisibility(mDisplayId, 0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
                     mLastFullscreenStackBounds, mLastDockedStackBounds);
         } else {
-            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
+            setSystemUiVisibility(mDisplayId, View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0,
                     View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds,
                     mLastDockedStackBounds);
         }
     }
 
-    private void notifySystemUiVisibilityChanged(int vis) {
-        try {
-            if (mLastDispatchedSystemUiVisibility != vis) {
-                mWindowManagerService.statusBarVisibilityChanged(DEFAULT_DISPLAY, vis);
-                mLastDispatchedSystemUiVisibility = vis;
-            }
-        } catch (RemoteException ex) {
-        }
-    }
-
     @Override
-    public void topAppWindowChanged(boolean showMenu) {
+    public void topAppWindowChanged(int displayId, boolean showMenu) {
+        if (mDisplayId != displayId) return;
         if (SPEW) {
-            Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
+            Log.d(TAG, "display#" + displayId + ": "
+                    + (showMenu ? "showing" : "hiding") + " the MENU button");
         }
 
         // See above re: lights-out policy for legacy apps.
@@ -2612,11 +2550,11 @@
     };
 
     public void resetUserExpandedStates() {
-        ArrayList<Entry> activeNotifications = mEntryManager.getNotificationData()
+        ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
                 .getActiveNotifications();
         final int notificationCount = activeNotifications.size();
         for (int i = 0; i < notificationCount; i++) {
-            NotificationData.Entry entry = activeNotifications.get(i);
+            NotificationEntry entry = activeNotifications.get(i);
             entry.resetUserExpansion();
         }
     }
@@ -2963,7 +2901,7 @@
                 if (mStatusBarView != null) {
                     mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
                 }
-                mNavigationBarController.transitionTo(DEFAULT_DISPLAY, barMode, animate);
+                mNavigationBarController.transitionTo(mDisplayId, barMode, animate);
             }
         }
         if (modeChange || command.equals(COMMAND_OPERATOR)) {
@@ -3101,7 +3039,7 @@
                     .setDuration(FADE_KEYGUARD_DURATION)
                     .withLayer()
                     .withEndAction(this::onLaunchTransitionFadingEnded);
-            mCommandQueue.appTransitionStarting(SystemClock.uptimeMillis(),
+            mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(),
                     LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
         };
         if (mNotificationPanel.isLaunchTransitionRunning()) {
@@ -3191,7 +3129,7 @@
 
             // Disable layout transitions in navbar for this transition because the load is just
             // too heavy for the CPU and GPU on any device.
-            mNavigationBarController.disableAnimationsDuringHide(DEFAULT_DISPLAY, delay);
+            mNavigationBarController.disableAnimationsDuringHide(mDisplayId, delay);
         } else if (!mNotificationPanel.isCollapsing()) {
             instantCollapseNotificationPanel();
         }
@@ -3224,7 +3162,7 @@
         // Treat Keyguard exit animation as an app transition to achieve nice transition for status
         // bar.
         mKeyguardMonitor.notifyKeyguardGoingAway(true);
-        mCommandQueue.appTransitionPending(true);
+        mCommandQueue.appTransitionPending(mDisplayId, true /* forced */);
     }
 
     /**
@@ -3235,11 +3173,11 @@
      * @param fadeoutDuration the duration of the exit animation, in milliseconds
      */
     public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) {
-        mCommandQueue.appTransitionStarting(startTime + fadeoutDuration
+        mCommandQueue.appTransitionStarting(mDisplayId, startTime + fadeoutDuration
                         - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
                 LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
-        mCommandQueue.recomputeDisableFlags(fadeoutDuration > 0 /* animate */);
-        mCommandQueue.appTransitionStarting(
+        mCommandQueue.recomputeDisableFlags(mDisplayId, fadeoutDuration > 0 /* animate */);
+        mCommandQueue.appTransitionStarting(mDisplayId,
                     startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
                     LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
         mKeyguardMonitor.notifyKeyguardFadingAway(delay, fadeoutDuration);
@@ -3365,7 +3303,7 @@
         // Make our window larger and the panel expanded.
         makeExpandedVisible(true);
         mNotificationPanel.expand(false /* animate */);
-        mCommandQueue.recomputeDisableFlags(false /* animate */);
+        mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
     }
 
     @Override
@@ -3421,7 +3359,7 @@
         updateReportRejectedTouchVisibility();
         updateDozing();
         updateTheme();
-        mNavigationBarController.touchAutoDim(DEFAULT_DISPLAY);
+        mNavigationBarController.touchAutoDim(mDisplayId);
         Trace.beginSection("StatusBar#updateKeyguardState");
         if (mState == StatusBarState.KEYGUARD) {
             mKeyguardIndicationController.setVisible(true);
@@ -3567,7 +3505,7 @@
 
         int userId = mLockscreenUserManager.getCurrentUserId();
         ExpandableNotificationRow row = null;
-        NotificationData.Entry entry = null;
+        NotificationEntry entry = null;
         if (expandView instanceof ExpandableNotificationRow) {
             entry = ((ExpandableNotificationRow) expandView).getEntry();
             entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
@@ -3606,7 +3544,7 @@
         mBouncerShowing = bouncerShowing;
         if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing);
         updateHideIconsForBouncer(true /* animate */);
-        mCommandQueue.recomputeDisableFlags(true /* animate */);
+        mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
         updateScrimController();
     }
 
@@ -3731,13 +3669,17 @@
     }
 
     @Override
-    public void appTransitionCancelled() {
-        getComponent(Divider.class).onAppTransitionFinished();
+    public void appTransitionCancelled(int displayId) {
+        if (displayId == mDisplayId) {
+            getComponent(Divider.class).onAppTransitionFinished();
+        }
     }
 
     @Override
-    public void appTransitionFinished() {
-        getComponent(Divider.class).onAppTransitionFinished();
+    public void appTransitionFinished(int displayId) {
+        if (displayId == mDisplayId) {
+            getComponent(Divider.class).onAppTransitionFinished();
+        }
     }
 
     @Override
@@ -4133,6 +4075,7 @@
     private IDreamManager mDreamManager;
 
     protected Display mDisplay;
+    private int mDisplayId;
 
     protected Recents mRecents;
 
@@ -4260,7 +4203,7 @@
             // Immediately update the icon hidden state, since that should only apply if we're
             // staying fullscreen.
             mWereIconsJustHidden = false;
-            mCommandQueue.recomputeDisableFlags(true);
+            mCommandQueue.recomputeDisableFlags(mDisplayId, true);
         }
         updateHideIconsForBouncer(true /* animate */);
     }
@@ -4319,7 +4262,7 @@
 
     @Override
     public void startPendingIntentDismissingKeyguard(
-            final PendingIntent intent, @Nullable final Runnable intentSentCallback) {
+            final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) {
         final boolean afterKeyguardGone = intent.isActivity()
                 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
@@ -4338,12 +4281,16 @@
             if (intent.isActivity()) {
                 mAssistManager.hideAssist();
             }
-            if (intentSentCallback != null) {
-                intentSentCallback.run();
+            if (intentSentUiThreadCallback != null) {
+                postOnUiThread(intentSentUiThreadCallback);
             }
         }, afterKeyguardGone);
     }
 
+    private void postOnUiThread(Runnable runnable) {
+        mMainThreadHandler.post(runnable);
+    }
+
     public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
         ActivityOptions options;
         if (animationAdapter != null) {
@@ -4460,4 +4407,8 @@
     public interface StatusBarInjector {
         void createStatusBar(StatusBar statusbar);
     }
+
+    public @TransitionMode int getStatusBarMode() {
+        return mStatusBarMode;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 8d1b911..4f61009 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -60,10 +60,10 @@
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -131,7 +131,7 @@
 
         mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
-            public void onPendingEntryAdded(NotificationData.Entry entry) {
+            public void onPendingEntryAdded(NotificationEntry entry) {
                 handleFullScreenIntent(entry);
             }
         });
@@ -267,7 +267,7 @@
             }
         }
         Intent fillInIntent = null;
-        NotificationData.Entry entry = row.getEntry();
+        NotificationEntry entry = row.getEntry();
         CharSequence remoteInputText = null;
         if (!TextUtils.isEmpty(entry.remoteInputText)) {
             remoteInputText = entry.remoteInputText;
@@ -345,7 +345,7 @@
         }, null, false /* afterKeyguardGone */);
     }
 
-    private void handleFullScreenIntent(NotificationData.Entry entry) {
+    private void handleFullScreenIntent(NotificationEntry entry) {
         boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
         if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) {
             if (shouldSuppressFullScreenIntent(entry)) {
@@ -413,7 +413,7 @@
                 || !mActivityLaunchAnimator.isAnimationPending();
     }
 
-    private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
+    private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) {
         if (mPresenter.isDeviceInVrMode()) {
             return true;
         }
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 7b7bcb4..16c8e62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -43,6 +43,7 @@
 import com.android.internal.widget.MessagingMessage;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceNotificationListener;
 import com.android.systemui.InitController;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
@@ -59,12 +60,12 @@
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -182,27 +183,23 @@
         Dependency.get(InitController.class).addPostInitTask(() -> {
             NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
                 @Override
-                public void onNotificationAdded(Entry entry) {
+                public void onNotificationAdded(NotificationEntry entry) {
                     // Recalculate the position of the sliding windows and the titles.
                     mShadeController.updateAreThereNotifications();
                 }
 
                 @Override
-                public void onEntryUpdated(Entry entry) {
+                public void onEntryUpdated(NotificationEntry entry) {
                     mShadeController.updateAreThereNotifications();
                 }
 
                 @Override
                 public void onEntryRemoved(
-                        @Nullable Entry entry,
-                        String key,
-                        StatusBarNotification old,
+                        @Nullable NotificationEntry entry,
                         NotificationVisibility visibility,
-                        boolean lifetimeExtended,
                         boolean removedByUser) {
-                    if (!lifetimeExtended) {
-                        StatusBarNotificationPresenter.this.onNotificationRemoved(key, old);
-                    }
+                    StatusBarNotificationPresenter.this.onNotificationRemoved(
+                            entry.key, entry.notification);
                     if (removedByUser) {
                         maybeEndAmbientPulse();
                     }
@@ -228,6 +225,10 @@
             mVisualStabilityManager.setUpWithPresenter(this);
             gutsManager.setUpWithPresenter(this,
                     notifListContainer, mCheckSaveListener, mOnSettingsClickListener);
+            // ForegroundServiceControllerListener adds its listener in its constructor
+            // but we need to request it here in order for it to be instantiated.
+            // TODO: figure out how to do this correctly once Dependency.get() is gone.
+            Dependency.get(ForegroundServiceNotificationListener.class);
 
             onUserSwitched(mLockscreenUserManager.getCurrentUserId());
         });
@@ -257,10 +258,10 @@
     }
 
     private void updateNotificationOnUiModeChanged() {
-        ArrayList<Entry> userNotifications
+        ArrayList<NotificationEntry> userNotifications
                 = mEntryManager.getNotificationData().getNotificationsForCurrentUser();
         for (int i = 0; i < userNotifications.size(); i++) {
-            Entry entry = userNotifications.get(i);
+            NotificationEntry entry = userNotifications.get(i);
             ExpandableNotificationRow row = entry.getRow();
             if (row != null) {
                 row.onUiModeChanged();
@@ -322,7 +323,7 @@
         return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
     }
 
-    public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
+    public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
         if (mShadeController.isDozing()) {
             return false;
         }
@@ -380,7 +381,7 @@
     }
 
     @Override
-    public void onBindRow(Entry entry, PackageManager pmUser,
+    public void onBindRow(NotificationEntry entry, PackageManager pmUser,
             StatusBarNotification sbn, ExpandableNotificationRow row) {
         row.setAboveShelfChangedListener(mAboveShelfObserver);
         row.setSecureStateProvider(mUnlockMethodCache::canSkipBouncer);
@@ -438,7 +439,7 @@
     }
 
     @Override
-    public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
+    public void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded) {
         mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
         if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
             mShadeController.goToLockedShade(clickedEntry.getRow());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 116ecc8..3ddfc0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -238,8 +238,10 @@
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
-        mDisabled2 = state2;
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId == mContext.getDisplayId()) {
+            mDisabled2 = state2;
+        }
     }
 
     protected class ChallengeReceiver extends BroadcastReceiver {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 5eb3e89..de7ef3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -299,7 +299,10 @@
     }
 
     @Override
-    public void disable(int state1, int state2, boolean animate) {
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != getDisplay().getDisplayId()) {
+            return;
+        }
         boolean clockVisibleByPolicy = (state1 & StatusBarManager.DISABLE_CLOCK) == 0;
         if (clockVisibleByPolicy != mClockVisibleByPolicy) {
             setClockVisibilityByPolicy(clockVisibleByPolicy);
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 a02c9d5..fd3f680 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -30,7 +30,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlertingNotificationManager;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 
 import java.io.FileDescriptor;
@@ -108,11 +108,11 @@
         }
     }
 
-    protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) {
+    protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) {
         return hasFullScreenIntent(entry);
     }
 
-    protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) {
+    protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
         return entry.notification.getNotification().fullScreenIntent != null;
     }
 
@@ -121,7 +121,7 @@
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "setEntryPinned: " + isPinned);
         }
-        NotificationData.Entry entry = headsUpEntry.mEntry;
+        NotificationEntry entry = headsUpEntry.mEntry;
         if (entry.isRowPinned() != isPinned) {
             entry.setRowPinned(isPinned);
             updatePinnedMode();
@@ -141,7 +141,7 @@
 
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setHeadsUp(true);
         setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
         for (OnHeadsUpChangedListener listener : mListeners) {
@@ -151,7 +151,7 @@
 
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
-        NotificationData.Entry entry = alertEntry.mEntry;
+        NotificationEntry entry = alertEntry.mEntry;
         entry.setHeadsUp(false);
         setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
         for (OnHeadsUpChangedListener listener : mListeners) {
@@ -222,7 +222,7 @@
      * Returns the top Heads Up Notification, which appears to show at first.
      */
     @Nullable
-    public NotificationData.Entry getTopEntry() {
+    public NotificationEntry getTopEntry() {
         HeadsUpEntry topEntry = getTopHeadsUpEntry();
         return (topEntry != null) ? topEntry.mEntry : null;
     }
@@ -323,7 +323,7 @@
      * @return -1 if the first argument should be ranked higher than the second, 1 if the second
      * one should be ranked higher and 0 if they are equal.
      */
-    public int compare(@NonNull NotificationData.Entry a, @NonNull NotificationData.Entry b) {
+    public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) {
         AlertEntry aEntry = getHeadsUpEntry(a.key);
         AlertEntry bEntry = getHeadsUpEntry(b.key);
         if (aEntry == null || bEntry == null) {
@@ -336,7 +336,7 @@
      * Set an entry to be expanded and therefore stick in the heads up area if it's pinned
      * until it's collapsed again.
      */
-    public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
+    public void setExpanded(@NonNull NotificationEntry entry, boolean expanded) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
         if (headsUpEntry != null && entry.isRowPinned()) {
             headsUpEntry.setExpanded(expanded);
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 7ad547a..438226a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -16,8 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * A listener to heads up changes
@@ -33,12 +32,12 @@
     /**
      * A notification was just pinned to the top.
      */
-    default void onHeadsUpPinned(NotificationData.Entry entry) {}
+    default void onHeadsUpPinned(NotificationEntry entry) {}
 
     /**
      * A notification was just unpinned from the top.
      */
-    default void onHeadsUpUnPinned(NotificationData.Entry entry) {}
+    default void onHeadsUpUnPinned(NotificationEntry entry) {}
 
     /**
      * A notification just became a heads up or turned back to its normal state.
@@ -46,5 +45,5 @@
      * @param entry     the entry of the changed notification
      * @param isHeadsUp whether the notification is now a headsUp notification
      */
-    default void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {}
+    default void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
index 630bd18..2b60274 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
@@ -82,6 +82,6 @@
      * to modify the disable flags according to the status of mRemoteInputActive and misLandscape.
      */
     private void recomputeDisableFlags() {
-        mCommandQueue.recomputeDisableFlags(true);
+        mCommandQueue.recomputeDisableFlags(mContext.getDisplayId(), true);
     }
 }
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 866015e..dfb02cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -56,7 +56,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
@@ -83,7 +83,7 @@
     private RemoteInputController mController;
     private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
 
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
 
     private boolean mRemoved;
 
@@ -180,7 +180,7 @@
     }
 
     public static RemoteInputView inflate(Context context, ViewGroup root,
-            NotificationData.Entry entry,
+            NotificationEntry entry,
             RemoteInputController controller) {
         RemoteInputView v = (RemoteInputView)
                 LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
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 4fa8321..5e5fc42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -16,8 +16,6 @@
 import android.graphics.drawable.InsetDrawable;
 import android.graphics.drawable.RippleDrawable;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
 import android.text.Layout;
 import android.text.TextPaint;
 import android.text.method.TransformationMethod;
@@ -38,8 +36,8 @@
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.text.BreakIterator;
@@ -65,7 +63,6 @@
     private final SmartReplyConstants mConstants;
     private final KeyguardDismissUtil mKeyguardDismissUtil;
     private final NotificationRemoteInputManager mRemoteInputManager;
-    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
 
     /**
      * The upper bound for the height of this view in pixels. Notifications are automatically
@@ -197,7 +194,7 @@
      */
     public void addRepliesFromRemoteInput(
             SmartReplies smartReplies,
-            SmartReplyController smartReplyController, NotificationData.Entry entry) {
+            SmartReplyController smartReplyController, NotificationEntry entry) {
         if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) {
             if (smartReplies.choices != null) {
                 for (int i = 0; i < smartReplies.choices.length; ++i) {
@@ -215,7 +212,7 @@
      * notification are shown.
      */
     public void addSmartActions(SmartActions smartActions,
-            SmartReplyController smartReplyController, NotificationData.Entry entry,
+            SmartReplyController smartReplyController, NotificationEntry entry,
             HeadsUpManager headsUpManager) {
         int numSmartActions = smartActions.actions.size();
         for (int n = 0; n < numSmartActions; n++) {
@@ -238,7 +235,7 @@
     @VisibleForTesting
     Button inflateReplyButton(Context context, ViewGroup root, int replyIndex,
             SmartReplies smartReplies, SmartReplyController smartReplyController,
-            NotificationData.Entry entry) {
+            NotificationEntry entry) {
         Button b = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_reply_button, root, false);
         CharSequence choice = smartReplies.choices[replyIndex];
@@ -292,7 +289,7 @@
     @VisibleForTesting
     Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
             SmartActions smartActions, SmartReplyController smartReplyController,
-            NotificationData.Entry entry, HeadsUpManager headsUpManager) {
+            NotificationEntry entry, HeadsUpManager headsUpManager) {
         Notification.Action action = smartActions.actions.get(actionIndex);
         Button button = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_action_button, root, false);
@@ -311,8 +308,7 @@
                         () -> {
                             smartReplyController.smartActionClicked(
                                     entry, actionIndex, action, smartActions.fromAssistant);
-                            postOnUiThread(() ->
-                                    headsUpManager.removeNotification(entry.key, true));
+                            headsUpManager.removeNotification(entry.key, true);
                         }));
 
         // TODO(b/119010281): handle accessibility
@@ -323,10 +319,6 @@
         return button;
     }
 
-    private void postOnUiThread(Runnable runnable) {
-        mMainThreadHandler.post(runnable);
-    }
-
     @Override
     public LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new LayoutParams(mContext, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 59aa522..faebf60 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -26,6 +26,7 @@
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.qs.QSFooterImpl;
 import com.android.systemui.qs.QuickStatusBarHeader;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -112,6 +113,11 @@
          * Creates the QSFooterImpl.
          */
         QSFooterImpl createQsFooter();
+
+        /**
+         * Creates the NotificationStackScrollLayout.
+         */
+        NotificationStackScrollLayout createNotificationStackScrollLayout();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 490cdd5c..6b4f7ed 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -69,10 +69,13 @@
 
         /**
          * Sets visibility of this {@link View} given the states passed from
-         * {@link com.android.systemui.statusbar.CommandQueue.Callbacks#disable(int, int)}.
+         * {@link com.android.systemui.statusbar.CommandQueue.Callbacks#disable(int, int, int)}.
          */
         @Override
-        public void disable(int state1, int state2, boolean animate) {
+        public void disable(int displayId, int state1, int state2, boolean animate) {
+            if (displayId != mView.getDisplay().getDisplayId()) {
+                return;
+            }
             final boolean disabled = ((state1 & mMask1) != 0) || ((state2 & mMask2) != 0);
             if (disabled == mDisabled) return;
             mDisabled = disabled;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 4150602..1844df5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -35,6 +35,8 @@
 import android.testing.TestableLooper.RunWithLooper;
 import android.text.TextPaint;
 import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.TextClock;
 
@@ -42,6 +44,8 @@
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -60,6 +64,7 @@
 public class KeyguardClockSwitchTest extends SysuiTestCase {
     private PluginManager mPluginManager;
     private FrameLayout mClockContainer;
+    private StatusBarStateController.StateListener mStateListener;
 
     @Mock
     TextClock mClockView;
@@ -75,6 +80,7 @@
         mClockContainer = mKeyguardClockSwitch.findViewById(R.id.clock_view);
         MockitoAnnotations.initMocks(this);
         when(mClockView.getPaint()).thenReturn(mock(TextPaint.class));
+        mStateListener = mKeyguardClockSwitch.getStateListener();
     }
 
     @Test
@@ -272,4 +278,28 @@
 
         verify(plugin).setStyle(style);
     }
+
+    @Test
+    public void onStateChanged_InvisibleInShade() {
+        // GIVEN that the big clock container is visible
+        ViewGroup container = mock(ViewGroup.class);
+        when(container.getVisibility()).thenReturn(View.VISIBLE);
+        mKeyguardClockSwitch.setBigClockContainer(container);
+        // WHEN transitioned to SHADE state
+        mStateListener.onStateChanged(StatusBarState.SHADE);
+        // THEN the container is invisible.
+        verify(container).setVisibility(View.INVISIBLE);
+    }
+
+    @Test
+    public void onStateChanged_VisibleInKeyguard() {
+        // GIVEN that the big clock container is invisible
+        ViewGroup container = mock(ViewGroup.class);
+        when(container.getVisibility()).thenReturn(View.INVISIBLE);
+        mKeyguardClockSwitch.setBigClockContainer(container);
+        // WHEN transitioned to KEYGUARD state
+        mStateListener.onStateChanged(StatusBarState.KEYGUARD);
+        // THEN the container is visible.
+        verify(container).setVisibility(View.VISIBLE);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index f278a17..9400d6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.annotation.UserIdInt;
@@ -35,197 +36,95 @@
 import android.widget.RemoteViews;
 
 import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ForegroundServiceControllerTest extends SysuiTestCase {
-    public static @UserIdInt int USERID_ONE = 10; // UserManagerService.MIN_USER_ID;
-    public static @UserIdInt int USERID_TWO = USERID_ONE + 1;
+    @UserIdInt private static final int USERID_ONE = 10; // UserManagerService.MIN_USER_ID;
+    @UserIdInt private static final int USERID_TWO = USERID_ONE + 1;
 
-    private ForegroundServiceController fsc;
+    private ForegroundServiceController mFsc;
+    private ForegroundServiceNotificationListener mListener;
+    private NotificationEntryListener mEntryListener;
 
     @Before
     public void setUp() throws Exception {
-        fsc = new ForegroundServiceControllerImpl(mContext);
-    }
-
-    @Test
-    public void testNotificationCRUD_dungeon() {
-        StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, "com.example.app1");
-        StatusBarNotification sbn_user2_app2_fg = makeMockFgSBN(USERID_TWO, "com.example.app2");
-        StatusBarNotification sbn_user1_app3_fg = makeMockFgSBN(USERID_ONE, "com.example.app3");
-        StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
-                5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-        StatusBarNotification sbn_user2_app1 = makeMockSBN(USERID_TWO, "com.example.app1",
-                5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-
-        assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
-        assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.addNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.addNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.addNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_DEFAULT);
-
-        // these are never added to the tracker
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.updateNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.updateNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_DEFAULT);
-        // should still not be there
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.updateNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.updateNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
-
-        assertTrue(fsc.removeNotification(sbn_user1_app3_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
-
-        assertTrue(fsc.removeNotification(sbn_user2_app2_fg));
-        assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
-
-        assertTrue(fsc.removeNotification(sbn_user1_app1_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
-
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-    }
-
-    @Test
-    public void testNotificationCRUD_stdLayout() {
-        StatusBarNotification sbn_user1_app1_fg =
-                makeMockFgSBN(USERID_ONE, "com.example.app1", 0, true);
-        StatusBarNotification sbn_user2_app2_fg =
-                makeMockFgSBN(USERID_TWO, "com.example.app2", 1, true);
-        StatusBarNotification sbn_user1_app3_fg =
-                makeMockFgSBN(USERID_ONE, "com.example.app3", 2, true);
-        StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
-                5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-        StatusBarNotification sbn_user2_app1 = makeMockSBN(USERID_TWO, "com.example.app1",
-                5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-
-        assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
-        assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
-        fsc.addNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_MIN);
-        fsc.addNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_MIN);
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
-        fsc.addNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_MIN);
-
-        // these are never added to the tracker
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.updateNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
-        fsc.updateNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_MIN);
-        // should still not be there
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
-
-        fsc.updateNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_MIN);
-        fsc.updateNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_MIN);
-        fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
-
-        assertTrue(fsc.removeNotification(sbn_user1_app3_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app3_fg));
-
-        assertTrue(fsc.removeNotification(sbn_user2_app2_fg));
-        assertFalse(fsc.removeNotification(sbn_user2_app2_fg));
-
-        assertTrue(fsc.removeNotification(sbn_user1_app1_fg));
-        assertFalse(fsc.removeNotification(sbn_user1_app1_fg));
-
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.removeNotification(sbn_user2_app1));
+        mFsc = new ForegroundServiceController();
+        NotificationEntryManager notificationEntryManager = mock(NotificationEntryManager.class);
+        mListener = new ForegroundServiceNotificationListener(
+                mContext, mFsc, notificationEntryManager);
+        ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
+                ArgumentCaptor.forClass(NotificationEntryListener.class);
+        verify(notificationEntryManager).addNotificationEntryListener(
+                entryListenerCaptor.capture());
+        mEntryListener = entryListenerCaptor.getValue();
     }
 
     @Test
     public void testAppOpsCRUD() {
         // no crash on remove that doesn't exist
-        fsc.onAppOpChanged(9, 1000, "pkg1", false);
-        assertNull(fsc.getAppOps(0, "pkg1"));
+        mFsc.onAppOpChanged(9, 1000, "pkg1", false);
+        assertNull(mFsc.getAppOps(0, "pkg1"));
 
         // multiuser & multipackage
-        fsc.onAppOpChanged(8, 50, "pkg1", true);
-        fsc.onAppOpChanged(1, 60, "pkg3", true);
-        fsc.onAppOpChanged(7, 500000, "pkg2", true);
+        mFsc.onAppOpChanged(8, 50, "pkg1", true);
+        mFsc.onAppOpChanged(1, 60, "pkg3", true);
+        mFsc.onAppOpChanged(7, 500000, "pkg2", true);
 
-        assertEquals(1, fsc.getAppOps(0, "pkg1").size());
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
+        assertEquals(1, mFsc.getAppOps(0, "pkg1").size());
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(8));
 
-        assertEquals(1, fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
-        assertTrue(fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
+        assertEquals(1, mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
+        assertTrue(mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
 
-        assertEquals(1, fsc.getAppOps(0, "pkg3").size());
-        assertTrue(fsc.getAppOps(0, "pkg3").contains(1));
+        assertEquals(1, mFsc.getAppOps(0, "pkg3").size());
+        assertTrue(mFsc.getAppOps(0, "pkg3").contains(1));
 
         // multiple ops for the same package
-        fsc.onAppOpChanged(9, 50, "pkg1", true);
-        fsc.onAppOpChanged(5, 50, "pkg1", true);
+        mFsc.onAppOpChanged(9, 50, "pkg1", true);
+        mFsc.onAppOpChanged(5, 50, "pkg1", true);
 
-        assertEquals(3, fsc.getAppOps(0, "pkg1").size());
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(9));
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(5));
+        assertEquals(3, mFsc.getAppOps(0, "pkg1").size());
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(8));
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(9));
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(5));
 
-        assertEquals(1, fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
-        assertTrue(fsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
+        assertEquals(1, mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
+        assertTrue(mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
 
         // remove one of the multiples
-        fsc.onAppOpChanged(9, 50, "pkg1", false);
-        assertEquals(2, fsc.getAppOps(0, "pkg1").size());
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(8));
-        assertTrue(fsc.getAppOps(0, "pkg1").contains(5));
+        mFsc.onAppOpChanged(9, 50, "pkg1", false);
+        assertEquals(2, mFsc.getAppOps(0, "pkg1").size());
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(8));
+        assertTrue(mFsc.getAppOps(0, "pkg1").contains(5));
 
         // remove last op
-        fsc.onAppOpChanged(1, 60, "pkg3", false);
-        assertNull(fsc.getAppOps(0, "pkg3"));
+        mFsc.onAppOpChanged(1, 60, "pkg3", false);
+        assertNull(mFsc.getAppOps(0, "pkg3"));
     }
 
     @Test
-    public void testDungeonPredicate() {
+    public void testDisclosurePredicate() {
         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
                 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-        StatusBarNotification sbn_user1_dungeon = makeMockSBN(USERID_ONE, "android",
+        StatusBarNotification sbn_user1_disclosure = makeMockSBN(USERID_ONE, "android",
                 SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES,
                 null, Notification.FLAG_NO_CLEAR);
 
-        assertTrue(fsc.isDungeonNotification(sbn_user1_dungeon));
-        assertFalse(fsc.isDungeonNotification(sbn_user1_app1));
+        assertTrue(mFsc.isDisclosureNotification(sbn_user1_disclosure));
+        assertFalse(mFsc.isDisclosureNotification(sbn_user1_app1));
     }
 
     @Test
-    public void testDungeonCRUD() {
-        StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
-                5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-        StatusBarNotification sbn_user1_dungeon = makeMockSBN(USERID_ONE, "android",
-                SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES,
-                null, Notification.FLAG_NO_CLEAR);
-
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
-        fsc.addNotification(sbn_user1_dungeon, NotificationManager.IMPORTANCE_DEFAULT);
-
-        fsc.removeNotification(sbn_user1_dungeon);
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-    }
-
-    @Test
-    public void testNeedsDungeonAfterRemovingUnrelatedNotification() {
+    public void testNeedsDisclosureAfterRemovingUnrelatedNotification() {
         final String PKG1 = "com.example.app100";
 
         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1,
@@ -233,21 +132,21 @@
         StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1);
 
         // first add a normal notification
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
         // nothing required yet
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
         // now the app starts a fg service
-        fsc.addNotification(makeMockDungeon(USERID_ONE, new String[]{ PKG1 }),
+        entryAdded(makeMockDisclosure(USERID_ONE, new String[]{PKG1}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
         // add the fg notification
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // app1 has got it covered
+        entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has got it covered
         // remove the boring notification
-        fsc.removeNotification(sbn_user1_app1);
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // app1 has STILL got it covered
-        assertTrue(fsc.removeNotification(sbn_user1_app1_fg));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
+        entryRemoved(sbn_user1_app1);
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has STILL got it covered
+        entryRemoved(sbn_user1_app1_fg);
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
     }
 
     @Test
@@ -257,115 +156,117 @@
 
         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1,
                 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
 
         // no services are "running"
-        fsc.addNotification(makeMockDungeon(USERID_ONE, null),
+        entryAdded(makeMockDisclosure(USERID_ONE, null),
                 NotificationManager.IMPORTANCE_DEFAULT);
 
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
-        fsc.updateNotification(makeMockDungeon(USERID_ONE, new String[]{PKG1}),
+        entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG1}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
         // switch to different package
-        fsc.updateNotification(makeMockDungeon(USERID_ONE, new String[]{PKG2}),
+        entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG2}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
-        fsc.updateNotification(makeMockDungeon(USERID_TWO, new String[]{PKG1}),
+        entryUpdated(makeMockDisclosure(USERID_TWO, new String[]{PKG1}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_TWO)); // finally user2 needs one too
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO)); // finally user2 needs one too
 
-        fsc.updateNotification(makeMockDungeon(USERID_ONE, new String[]{PKG2, PKG1}),
+        entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG2, PKG1}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
-        fsc.removeNotification(makeMockDungeon(USERID_ONE, null /*unused*/));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryRemoved(makeMockDisclosure(USERID_ONE, null /*unused*/));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
-        fsc.removeNotification(makeMockDungeon(USERID_TWO, null /*unused*/));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryRemoved(makeMockDisclosure(USERID_TWO, null /*unused*/));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
     }
 
     @Test
-    public void testDungeonBasic() {
+    public void testDisclosureBasic() {
         final String PKG1 = "com.example.app0";
 
         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1,
                 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
         StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1);
 
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); // not fg
-        fsc.addNotification(makeMockDungeon(USERID_ONE, new String[]{ PKG1 }),
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); // not fg
+        entryAdded(makeMockDisclosure(USERID_ONE, new String[]{PKG1}),
                 NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // app1 has got it covered
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
+        entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has got it covered
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
         // let's take out the other notification and see what happens.
 
-        fsc.removeNotification(sbn_user1_app1);
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // still covered by sbn_user1_app1_fg
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryRemoved(sbn_user1_app1);
+        assertFalse(
+                mFsc.isDisclosureNeededForUser(USERID_ONE)); // still covered by sbn_user1_app1_fg
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
         // let's attempt to downgrade the notification from FLAG_FOREGROUND and see what we get
         StatusBarNotification sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1);
         sbn_user1_app1_fg_sneaky.getNotification().flags = 0;
-        fsc.updateNotification(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryUpdated(sbn_user1_app1_fg_sneaky,
+                NotificationManager.IMPORTANCE_DEFAULT);
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
         // ok, ok, we'll put it back
         sbn_user1_app1_fg_sneaky.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
-        fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryUpdated(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
-        assertTrue(fsc.removeNotification(sbn_user1_app1_fg_sneaky));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required!
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryRemoved(sbn_user1_app1_fg_sneaky);
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
 
         // now let's test an upgrade
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
         sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
-        fsc.updateNotification(sbn_user1_app1,
+        entryUpdated(sbn_user1_app1,
                 NotificationManager.IMPORTANCE_DEFAULT); // this is now a fg notification
 
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
 
         // remove it, make sure we're out of compliance again
-        assertTrue(fsc.removeNotification(sbn_user1_app1)); // was fg, should return true
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
+        entryRemoved(sbn_user1_app1); // was fg, should return true
+        entryRemoved(sbn_user1_app1);
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
 
         // importance upgrade
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
-        assertTrue(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+        assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
         sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
-        fsc.updateNotification(sbn_user1_app1_fg,
+        entryUpdated(sbn_user1_app1_fg,
                 NotificationManager.IMPORTANCE_DEFAULT); // this is now a fg notification
 
         // finally, let's turn off the service
-        fsc.addNotification(makeMockDungeon(USERID_ONE, null),
+        entryAdded(makeMockDisclosure(USERID_ONE, null),
                 NotificationManager.IMPORTANCE_DEFAULT);
 
-        assertFalse(fsc.isDungeonNeededForUser(USERID_ONE));
-        assertFalse(fsc.isDungeonNeededForUser(USERID_TWO));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
+        assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
     }
 
     @Test
@@ -375,8 +276,8 @@
         StatusBarNotification sbn_user1_overlay = makeMockSBN(USERID_ONE, "android",
                 0, "AlertWindowNotification", Notification.FLAG_NO_CLEAR);
 
-        assertTrue(fsc.isSystemAlertNotification(sbn_user1_overlay));
-        assertFalse(fsc.isSystemAlertNotification(sbn_user1_app1));
+        assertTrue(mFsc.isSystemAlertNotification(sbn_user1_overlay));
+        assertFalse(mFsc.isSystemAlertNotification(sbn_user1_app1));
     }
 
     @Test
@@ -386,54 +287,54 @@
         StatusBarNotification sbn_user1_app1 = makeMockFgSBN(USERID_ONE, PKG1, 0, true);
         sbn_user1_app1.getNotification().flags = 0;
         StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1, 1, true);
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN); // not fg
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
-        fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // app1 has got it covered
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "otherpkg"));
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN); // not fg
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+        entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // app1 has got it covered
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "otherpkg"));
         // let's take out the non-fg notification and see what happens.
-        fsc.removeNotification(sbn_user1_app1);
+        entryRemoved(sbn_user1_app1);
         // still covered by sbn_user1_app1_fg
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anyPkg"));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "anyPkg"));
 
         // let's attempt to downgrade the notification from FLAG_FOREGROUND and see what we get
         StatusBarNotification sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1, 1, true);
         sbn_user1_app1_fg_sneaky.getNotification().flags = 0;
-        fsc.updateNotification(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
+        entryUpdated(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
         // ok, ok, we'll put it back
         sbn_user1_app1_fg_sneaky.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
-        fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "whatever"));
+        entryUpdated(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "whatever"));
 
-        assertTrue(fsc.removeNotification(sbn_user1_app1_fg_sneaky));
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "a"));
+        entryRemoved(sbn_user1_app1_fg_sneaky);
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "a"));
 
         // let's try a custom layout
         sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1, 1, false);
-        fsc.updateNotification(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
+        entryUpdated(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_MIN);
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1)); // should be required!
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "anything"));
         // now let's test an upgrade (non fg to fg)
-        fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, "b"));
+        entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_MIN);
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, "b"));
         sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
-        fsc.updateNotification(sbn_user1_app1,
+        entryUpdated(sbn_user1_app1,
                 NotificationManager.IMPORTANCE_MIN); // this is now a fg notification
 
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
 
         // remove it, make sure we're out of compliance again
-        assertTrue(fsc.removeNotification(sbn_user1_app1)); // was fg, should return true
-        assertFalse(fsc.removeNotification(sbn_user1_app1));
-        assertFalse(fsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
-        assertTrue(fsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
+        entryRemoved(sbn_user1_app1); // was fg, should return true
+        entryRemoved(sbn_user1_app1);
+        assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_TWO, PKG1));
+        assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
     }
 
     private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag,
@@ -475,7 +376,7 @@
         return makeMockSBN(userid, pkg, 1000, "foo", Notification.FLAG_FOREGROUND_SERVICE);
     }
 
-    private StatusBarNotification makeMockDungeon(int userid, String[] pkgs) {
+    private StatusBarNotification makeMockDisclosure(int userid, String[] pkgs) {
         final Notification n = mock(Notification.class);
         n.flags = Notification.FLAG_ONGOING_EVENT;
         final Bundle extras = new Bundle();
@@ -488,4 +389,21 @@
         sbn.getNotification().extras = extras;
         return sbn;
     }
+
+    private void entryRemoved(StatusBarNotification notification) {
+        mEntryListener.onEntryRemoved(new NotificationEntry(notification),
+                null, false);
+    }
+
+    private void entryAdded(StatusBarNotification notification, int importance) {
+        NotificationEntry entry = new NotificationEntry(notification);
+        entry.importance = importance;
+        mEntryListener.onPendingEntryAdded(entry);
+    }
+
+    private void entryUpdated(StatusBarNotification notification, int importance) {
+        NotificationEntry entry = new NotificationEntry(notification);
+        entry.importance = importance;
+        mEntryListener.onEntryUpdated(entry);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 8f2b2d0..21d3652 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -18,6 +18,10 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.IActivityManager;
 import android.content.Context;
@@ -29,6 +33,9 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
@@ -36,6 +43,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -45,6 +54,8 @@
 public class BubbleControllerTest extends SysuiTestCase {
 
     @Mock
+    private NotificationEntryManager mNotificationEntryManager;
+    @Mock
     private WindowManager mWindowManager;
     @Mock
     private IActivityManager mActivityManager;
@@ -52,17 +63,23 @@
     private DozeParameters mDozeParameters;
     @Mock
     private FrameLayout mStatusBarView;
+    @Captor
+    private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
 
     private TestableBubbleController mBubbleController;
     private StatusBarWindowController mStatusBarWindowController;
+    private NotificationEntryListener mEntryListener;
 
     private NotificationTestHelper mNotificationTestHelper;
     private ExpandableNotificationRow mRow;
     private ExpandableNotificationRow mRow2;
 
+    private final NotificationData mNotificationData = new NotificationData();
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager);
 
         // Bubbles get added to status bar window view
         mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
@@ -74,7 +91,15 @@
         mRow = mNotificationTestHelper.createBubble();
         mRow2 = mNotificationTestHelper.createBubble();
 
+        // Return non-null notification data from the NEM
+        when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
+
         mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController);
+
+        // Get a reference to the BubbleController's entry listener
+        verify(mNotificationEntryManager, atLeastOnce())
+                .addNotificationEntryListener(mEntryListenerCaptor.capture());
+        mEntryListener = mEntryListenerCaptor.getValue();
     }
 
     @Test
@@ -102,6 +127,8 @@
 
         mBubbleController.removeBubble(mRow.getEntry().key);
         assertFalse(mStatusBarWindowController.getBubblesShowing());
+        assertTrue(mRow.getEntry().isBubbleDismissed());
+        verify(mNotificationEntryManager).updateNotifications();
     }
 
     @Test
@@ -112,6 +139,7 @@
 
         mBubbleController.dismissStack();
         assertFalse(mStatusBarWindowController.getBubblesShowing());
+        verify(mNotificationEntryManager, times(3)).updateNotifications();
     }
 
     @Test
@@ -140,6 +168,12 @@
         assertFalse(mBubbleController.isStackExpanded());
     }
 
+    @Test
+    public void testMarkNewNotificationAsBubble() {
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        assertTrue(mRow.getEntry().isBubble());
+    }
+
     static class TestableBubbleController extends BubbleController {
 
         TestableBubbleController(Context context,
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 a8ff0b2..cfc19ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
@@ -44,6 +45,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarStateController;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -67,6 +69,8 @@
     private AlarmManager mAlarmManager;
     @Mock
     private NotificationMediaManager mNotificationMediaManager;
+    @Mock
+    private StatusBarStateController mStatusBarStateController;
     private TestableKeyguardSliceProvider mProvider;
     private boolean mIsZenMode;
 
@@ -76,7 +80,7 @@
         mIsZenMode = false;
         mProvider = new TestableKeyguardSliceProvider();
         mProvider.attachInfo(getContext(), null);
-        mProvider.initDependencies();
+        mProvider.initDependencies(mNotificationMediaManager, mStatusBarStateController);
         SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST)));
     }
 
@@ -98,6 +102,7 @@
     @Test
     public void onBindSlice_readsMedia() {
         MediaMetadata metadata = mock(MediaMetadata.class);
+        mProvider.onDozingChanged(true);
         mProvider.onMetadataChanged(metadata);
         mProvider.onBindSlice(mProvider.getUri());
         verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE));
@@ -162,7 +167,31 @@
     @Test
     public void onMetadataChanged_updatesSlice() {
         mProvider.onMetadataChanged(mock(MediaMetadata.class));
+        mProvider.onDozingChanged(true);
         verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+        // Hides after waking up
+        reset(mContentResolver);
+        mProvider.onDozingChanged(false);
+        verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+        // And won't update slice if device is awake
+        reset(mContentResolver);
+        mProvider.onMetadataChanged(mock(MediaMetadata.class));
+        verify(mContentResolver, never()).notifyChange(eq(mProvider.getUri()), eq(null));
+    }
+
+    @Test
+    public void onDozingChanged_updatesSliceIfMedia() {
+        // Show media when dozing
+        mProvider.onMetadataChanged(mock(MediaMetadata.class));
+        mProvider.onDozingChanged(true);
+        verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+        // Do not notify again if nothing changed
+        reset(mContentResolver);
+        mProvider.onDozingChanged(true);
+        verify(mContentResolver, never()).notifyChange(eq(mProvider.getUri()), eq(null));
     }
 
     private class TestableKeyguardSliceProvider extends KeyguardSliceProvider {
@@ -201,11 +230,6 @@
         protected String getFormattedDateLocked() {
             return super.getFormattedDateLocked() + mCounter++;
         }
-
-        @Override
-        public void initDependencies() {
-            mMediaManager = mNotificationMediaManager;
-        }
     }
 
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 2cb326e..823485f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -39,6 +39,7 @@
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -92,7 +93,8 @@
         processAllMessages();
         QSTileHost host = new QSTileHost(mContext, mock(StatusBarIconController.class),
                 mock(QSFactoryImpl.class), new Handler(), Looper.myLooper(),
-                mock(PluginManager.class), mock(TunerService.class));
+                mock(PluginManager.class), mock(TunerService.class),
+                () -> mock(AutoTileManager.class));
         qs.setHost(host);
 
         qs.setListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 63f4bbc..03278b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -33,6 +33,7 @@
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.tuner.TunerService;
@@ -65,7 +66,8 @@
                 new Handler(),
                 Looper.myLooper(),
                 mock(PluginManager.class),
-                mock(TunerService.class));
+                mock(TunerService.class),
+                () -> mock(AutoTileManager.class));
         mTileService = new TestTileServices(host, Looper.getMainLooper());
     }
 
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 9d0556f..f8957b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -36,7 +36,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 
@@ -67,7 +67,7 @@
 
     private AlertingNotificationManager mAlertingNotificationManager;
 
-    protected NotificationData.Entry mEntry;
+    protected NotificationEntry mEntry;
     protected Handler mTestHandler;
     private StatusBarNotification mSbn;
     protected boolean mTimedOut = false;
@@ -119,7 +119,7 @@
     public void setUp() {
         mTestHandler = Handler.createAsync(Looper.myLooper());
         mSbn = createNewNotification(0 /* id */);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(mSbn);
         mEntry.setRow(mRow);
 
         mAlertingNotificationManager = createAlertingNotificationManager();
@@ -170,7 +170,7 @@
     public void testReleaseAllImmediately() {
         for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
             StatusBarNotification sbn = createNewNotification(i);
-            NotificationData.Entry entry = new NotificationData.Entry(sbn);
+            NotificationEntry entry = new NotificationEntry(sbn);
             entry.setRow(mRow);
             mAlertingNotificationManager.showNotification(entry);
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index a04e57b..51d94f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -16,6 +16,7 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -42,10 +43,10 @@
 
     @Before
     public void setup() {
-        mCommandQueue = new CommandQueue();
+        mCommandQueue = new CommandQueue(mContext);
         mCallbacks = mock(Callbacks.class);
         mCommandQueue.addCallback(mCallbacks);
-        verify(mCallbacks).disable(eq(0), eq(0), eq(false));
+        verify(mCallbacks).disable(anyInt(), eq(0), eq(0), eq(false));
     }
 
     @After
@@ -73,7 +74,7 @@
         int state2 = 42;
         mCommandQueue.disable(DEFAULT_DISPLAY, state1, state2);
         waitForIdleSync();
-        verify(mCallbacks).disable(eq(state1), eq(state2), eq(true));
+        verify(mCallbacks).disable(eq(DEFAULT_DISPLAY), eq(state1), eq(state2), eq(true));
     }
 
     @Test
@@ -104,7 +105,8 @@
         Rect r = new Rect();
         mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r);
         waitForIdleSync();
-        verify(mCallbacks).setSystemUiVisibility(eq(1), eq(2), eq(3), eq(4), eq(null), eq(r));
+        verify(mCallbacks).setSystemUiVisibility(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(3), eq(4),
+                eq(null), eq(r));
     }
 
     // TODO(b/117478341): add test case for multi-display
@@ -112,7 +114,7 @@
     public void testTopAppWindowChanged() {
         mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true);
         waitForIdleSync();
-        verify(mCallbacks).topAppWindowChanged(eq(true));
+        verify(mCallbacks).topAppWindowChanged(eq(DEFAULT_DISPLAY), eq(true));
     }
 
     // TODO(b/117478341): add test case for multi-display
@@ -120,7 +122,8 @@
     public void testShowImeButton() {
         mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
         waitForIdleSync();
-        verify(mCallbacks).setImeWindowStatus(eq(null), eq(1), eq(2), eq(true));
+        verify(mCallbacks).setImeWindowStatus(
+                eq(DEFAULT_DISPLAY), eq(null), eq(1), eq(2), eq(true));
     }
 
     @Test
@@ -177,7 +180,7 @@
     public void testSetWindowState() {
         mCommandQueue.setWindowState(DEFAULT_DISPLAY, 1, 2);
         waitForIdleSync();
-        verify(mCallbacks).setWindowState(eq(1), eq(2));
+        verify(mCallbacks).setWindowState(eq(DEFAULT_DISPLAY), eq(1), eq(2));
     }
 
     @Test
@@ -192,7 +195,7 @@
     public void testAppTransitionPending() {
         mCommandQueue.appTransitionPending(DEFAULT_DISPLAY);
         waitForIdleSync();
-        verify(mCallbacks).appTransitionPending(eq(false));
+        verify(mCallbacks).appTransitionPending(eq(DEFAULT_DISPLAY), eq(false));
     }
 
     // TODO(b/117478341): add test case for multi-display
@@ -200,7 +203,7 @@
     public void testAppTransitionCancelled() {
         mCommandQueue.appTransitionCancelled(DEFAULT_DISPLAY);
         waitForIdleSync();
-        verify(mCallbacks).appTransitionCancelled();
+        verify(mCallbacks).appTransitionCancelled(eq(DEFAULT_DISPLAY));
     }
 
     // TODO(b/117478341): add test case for multi-display
@@ -208,7 +211,8 @@
     public void testAppTransitionStarting() {
         mCommandQueue.appTransitionStarting(DEFAULT_DISPLAY, 1, 2);
         waitForIdleSync();
-        verify(mCallbacks).appTransitionStarting(eq(1L), eq(2L), eq(false));
+        verify(mCallbacks).appTransitionStarting(
+                eq(DEFAULT_DISPLAY), eq(1L), eq(2L), eq(false));
     }
 
     // TODO(b/117478341): add test case for multi-display
@@ -216,7 +220,7 @@
     public void testAppTransitionFinished() {
         mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
         waitForIdleSync();
-        verify(mCallbacks).appTransitionFinished();
+        verify(mCallbacks).appTransitionFinished(eq(DEFAULT_DISPLAY));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 65c04fe..c880172 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -31,8 +31,9 @@
 
 import com.android.systemui.Dependency;
 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.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -85,7 +86,7 @@
 
     @Test
     public void testNotificationUpdateCallsUpdateNotification() {
-        when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationData.Entry(mSbn));
+        when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationEntry(mSbn));
         mListener.onNotificationPosted(mSbn, mRanking);
         TestableLooper.get(this).processAllMessages();
         verify(mEntryManager).updateNotification(mSbn, mRanking);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index f168a49..d7ca5b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
 import static android.content.Intent.ACTION_USER_SWITCHED;
 
 import static junit.framework.Assert.assertFalse;
@@ -42,8 +41,8 @@
 
 import com.android.systemui.Dependency;
 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.collection.NotificationData;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
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 150dcb9..c159516 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -24,8 +24,8 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputActiveExtender;
 import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputHistoryExtender;
 import com.android.systemui.statusbar.NotificationRemoteInputManager.SmartReplyHistoryExtender;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.ShadeController;
 
@@ -60,7 +60,7 @@
 
     private TestableNotificationRemoteInputManager mRemoteInputManager;
     private StatusBarNotification mSbn;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private RemoteInputHistoryExtender mRemoteInputHistoryExtender;
     private SmartReplyHistoryExtender mSmartReplyHistoryExtender;
     private RemoteInputActiveExtender mRemoteInputActiveExtender;
@@ -75,7 +75,7 @@
                 Handler.createAsync(Looper.myLooper()));
         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 = new NotificationEntry(mSbn);
         mEntry.setRow(mRow);
 
         mRemoteInputManager.setUpWithPresenterForTest(mCallback,
@@ -170,7 +170,7 @@
         // Setup a notification entry with 1 remote input.
         StatusBarNotification newSbn =
                 mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
-        NotificationData.Entry entry = new NotificationData.Entry(newSbn);
+        NotificationEntry entry = new NotificationEntry(newSbn);
 
         // Try rebuilding to add another reply.
         newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true);
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 fb5e875..529da82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -32,7 +32,7 @@
 import android.widget.RemoteViews;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
@@ -238,7 +238,7 @@
                 mUser,
                 null /* overrideGroupKey */,
                 System.currentTimeMillis());
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.setRow(row);
         entry.createIcons(mContext, sbn);
         entry.channel = new NotificationChannel(
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 8cf4b05..bf91305 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -38,10 +38,10 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.bubbles.BubbleController;
 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.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -77,7 +77,7 @@
     @Mock private ShadeController mShadeController;
 
     private NotificationViewHierarchyManager mViewHierarchyManager;
-    private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);
+    private NotificationTestHelper mHelper;
 
     @Before
     public void setUp() {
@@ -90,6 +90,8 @@
         mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
         mDependency.injectTestDependency(ShadeController.class, mShadeController);
 
+        mHelper = new NotificationTestHelper(mContext);
+
         when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
 
         mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
@@ -100,9 +102,9 @@
         mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
     }
 
-    private NotificationData.Entry createEntry() throws Exception {
+    private NotificationEntry createEntry() throws Exception {
         ExpandableNotificationRow row = mHelper.createRow();
-        NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification());
+        NotificationEntry entry = new NotificationEntry(row.getStatusBarNotification());
         entry.setRow(row);
         return entry;
     }
@@ -111,9 +113,9 @@
     public void testNotificationsBecomingBundled() throws Exception {
         // Tests 3 top level notifications becoming a single bundled notification with |entry0| as
         // the summary.
-        NotificationData.Entry entry0 = createEntry();
-        NotificationData.Entry entry1 = createEntry();
-        NotificationData.Entry entry2 = createEntry();
+        NotificationEntry entry0 = createEntry();
+        NotificationEntry entry1 = createEntry();
+        NotificationEntry entry2 = createEntry();
 
         // Set up the prior state to look like three top level notifications.
         mListContainer.addContainerView(entry0.getRow());
@@ -140,9 +142,9 @@
     @Test
     public void testNotificationsBecomingUnbundled() throws Exception {
         // Tests a bundled notification becoming three top level notifications.
-        NotificationData.Entry entry0 = createEntry();
-        NotificationData.Entry entry1 = createEntry();
-        NotificationData.Entry entry2 = createEntry();
+        NotificationEntry entry0 = createEntry();
+        NotificationEntry entry1 = createEntry();
+        NotificationEntry entry2 = createEntry();
         entry0.getRow().addChildNotification(entry1.getRow());
         entry0.getRow().addChildNotification(entry2.getRow());
 
@@ -171,8 +173,8 @@
     @Test
     public void testNotificationsBecomingSuppressed() throws Exception {
         // Tests two top level notifications becoming a suppressed summary and a child.
-        NotificationData.Entry entry0 = createEntry();
-        NotificationData.Entry entry1 = createEntry();
+        NotificationEntry entry0 = createEntry();
+        NotificationEntry entry1 = createEntry();
         entry0.getRow().addChildNotification(entry1.getRow());
 
         // Set up the prior state to look like a top level notification.
@@ -197,7 +199,7 @@
 
     @Test
     public void testUpdateNotificationViews_appOps() throws Exception {
-        NotificationData.Entry entry0 = createEntry();
+        NotificationEntry entry0 = createEntry();
         entry0.setRow(spy(entry0.getRow()));
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0));
@@ -262,10 +264,7 @@
         public void setMaxDisplayedNotifications(int maxNotifications) {}
 
         @Override
-        public void snapViewIfNeeded(Entry entry) {}
-
-        @Override
-        public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
+        public ViewGroup getViewParentForNotification(NotificationEntry entry) {
             return null;
         }
 
@@ -281,10 +280,10 @@
         }
 
         @Override
-        public void cleanUpViewStateForEntry(Entry entry) { }
+        public void cleanUpViewStateForEntry(NotificationEntry entry) { }
 
         @Override
-        public boolean isInVisibleLocation(Entry entry) {
+        public boolean isInVisibleLocation(NotificationEntry entry) {
             return true;
         }
 
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 e7a1f05..6cca434 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -36,8 +36,8 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.R;
 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.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.ShadeController;
 
 import org.junit.Before;
@@ -58,7 +58,7 @@
     private static final int TEST_ACTION_COUNT = 3;
 
     private Notification mNotification;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private SmartReplyController mSmartReplyController;
     private NotificationRemoteInputManager mRemoteInputManager;
 
@@ -92,7 +92,7 @@
 
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, mNotification, new UserHandle(ActivityManager.getCurrentUser()), null, 0);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(mSbn);
     }
 
     @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 a2d408e..4d22536 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
@@ -65,7 +65,8 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationInflater;
@@ -121,8 +122,9 @@
     @Mock private MetricsLogger mMetricsLogger;
     @Mock private SmartReplyController mSmartReplyController;
     @Mock private RowInflaterTask mAsyncInflationTask;
+    @Mock private NotificationRowBinder mMockedRowBinder;
 
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private StatusBarNotification mSbn;
     private TestableNotificationEntryManager mEntryManager;
     private CountDownLatch mCountDownLatch;
@@ -136,7 +138,7 @@
         }
 
         @Override
-        public void onAsyncInflationFinished(NotificationData.Entry entry,
+        public void onAsyncInflationFinished(NotificationEntry entry,
                 @NotificationInflater.InflationFlag int inflatedFlags) {
             super.onAsyncInflationFinished(entry, inflatedFlags);
 
@@ -221,7 +223,7 @@
                 .setContentText("Text");
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
-        mEntry = new NotificationData.Entry(mSbn);
+        mEntry = new NotificationEntry(mSbn);
         mEntry.expandedIcon = mock(StatusBarIconView.class);
 
         mEntryManager = new TestableNotificationEntryManager(mContext);
@@ -256,13 +258,12 @@
 
         // Check that no inflation error occurred.
         verify(mEntryListener, never()).onInflationError(any(), any());
-        verify(mForegroundServiceController).addNotification(eq(mSbn), anyInt());
 
         // Row inflation:
-        ArgumentCaptor<NotificationData.Entry> entryCaptor = ArgumentCaptor.forClass(
-                NotificationData.Entry.class);
+        ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
+                NotificationEntry.class);
         verify(mBindCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
-        NotificationData.Entry entry = entryCaptor.getValue();
+        NotificationEntry entry = entryCaptor.getValue();
         verify(mRemoteInputManager).bindRow(entry.getRow());
 
         // Row content inflation:
@@ -292,7 +293,6 @@
         verify(mEntryListener, never()).onInflationError(any(), any());
 
         verify(mPresenter).updateNotificationViews();
-        verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
         verify(mEntryListener).onEntryUpdated(mEntry);
         assertNotNull(mEntry.getRow());
         assertEquals(mEntry.userSentiment,
@@ -310,11 +310,10 @@
 
         verify(mEntryListener, never()).onInflationError(any(), any());
 
-        verify(mForegroundServiceController).removeNotification(mSbn);
         verify(mListContainer).cleanUpViewStateForEntry(mEntry);
         verify(mPresenter).updateNotificationViews();
-        verify(mEntryListener).onEntryRemoved(mEntry, mSbn.getKey(), mSbn,
-                null, false /* lifetimeExtended */, false /* removedByUser */);
+        verify(mEntryListener).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
         verify(mRow).setRemoved();
 
         assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
@@ -338,8 +337,31 @@
 
         assertNotNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
         verify(extender).setShouldManageLifetime(mEntry, true /* shouldManage */);
-        verify(mEntryListener).onEntryRemoved(mEntry, mSbn.getKey(), null,
-                null, true /* lifetimeExtended */, false /* removedByUser */);
+        verify(mEntryListener, never()).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
+    }
+
+    @Test
+    public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
+        com.android.systemui.util.Assert.isNotMainThread();
+
+        mEntryManager.removeNotification("not_a_real_key", mRankingMap);
+
+        verify(mEntryListener, never()).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
+    }
+
+    @Test
+    public void testRemoveNotification_whilePending() throws InterruptedException {
+        com.android.systemui.util.Assert.isNotMainThread();
+
+        mEntryManager.setRowBinder(mMockedRowBinder);
+
+        mEntryManager.addNotification(mSbn, mRankingMap);
+        mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
+
+        verify(mEntryListener, never()).onEntryRemoved(
+                mEntry, null, false /* removedByUser */);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index da8bc01d..387475f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -40,6 +40,8 @@
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -191,12 +193,12 @@
 
         // test should filter out hidden notifications:
         // hidden
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         entry.suspended = true;
         assertTrue(mNotificationFilter.shouldFilterOut(entry));
 
         // not hidden
-        entry = new NotificationData.Entry(mMockStatusBarNotification);
+        entry = new NotificationEntry(mMockStatusBarNotification);
         entry.suspended = false;
         assertFalse(mNotificationFilter.shouldFilterOut(entry));
     }
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 6fbd8c7..813fc9d 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
@@ -29,6 +29,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import org.junit.Before;
@@ -44,13 +45,13 @@
     private VisualStabilityManager.Callback mCallback = mock(VisualStabilityManager.Callback.class);
     private VisibilityLocationProvider mLocationProvider = mock(VisibilityLocationProvider.class);
     private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class);
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
 
     @Before
     public void setUp() {
         mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class));
         mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
-        mEntry = new NotificationData.Entry(mock(StatusBarNotification.class));
+        mEntry = new NotificationEntry(mock(StatusBarNotification.class));
         mEntry.setRow(mRow);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 871ff89..193f483 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.collection;
 
 import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER;
 import static android.app.AppOpsManager.OP_CAMERA;
@@ -59,7 +59,9 @@
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -203,7 +205,7 @@
                 mRow.getEntry().notification)).thenReturn(false);
         when(mEnvironment.isNotificationForCurrentProfiles(
                 row2.getEntry().notification)).thenReturn(true);
-        ArrayList<NotificationData.Entry> result =
+        ArrayList<NotificationEntry> result =
                 mNotificationData.getNotificationsForCurrentUser();
 
         assertEquals(result.size(), 1);
@@ -217,7 +219,7 @@
                 TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
         Notification n = mMockStatusBarNotification.getNotification();
         n.flags = Notification.FLAG_FOREGROUND_SERVICE;
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         mNotificationData.add(entry);
 
         assertTrue(entry.isExemptFromDndVisualSuppression());
@@ -234,7 +236,7 @@
         nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
         n = nb.build();
         when(mMockStatusBarNotification.getNotification()).thenReturn(n);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         mNotificationData.add(entry);
 
         assertTrue(entry.isExemptFromDndVisualSuppression());
@@ -246,7 +248,7 @@
         initStatusBarNotification(false);
         when(mMockStatusBarNotification.getKey()).thenReturn(
                 TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         entry.mIsSystemNotification = true;
         mNotificationData.add(entry);
 
@@ -259,7 +261,7 @@
         initStatusBarNotification(false);
         when(mMockStatusBarNotification.getKey()).thenReturn(
                 TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
-        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+        NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
         entry.mIsSystemNotification = true;
         mNotificationData.add(entry);
 
@@ -313,8 +315,8 @@
         snoozeCriterions.add(snoozeCriterion);
         when(ranking.getSnoozeCriteria()).thenReturn(snoozeCriterions);
 
-        NotificationData.Entry entry =
-                new NotificationData.Entry(mMockStatusBarNotification, ranking);
+        NotificationEntry entry =
+                new NotificationEntry(mMockStatusBarNotification, ranking);
 
         assertEquals(systemGeneratedSmartActions, entry.systemGeneratedSmartActions);
         assertEquals(NOTIFICATION_CHANNEL, entry.channel);
@@ -343,7 +345,7 @@
         StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
                 notification, mContext.getUser(), "", 0);
 
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.setHasSentReply();
 
         assertTrue(entry.isLastMessageFromReply());
@@ -400,8 +402,7 @@
                 Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
                 title,
                 PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0))
-                        .setSemanticAction(
-                                Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION)
+                        .setContextual(true)
                         .build();
     }
 
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 983ca83..6472589 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
@@ -41,9 +41,10 @@
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 
@@ -77,7 +78,7 @@
     @Mock private NotificationListener mListener;
     @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
 
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private TestableNotificationLogger mLogger;
     private NotificationEntryListener mNotificationEntryListener;
     private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>();
@@ -93,7 +94,7 @@
         StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
                 0, null, TEST_UID,
                 0, new Notification(), UserHandle.CURRENT, null, 0);
-        mEntry = new NotificationData.Entry(sbn);
+        mEntry = new NotificationEntry(sbn);
         mEntry.setRow(mRow);
 
         mLogger = new TestableNotificationLogger(mListener, Dependency.get(UiOffloadThread.class),
@@ -159,11 +160,6 @@
         verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
     }
 
-    @Test
-    public void testHandleNullEntryOnEntryRemoved() {
-        mNotificationEntryListener.onEntryRemoved(null, "foobar", null, null, false, false);
-    }
-
     private class TestableNotificationLogger extends NotificationLogger {
 
         TestableNotificationLogger(NotificationListener notificationListener,
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 f0fa788..d94bf84 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
@@ -47,7 +47,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 
 import org.junit.Before;
@@ -73,7 +73,7 @@
     StatusBarNotification mStatusBarNotification;
     @Mock
     Notification mNotification;
-    NotificationData.Entry mEntry;
+    NotificationEntry mEntry;
     @Mock
     RemoteInput mRemoteInput;
     @Mock
@@ -107,7 +107,7 @@
         // Smart replies and actions
         when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
         when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
-        mEntry = new NotificationData.Entry(mStatusBarNotification);
+        mEntry = new NotificationEntry(mStatusBarNotification);
         when(mSmartReplyConstants.isEnabled()).thenReturn(true);
         mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
     }
@@ -159,28 +159,37 @@
         verify(mockHeadsUp, times(1)).showAppOpsIcons(any());
     }
 
-    private void setupAppGeneratedReplies(CharSequence[] smartReplyTitles) {
-        Notification.Action freeFormAction =
-                new Notification.Action.Builder(null, "Freeform Test Action", null).build();
-        setupAppGeneratedReplies(smartReplyTitles, freeFormAction);
+    private void setupAppGeneratedReplies(CharSequence[] smartReplies) {
+        setupAppGeneratedReplies(smartReplies, true /* allowSystemGeneratedReplies */);
     }
 
     private void setupAppGeneratedReplies(
-            CharSequence[] smartReplyTitles,
-            Notification.Action freeFormRemoteInputAction) {
+            CharSequence[] smartReplies, boolean allowSystemGeneratedReplies) {
         PendingIntent pendingIntent =
                 PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0);
         Notification.Action action =
                 new Notification.Action.Builder(null, "Test Action", pendingIntent).build();
-        when(mRemoteInput.getChoices()).thenReturn(smartReplyTitles);
+        when(mRemoteInput.getChoices()).thenReturn(smartReplies);
         Pair<RemoteInput, Notification.Action> remoteInputActionPair =
                 Pair.create(mRemoteInput, action);
         when(mNotification.findRemoteInputActionPair(false)).thenReturn(remoteInputActionPair);
 
+        Notification.Action freeFormRemoteInputAction =
+                createActionBuilder("Freeform Test Action")
+                .setAllowGeneratedReplies(allowSystemGeneratedReplies)
+                .build();
         Pair<RemoteInput, Notification.Action> freeFormRemoteInputActionPair =
                 Pair.create(mFreeFormRemoteInput, freeFormRemoteInputAction);
         when(mNotification.findRemoteInputActionPair(true)).thenReturn(
                 freeFormRemoteInputActionPair);
+
+        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+    }
+
+    private void setupAppGeneratedSuggestions(
+            CharSequence[] smartReplies, List<Notification.Action> smartActions) {
+        setupAppGeneratedReplies(smartReplies);
+        when(mNotification.getContextualActions()).thenReturn(smartActions);
     }
 
     @Test
@@ -198,7 +207,6 @@
     public void chooseSmartRepliesAndActions_appGeneratedSmartReplies() {
         CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
         setupAppGeneratedReplies(smartReplies);
-        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
 
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
@@ -211,12 +219,9 @@
     @Test
     public void chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions() {
         CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
-        setupAppGeneratedReplies(smartReplies);
-        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
-
         List<Notification.Action> smartActions =
                 createActions(new String[] {"Test Action 1", "Test Action 2"});
-        when(mNotification.getContextualActions()).thenReturn(smartActions);
+        setupAppGeneratedSuggestions(smartReplies, smartActions);
 
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
@@ -229,15 +234,11 @@
 
     @Test
     public void chooseSmartRepliesAndActions_sysGeneratedSmartReplies() {
-        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
-                .setAllowGeneratedReplies(true)
-                .build();
         // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
         // replies.
-        setupAppGeneratedReplies(null, freeFormAction);
+        setupAppGeneratedReplies(null /* smartReplies */);
 
-        mEntry.smartReplies =
-                new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
@@ -248,12 +249,9 @@
 
     @Test
     public void chooseSmartRepliesAndActions_noSysGeneratedSmartRepliesIfNotAllowed() {
-        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
-                .setAllowGeneratedReplies(false)
-                .build();
         // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
         // replies.
-        setupAppGeneratedReplies(null, freeFormAction);
+        setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
 
         mEntry.smartReplies =
                 new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
@@ -268,7 +266,7 @@
     public void chooseSmartRepliesAndActions_sysGeneratedSmartActions() {
         // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
         // actions.
-        setupAppGeneratedReplies(null);
+        setupAppGeneratedReplies(null /* smartReplies */);
 
         mEntry.systemGeneratedSmartActions =
                 createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
@@ -283,18 +281,12 @@
 
     @Test
     public void chooseSmartRepliesAndActions_appGenPreferredOverSysGen() {
-        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
-                .setAllowGeneratedReplies(true)
-                .build();
         CharSequence[] appGenSmartReplies = new String[] {"Reply1", "Reply2"};
         // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
         // replies.
-        setupAppGeneratedReplies(appGenSmartReplies, freeFormAction);
-        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
-
         List<Notification.Action> appGenSmartActions =
                 createActions(new String[] {"Test Action 1", "Test Action 2"});
-        when(mNotification.getContextualActions()).thenReturn(appGenSmartActions);
+        setupAppGeneratedSuggestions(appGenSmartReplies, appGenSmartActions);
 
         mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
         mEntry.systemGeneratedSmartActions =
@@ -313,12 +305,12 @@
     public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() {
         // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
         // actions.
-        setupAppGeneratedReplies(null);
-
+        setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
         when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
-
+        mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
         mEntry.systemGeneratedSmartActions =
                 createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+
         NotificationContentView.SmartRepliesAndActions repliesAndActions =
                 NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
 
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 ad43bea..0899c73 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
@@ -60,7 +60,7 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -183,8 +183,8 @@
         when(row.getGuts()).thenReturn(guts);
         doNothing().when(row).inflateGuts();
 
-        NotificationData.Entry realEntry = realRow.getEntry();
-        NotificationData.Entry entry = spy(realEntry);
+        NotificationEntry realEntry = realRow.getEntry();
+        NotificationEntry entry = spy(realEntry);
 
         when(entry.getRow()).thenReturn(row);
         when(entry.getGuts()).thenReturn(guts);
@@ -443,7 +443,7 @@
         NotificationGuts guts = new NotificationGuts(mContext);
         ExpandableNotificationRow row = spy(createTestNotificationRow());
         doReturn(guts).when(row).getGuts();
-        NotificationData.Entry entry = row.getEntry();
+        NotificationEntry entry = row.getEntry();
         entry.setRow(row);
         mGutsManager.setExposedGuts(guts);
 
@@ -452,7 +452,7 @@
 
     @Test
     public void testSetShouldManageLifetime_setShouldManage() {
-        NotificationData.Entry entry = createTestNotificationRow().getEntry();
+        NotificationEntry entry = createTestNotificationRow().getEntry();
         mGutsManager.setShouldManageLifetime(entry, true /* shouldManage */);
 
         assertTrue(entry.key.equals(mGutsManager.mKeyToRemoveOnGutsClosed));
@@ -460,7 +460,7 @@
 
     @Test
     public void testSetShouldManageLifetime_setShouldNotManage() {
-        NotificationData.Entry entry = createTestNotificationRow().getEntry();
+        NotificationEntry entry = createTestNotificationRow().getEntry();
         mGutsManager.mKeyToRemoveOnGutsClosed = entry.key;
         mGutsManager.setShouldManageLifetime(entry, false /* shouldManage */);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
index d6b706d..648df3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
@@ -48,7 +48,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -88,7 +88,7 @@
             }
 
             @Override
-            public void onAsyncInflationFinished(NotificationData.Entry entry,
+            public void onAsyncInflationFinished(NotificationEntry entry,
                     @NotificationInflater.InflationFlag int inflatedFlags) {
             }
         });
@@ -174,7 +174,7 @@
                     }
 
                     @Override
-                    public void onAsyncInflationFinished(NotificationData.Entry entry,
+                    public void onAsyncInflationFinished(NotificationEntry entry,
                             @NotificationInflater.InflationFlag int inflatedFlags) {
                         countDownLatch.countDown();
                     }
@@ -256,7 +256,7 @@
             }
 
             @Override
-            public void onAsyncInflationFinished(NotificationData.Entry entry,
+            public void onAsyncInflationFinished(NotificationEntry entry,
                     @NotificationInflater.InflationFlag int inflatedFlags) {
                 if (expectingException) {
                     exceptionHolder.setException(new RuntimeException(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index e4d0196..04d7509 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -35,7 +35,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
 import org.junit.Before;
@@ -54,7 +54,7 @@
     public void setup() {
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         mRow = mock(ExpandableNotificationRow.class);
-        NotificationData.Entry entry = new NotificationData.Entry(
+        NotificationEntry entry = new NotificationEntry(
                 mock(StatusBarNotification.class));
         entry.channel = mock(NotificationChannel.class);
         when(mRow.getEntry()).thenReturn(entry);
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 b8c7ee0..214404c 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
@@ -35,12 +35,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.os.Handler;
-import android.os.IPowerManager;
-import android.os.Looper;
-import android.os.PowerManager;
 import android.provider.Settings;
-import android.service.dreams.IDreamManager;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -58,9 +53,9 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
-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.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -104,8 +99,6 @@
     @Mock private NotificationData mNotificationData;
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
-    @Mock private IDreamManager mDreamManager;
-    private PowerManager mPowerManager;
     private TestableNotificationEntryManager mEntryManager;
     private int mOriginalInterruptionModelSetting;
 
@@ -122,12 +115,7 @@
         mDependency.injectMockDependency(ShadeController.class);
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
 
-        IPowerManager powerManagerService = mock(IPowerManager.class);
-        mPowerManager = new PowerManager(mContext, powerManagerService,
-                Handler.createAsync(Looper.myLooper()));
-
-        mEntryManager = new TestableNotificationEntryManager(mPowerManager,
-                mContext);
+        mEntryManager = new TestableNotificationEntryManager(mContext);
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         Dependency.get(InitController.class).executePostInitTasks();
         mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager,
@@ -135,7 +123,8 @@
 
 
         NotificationShelf notificationShelf = mock(NotificationShelf.class);
-        mStackScroller = spy(new NotificationStackScrollLayout(getContext()));
+        mStackScroller = spy(new NotificationStackScrollLayout(getContext(), null,
+                true /* allowLongPress */));
         mStackScroller.setShelf(notificationShelf);
         mStackScroller.setStatusBar(mBar);
         mStackScroller.setScrimController(mock(ScrimController.class));
@@ -276,8 +265,8 @@
     @Test
     public void testUpdateFooter_remoteInput() {
         setBarStateForTest(StatusBarState.SHADE);
-        ArrayList<Entry> entries = new ArrayList<>();
-        entries.add(mock(Entry.class));
+        ArrayList<NotificationEntry> entries = new ArrayList<>();
+        entries.add(mock(NotificationEntry.class));
         when(mNotificationData.getActiveNotifications()).thenReturn(entries);
 
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
@@ -293,8 +282,8 @@
     @Test
     public void testUpdateFooter_oneClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
-        ArrayList<Entry> entries = new ArrayList<>();
-        entries.add(mock(Entry.class));
+        ArrayList<NotificationEntry> entries = new ArrayList<>();
+        entries.add(mock(NotificationEntry.class));
         when(mNotificationData.getActiveNotifications()).thenReturn(entries);
 
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
@@ -309,8 +298,8 @@
     @Test
     public void testUpdateFooter_oneNonClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
-        ArrayList<Entry> entries = new ArrayList<>();
-        entries.add(mock(Entry.class));
+        ArrayList<NotificationEntry> entries = new ArrayList<>();
+        entries.add(mock(NotificationEntry.class));
         when(mEntryManager.getNotificationData().getActiveNotifications()).thenReturn(entries);
         assertTrue(mEntryManager.getNotificationData().getActiveNotifications().size() != 0);
 
@@ -325,7 +314,7 @@
 
         // add notification
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
-        NotificationData.Entry entry = mock(NotificationData.Entry.class);
+        NotificationEntry entry = mock(NotificationEntry.class);
         when(row.getEntry()).thenReturn(entry);
         when(entry.isClearable()).thenReturn(true);
         mStackScroller.addContainerView(row);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 1481aef..c0f7f0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
@@ -30,6 +31,8 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.qs.AutoAddTracker;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.HotspotController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -51,7 +54,11 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mAutoTileManager = new AutoTileManager(mContext, mAutoAddTracker, mQsTileHost,
-                Handler.createAsync(TestableLooper.get(this).getLooper()));
+                Handler.createAsync(TestableLooper.get(this).getLooper()),
+                mock(HotspotController.class),
+                mock(DataSaverController.class),
+                mock(ManagedProfileController.class),
+                mock(ColorDisplayController.class));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index d99e46d..add8838 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeast;
@@ -74,7 +76,7 @@
 
         CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
         fragment.initNotificationIconArea(mMockNotificiationAreaController);
-        fragment.disable(0, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
@@ -89,12 +91,12 @@
 
         CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
         fragment.initNotificationIconArea(mMockNotificiationAreaController);
-        fragment.disable(StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
 
         assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
 
-        fragment.disable(0, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
@@ -107,11 +109,11 @@
 
         CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
         fragment.initNotificationIconArea(mMockNotificiationAreaController);
-        fragment.disable(StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
         Mockito.verify(mNotificationAreaInner).setVisibility(eq(View.INVISIBLE));
 
-        fragment.disable(0, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
     }
@@ -123,11 +125,11 @@
 
         CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
         fragment.initNotificationIconArea(mMockNotificiationAreaController);
-        fragment.disable(StatusBarManager.DISABLE_CLOCK, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
 
         assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility());
 
-        fragment.disable(0, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
     }
@@ -139,7 +141,7 @@
 
         CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
         fragment.initNotificationIconArea(mMockNotificiationAreaController);
-        fragment.disable(StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+        fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
         Mockito.verify(mNotificationAreaInner).setVisibility(eq(View.INVISIBLE));
 
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 44deb10..9ceb325 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
@@ -16,15 +16,20 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.view.View;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.view.View;
 
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AlertingNotificationManagerTest;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -34,11 +39,6 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertFalse;
-
-import static org.mockito.Mockito.when;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -96,7 +96,7 @@
 
     @Test
     public void testCanRemoveImmediately_notTopEntry() {
-        NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
+        NotificationEntry laterEntry = new NotificationEntry(createNewNotification(1));
         laterEntry.setRow(mRow);
         mHeadsUpManager.showNotification(mEntry);
         mHeadsUpManager.showNotification(laterEntry);
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 56af400..cefd4ac 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
@@ -32,10 +32,9 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
@@ -64,7 +63,7 @@
     @Captor
     private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
     private NotificationEntryListener mNotificationEntryListener;
-    private final HashMap<String, Entry> mPendingEntries = new HashMap<>();
+    private final HashMap<String, NotificationEntry> mPendingEntries = new HashMap<>();
     private final NotificationGroupTestHelper mGroupTestHelper =
             new NotificationGroupTestHelper(mContext);
 
@@ -94,9 +93,9 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransfersToChild() {
-        Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
-        Entry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
 
         // Summary will be suppressed because there is only one child.
         mGroupManager.onEntryAdded(summaryEntry);
@@ -109,11 +108,11 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransfersToChildButBackAgain() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry2 =
+        NotificationEntry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -133,11 +132,11 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpDoesntTransferBackOnDozingChanged() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry2 =
+        NotificationEntry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -158,9 +157,9 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransferDoesNotAlertChildIfUninflated() {
-        Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
-        Entry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
 
@@ -176,9 +175,9 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransferAlertsChildOnInflation() {
-        Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
-        Entry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
 
@@ -196,13 +195,13 @@
 
     @Test
     public void testSuppressedSummaryHeadsUpTransferBackAbortsChildInflation() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
-        NotificationData.Entry childEntry2 =
+        NotificationEntry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -226,9 +225,9 @@
 
     @Test
     public void testCleanUpPendingAlertInfo() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
@@ -237,17 +236,16 @@
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
 
-        mNotificationEntryListener.onEntryRemoved(childEntry, childEntry.key, null, null,
-                false, false);
+        mNotificationEntryListener.onEntryRemoved(childEntry, null, false);
 
         assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
     }
 
     @Test
     public void testUpdateGroupChangeDoesNotTransfer() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
@@ -268,9 +266,9 @@
 
     @Test
     public void testUpdateChildToSummaryDoesNotTransfer() {
-        NotificationData.Entry summaryEntry =
+        NotificationEntry summaryEntry =
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
-        NotificationData.Entry childEntry =
+        NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
             .thenReturn(false);
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 b0bd1fb..ecb3e4d 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
@@ -29,7 +29,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
@@ -69,8 +69,8 @@
 
     @Test
     public void testIsOnlyChildInGroup() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -80,8 +80,8 @@
 
     @Test
     public void testIsChildInGroupWithSummary() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -92,8 +92,8 @@
 
     @Test
     public void testIsSummaryOfGroupWithChildren() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -105,8 +105,8 @@
 
     @Test
     public void testRemoveChildFromGroupWithSummary() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -118,8 +118,8 @@
 
     @Test
     public void testRemoveSummaryFromGroupWithSummary() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -132,8 +132,8 @@
 
     @Test
     public void testHeadsUpEntryIsIsolated() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -149,8 +149,8 @@
 
     @Test
     public void testAmbientPulseEntryIsIsolated() {
-        NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
-        NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
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 7ad68eb..e6b9778 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
@@ -27,7 +27,7 @@
 import android.service.notification.StatusBarNotification;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
@@ -44,23 +44,23 @@
         mContext = context;
     }
 
-    public NotificationData.Entry createSummaryNotification() {
+    public NotificationEntry createSummaryNotification() {
         return createSummaryNotification(Notification.GROUP_ALERT_ALL);
     }
 
-    public NotificationData.Entry createSummaryNotification(int groupAlertBehavior) {
+    public NotificationEntry createSummaryNotification(int groupAlertBehavior) {
         return createEntry(true, groupAlertBehavior);
     }
 
-    public NotificationData.Entry createChildNotification() {
+    public NotificationEntry createChildNotification() {
         return createChildNotification(Notification.GROUP_ALERT_ALL);
     }
 
-    public NotificationData.Entry createChildNotification(int groupAlertBehavior) {
+    public NotificationEntry createChildNotification(int groupAlertBehavior) {
         return createEntry(false, groupAlertBehavior);
     }
 
-    public NotificationData.Entry createEntry(boolean isSummary, int groupAlertBehavior) {
+    public NotificationEntry createEntry(boolean isSummary, int groupAlertBehavior) {
         Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("Title")
                 .setSmallIcon(R.drawable.ic_person)
@@ -79,7 +79,7 @@
                 new UserHandle(ActivityManager.getCurrentUser()),
                 null /* overrideGroupKey */,
                 0 /* postTime */);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
         entry.setRow(row);
         when(row.getEntry()).thenReturn(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 4fabe7f..12cb9957 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -38,7 +40,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 
@@ -61,7 +63,7 @@
     public void setup() {
         mMetricsLogger = new FakeMetricsLogger();
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
-        mCommandQueue = new CommandQueue();
+        mCommandQueue = new CommandQueue(mContext);
         mContext.putComponent(CommandQueue.class, mCommandQueue);
         mDependency.injectTestDependency(ShadeController.class, mShadeController);
 
@@ -80,8 +82,9 @@
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
-        mCommandQueue.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
+        NotificationEntry entry = new NotificationEntry(sbn);
+        mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
+                false /* animate */);
         TestableLooper.get(this).processAllMessages();
 
         assertFalse("The panel shouldn't allow heads up while disabled",
@@ -93,8 +96,9 @@
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
-        mCommandQueue.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
+        NotificationEntry entry = new NotificationEntry(sbn);
+        mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+                false /* animate */);
         TestableLooper.get(this).processAllMessages();
 
         assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 18ead90..0d4046b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -18,6 +18,7 @@
 
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -94,13 +95,13 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -134,7 +135,7 @@
     @Mock private IDreamManager mDreamManager;
     @Mock private ScrimController mScrimController;
     @Mock private DozeScrimController mDozeScrimController;
-    @Mock private ArrayList<Entry> mNotificationList;
+    @Mock private ArrayList<NotificationEntry> mNotificationList;
     @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private NotificationData mNotificationData;
     @Mock
@@ -204,7 +205,7 @@
 
         mMetricsLogger = new FakeMetricsLogger();
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
-        mEntryManager = new TestableNotificationEntryManager(mPowerManager, mContext);
+        mEntryManager = new TestableNotificationEntryManager(mContext);
         mNotificationLogger = new NotificationLogger(mNotificationListener,
                 Dependency.get(UiOffloadThread.class), mEntryManager, mStatusBarStateController);
         mDependency.injectTestDependency(NotificationLogger.class, mNotificationLogger);
@@ -247,7 +248,8 @@
                 mock(StatusBarWindowController.class), mock(NotificationIconAreaController.class),
                 mDozeScrimController, mock(NotificationShelf.class),
                 mLockscreenUserManager, mCommandQueue, mNotificationPresenter,
-                mock(BubbleController.class), mock(NavigationBarController.class));
+                mock(BubbleController.class), mock(NavigationBarController.class),
+                mock(AutoHideController.class));
         mStatusBar.mContext = mContext;
         mStatusBar.mComponents = mContext.getComponents();
         SystemUIFactory.getInstance().getRootComponent()
@@ -407,7 +409,7 @@
                 .build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.importance = IMPORTANCE_HIGH;
 
         assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
@@ -428,7 +430,7 @@
                 .build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.importance = IMPORTANCE_HIGH;
 
         assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
@@ -445,7 +447,7 @@
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK;
         entry.importance = IMPORTANCE_HIGH;
 
@@ -463,7 +465,7 @@
         Notification n = new Notification.Builder(getContext(), "a").build();
         StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
                 UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        NotificationEntry entry = new NotificationEntry(sbn);
         entry.importance = IMPORTANCE_HIGH;
 
         assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
@@ -546,7 +548,7 @@
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
 
         when(mCommandQueue.panelsEnabled()).thenReturn(false);
-        mStatusBar.disable(StatusBarManager.DISABLE_NONE,
+        mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
                 StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
         verify(mNotificationPanelView).setQsExpansionEnabled(false);
         mStatusBar.animateExpandNotificationsPanel();
@@ -555,7 +557,8 @@
         verify(mNotificationPanelView, never()).expand(anyBoolean());
 
         when(mCommandQueue.panelsEnabled()).thenReturn(true);
-        mStatusBar.disable(StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE, false);
+        mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
+                StatusBarManager.DISABLE2_NONE, false);
         verify(mNotificationPanelView).setQsExpansionEnabled(true);
         mStatusBar.animateExpandNotificationsPanel();
         verify(mNotificationPanelView).expandWithoutQs();
@@ -698,7 +701,8 @@
                 CommandQueue commandQueue,
                 NotificationPresenter notificationPresenter,
                 BubbleController bubbleController,
-                NavigationBarController navBarController) {
+                NavigationBarController navBarController,
+                AutoHideController autoHideController) {
             mStatusBarKeyguardViewManager = man;
             mUnlockMethodCache = unlock;
             mKeyguardIndicationController = key;
@@ -730,6 +734,7 @@
             mGestureWakeLock = mock(PowerManager.WakeLock.class);
             mBubbleController = bubbleController;
             mNavigationBarController = navBarController;
+            mAutoHideController = autoHideController;
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
@@ -756,9 +761,8 @@
 
     public static class TestableNotificationEntryManager extends NotificationEntryManager {
 
-        public TestableNotificationEntryManager(PowerManager powerManager, Context context) {
+        public TestableNotificationEntryManager(Context context) {
             super(context);
-            mPowerManager = powerManager;
         }
 
         public void setUpForTest(NotificationPresenter presenter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
index e3a41be..8b8e3f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
@@ -14,11 +14,11 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static junit.framework.TestCase.assertTrue;
+
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -59,7 +59,7 @@
         mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
         mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
         assertFalse(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
-        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
     }
 
     @Test
@@ -67,7 +67,7 @@
         mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
         mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
         assertTrue(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
-        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
     }
 
     @Test
@@ -78,7 +78,7 @@
         c.orientation = Configuration.ORIENTATION_LANDSCAPE;
         mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
         assertTrue(mRemoteInputQuickSettingsDisabler.misLandscape);
-        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
     }
 
     @Test
@@ -89,7 +89,7 @@
         c.orientation = Configuration.ORIENTATION_PORTRAIT;
         mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
         assertFalse(mRemoteInputQuickSettingsDisabler.misLandscape);
-        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyBoolean());
+        verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
     }
 
 }
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 c5bac92..fdc8ef1 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
@@ -50,7 +50,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.phone.ShadeController;
 
@@ -94,7 +94,7 @@
     private int mSpacing;
 
     @Mock private SmartReplyController mLogger;
-    private NotificationData.Entry mEntry;
+    private NotificationEntry mEntry;
     private Notification mNotification;
 
     @Mock ActivityStarter mActivityStarter;
@@ -127,7 +127,7 @@
         StatusBarNotification sbn = mock(StatusBarNotification.class);
         when(sbn.getNotification()).thenReturn(mNotification);
         when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
-        mEntry = new NotificationData.Entry(sbn);
+        mEntry = new NotificationEntry(sbn);
 
         mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
     }
diff --git a/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
index baf09b1..da10361 100644
--- a/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorBlackOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Black accent color name application label. [CHAR LIMIT=50] -->
-    <string name="accent_color_black_overlay" translatable="false">Black Accent Color</string>
+    <string name="accent_color_black_overlay" translatable="false">Black</string>
 </resources>
 
diff --git a/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
index 4de344c..623a1da 100644
--- a/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorGreenOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Green accent color name application label. [CHAR LIMIT=50] -->
-    <string name="accent_color_green_overlay" translatable="false">Green Accent Color</string>
+    <string name="accent_color_green_overlay" translatable="false">Green</string>
 </resources>
 
diff --git a/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
index d1eb95a..d1c7168 100644
--- a/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
+++ b/packages/overlays/AccentColorPurpleOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Purple accent color name application label. [CHAR LIMIT=50] -->
-    <string name="accent_color_purple_overlay" translatable="false">Purple Accent Color</string>
+    <string name="accent_color_purple_overlay" translatable="false">Purple</string>
 </resources>
 
diff --git a/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
index dc5c196..3c4c24d 100644
--- a/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeRoundedRectOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Rounded corner rectangle overlay -->
-    <string name="icon_shape_roundedrect_overlay" translatable="false">RoundedRectangle Icons</string>
+    <string name="icon_shape_roundedrect_overlay" translatable="false">Rounded Rectangle</string>
 
 </resources>
diff --git a/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml b/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
index 4fd39ff26..5772165 100644
--- a/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeSquareOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Square icon overlay -->
-    <string name="icon_shape_square_overlay" translatable="false">Square Icons</string>
+    <string name="icon_shape_square_overlay" translatable="false">Square</string>
 
 </resources>
diff --git a/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
index b7c001c..028eccb 100644
--- a/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeSquircleOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Squircle icon shape overlay -->
-    <string name="icon_shape_squircle_overlay" translatable="false">Square Icons</string>
+    <string name="icon_shape_squircle_overlay" translatable="false">Squircle</string>
 
 </resources>
diff --git a/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
index d946ee8..db9fa98 100644
--- a/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
+++ b/packages/overlays/IconShapeTeardropOverlay/res/values/strings.xml
@@ -18,6 +18,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Teardrop icon overlay -->
-    <string name="icon_shape_teardrop_overlay" translatable="false">Teardrop Icons</string>
+    <string name="icon_shape_teardrop_overlay" translatable="false">Teardrop</string>
 
 </resources>
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 3180b47..2b399de 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -3684,7 +3684,7 @@
     ACTION_SETTINGS_TILE_CLICK = 830;
 
     // OPEN: Notification unsnoozed. CLOSE: Notification snoozed. UPDATE: snoozed notification
-    // updated
+    // updated. TYPE_DISMISS: snoozing canceled due to data being cleared on package
     // CATEGORY: NOTIFICATION
     // OS: O
     NOTIFICATION_SNOOZED = 831;
@@ -6730,6 +6730,11 @@
     // OS: Q
     ZEN_CUSTOM_SETTINGS_DIALOG = 1612;
 
+    // OPEN: Settings > Developer Options > Game Update Packages
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SETTINGS_GUP_DASHBOARD = 1613;
+
     // ---- End Q Constants, all Q constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/Android.bp b/services/Android.bp
index 58a0997..01734f4 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -28,6 +28,7 @@
         "services.net",
         "services.print",
         "services.restrictions",
+        "services.startop",
         "services.usage",
         "services.usb",
         "services.voiceinteraction",
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 8b07db7..6bd3990 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -127,7 +127,7 @@
     protected void startServiceForUser(int userId) {
         UserBackupManagerService userBackupManagerService =
                 UserBackupManagerService.createAndInitializeService(
-                        userId, mContext, mTrampoline, mBackupThread, mTransportWhitelist);
+                        userId, mContext, mTrampoline, mTransportWhitelist);
         startServiceForUser(userId, userBackupManagerService);
     }
 
@@ -152,7 +152,11 @@
     /** Stops the backup service for user {@code userId} when the user is stopped. */
     @VisibleForTesting
     protected void stopServiceForUser(int userId) {
-        mServiceUsers.remove(userId);
+        UserBackupManagerService userBackupManagerService = mServiceUsers.removeReturnOld(userId);
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.tearDownService();
+        }
     }
 
     SparseArray<UserBackupManagerService> getServiceUsers() {
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 82638b4..5708b1c 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -24,15 +24,12 @@
 import android.content.Context;
 
 public class FullBackupJob extends JobService {
-    private static final String TAG = "FullBackupJob";
-    private static final boolean DEBUG = true;
-
     private static ComponentName sIdleService =
             new ComponentName("android", FullBackupJob.class.getName());
 
     private static final int JOB_ID = 0x5038;
 
-    JobParameters mParams;
+    private JobParameters mParams;
 
     public static void schedule(Context ctx, long minDelay, BackupManagerConstants constants) {
         JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index bed520e..3184bd8 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -13,8 +13,6 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
-import com.android.server.backup.restore.PerformAdbRestoreTask;
-
 import libcore.io.IoUtils;
 
 import java.io.File;
@@ -42,11 +40,10 @@
     private final UserBackupManagerService mBackupManagerService;
     private final File mDataDir;
 
-    FileMetadata mInfo;
-    PerformAdbRestoreTask mRestoreTask;
-    ParcelFileDescriptor mInFD;
-    IBackupAgent mAgent;
-    int mToken;
+    private final FileMetadata mInfo;
+    private final ParcelFileDescriptor mInFD;
+    private final IBackupAgent mAgent;
+    private final int mToken;
 
     public KeyValueAdbRestoreEngine(UserBackupManagerService backupManagerService,
             File dataDir, FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent,
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
index a0feaf9..aabd41a 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
@@ -17,29 +17,35 @@
 package com.android.server.backup;
 
 import android.os.Environment;
+import android.os.UserHandle;
 
 import java.io.File;
 
 /** Directories used for user specific backup/restore persistent state and book-keeping. */
-public final class UserBackupManagerFiles {
+final class UserBackupManagerFiles {
     // Name of the directories the service stores bookkeeping data under.
     private static final String BACKUP_PERSISTENT_DIR = "backup";
     private static final String BACKUP_STAGING_DIR = "backup_stage";
 
+    private static File getBaseDir(int userId) {
+        return Environment.getDataSystemCeDirectory(userId);
+    }
+
     static File getBaseStateDir(int userId) {
-        // TODO (b/120424138) this should be per user
+        if (userId != UserHandle.USER_SYSTEM) {
+            return new File(getBaseDir(userId), BACKUP_PERSISTENT_DIR);
+        }
+        // TODO (b/120424138) remove if clause above and use same logic for system user.
+        // simultaneously, copy below dir to new system user dir
         return new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
     }
 
     static File getDataDir(int userId) {
-        // TODO (b/120424138) this should be per user
-        // This dir on /cache is managed directly in init.rc
+        if (userId != UserHandle.USER_SYSTEM) {
+            return new File(getBaseDir(userId), BACKUP_STAGING_DIR);
+        }
+        // TODO (b/120424138) remove if clause above and use same logic for system user. Since this
+        // is a staging dir, we dont need to copy below dir to new system user dir
         return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
     }
-
-    /** Directory used by full backup engine to store state. */
-    public static File getFullBackupEngineFilesDir(int userId) {
-        // TODO (b/120424138) this should be per user
-        return new File("/data/system");
-    }
 }
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 6425508..bfaa221 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -110,9 +110,9 @@
 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.internal.SetupObserver;
 import com.android.server.backup.keyvalue.BackupRequest;
 import com.android.server.backup.params.AdbBackupParams;
 import com.android.server.backup.params.AdbParams;
@@ -208,8 +208,8 @@
 
     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";
+    private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
+    private 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.
@@ -245,26 +245,27 @@
     private final @UserIdInt int mUserId;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     private final TransportManager mTransportManager;
+    private final HandlerThread mUserBackupThread;
 
-    private Context mContext;
-    private PackageManager mPackageManager;
-    private IPackageManager mPackageManagerBinder;
-    private IActivityManager mActivityManager;
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final IPackageManager mPackageManagerBinder;
+    private final IActivityManager mActivityManager;
     private PowerManager mPowerManager;
-    private AlarmManager mAlarmManager;
-    private IStorageManager mStorageManager;
-    private BackupManagerConstants mConstants;
-    private PowerManager.WakeLock mWakelock;
-    private BackupHandler mBackupHandler;
+    private final AlarmManager mAlarmManager;
+    private final IStorageManager mStorageManager;
+    private final BackupManagerConstants mConstants;
+    private final PowerManager.WakeLock mWakelock;
+    private final BackupHandler mBackupHandler;
 
-    private IBackupManager mBackupManagerBinder;
+    private final IBackupManager mBackupManagerBinder;
 
     private boolean mEnabled;   // access to this is synchronized on 'this'
-    private boolean mProvisioned;
+    private boolean mSetupComplete;
     private boolean mAutoRestore;
 
-    private PendingIntent mRunBackupIntent;
-    private PendingIntent mRunInitIntent;
+    private final PendingIntent mRunBackupIntent;
+    private final PendingIntent mRunInitIntent;
 
     private final ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
 
@@ -272,7 +273,7 @@
     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<>();
+    private final HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
 
     // locking around the pending-backup management
     private final Object mQueueLock = new Object();
@@ -314,9 +315,6 @@
 
     private ActiveRestoreSession mActiveRestoreSession;
 
-    // Watch the device provisioning operation during setup
-    private ContentObserver mProvisionedObserver;
-
     /**
      * mCurrentOperations contains the list of currently active operations.
      *
@@ -342,7 +340,7 @@
     private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
     private final Object mCurrentOpLock = new Object();
     private final Random mTokenGenerator = new Random();
-    final AtomicInteger mNextToken = new AtomicInteger();
+    private final AtomicInteger mNextToken = new AtomicInteger();
 
     // Where we keep our journal files and other bookkeeping.
     private final File mBaseStateDir;
@@ -350,7 +348,7 @@
     private final File mJournalDir;
     @Nullable
     private DataChangedJournal mJournal;
-    private File mFullBackupScheduleFile;
+    private final File mFullBackupScheduleFile;
 
     // Keep a log of all the apps we've ever backed up.
     private ProcessedPackagesJournal mProcessedPackagesJournal;
@@ -371,11 +369,10 @@
             @UserIdInt int userId,
             Context context,
             Trampoline trampoline,
-            HandlerThread backupThread,
             Set<ComponentName> transportWhitelist) {
         String currentTransport =
-                Settings.Secure.getString(
-                        context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+                Settings.Secure.getStringForUser(
+                        context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT, userId);
         if (TextUtils.isEmpty(currentTransport)) {
             currentTransport = null;
         }
@@ -389,8 +386,21 @@
         File baseStateDir = UserBackupManagerFiles.getBaseStateDir(userId);
         File dataDir = UserBackupManagerFiles.getDataDir(userId);
 
+        HandlerThread userBackupThread =
+                new HandlerThread("backup-" + userId, Process.THREAD_PRIORITY_BACKGROUND);
+        userBackupThread.start();
+        if (DEBUG) {
+            Slog.d(TAG, "Started thread " + userBackupThread.getName() + " for user " + userId);
+        }
+
         return createAndInitializeService(
-                userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+                userId,
+                context,
+                trampoline,
+                userBackupThread,
+                baseStateDir,
+                dataDir,
+                transportManager);
     }
 
     /**
@@ -399,7 +409,7 @@
      * @param userId The user which this service is for.
      * @param context The system server context.
      * @param trampoline A reference to the proxy to {@link BackupManagerService}.
-     * @param backupThread The thread running backup/restore operations for the user.
+     * @param userBackupThread The thread running backup/restore operations for the user.
      * @param baseStateDir The directory we store the user's persistent bookkeeping data.
      * @param dataDir The directory we store the user's temporary staging data.
      * @param transportManager The {@link TransportManager} responsible for handling the user's
@@ -410,19 +420,38 @@
             @UserIdInt int userId,
             Context context,
             Trampoline trampoline,
-            HandlerThread backupThread,
+            HandlerThread userBackupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
         return new UserBackupManagerService(
-                userId, context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
+                userId,
+                context,
+                trampoline,
+                userBackupThread,
+                baseStateDir,
+                dataDir,
+                transportManager);
+    }
+
+    /**
+     * Returns the value of {@link Settings.Secure#USER_SETUP_COMPLETE} for the specified user
+     * {@code userId} as a {@code boolean}.
+     */
+    public static boolean getSetupCompleteSettingForUser(Context context, int userId) {
+        return Settings.Secure.getIntForUser(
+                context.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE,
+                0,
+                userId)
+                != 0;
     }
 
     private UserBackupManagerService(
             @UserIdInt int userId,
             Context context,
             Trampoline parent,
-            HandlerThread backupThread,
+            HandlerThread userBackupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
@@ -443,21 +472,22 @@
                 BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
         mAgentTimeoutParameters.start();
 
-        // spin up the backup/restore handler thread
-        checkNotNull(backupThread, "backupThread cannot be null");
-        mBackupHandler = new BackupHandler(this, backupThread.getLooper());
+        checkNotNull(userBackupThread, "userBackupThread cannot be null");
+        mUserBackupThread = userBackupThread;
+        mBackupHandler = new BackupHandler(this, userBackupThread.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;
+        mSetupComplete = getSetupCompleteSettingForUser(context, userId);
+        mAutoRestore = Settings.Secure.getIntForUser(resolver,
+                Settings.Secure.BACKUP_AUTO_RESTORE, 1, userId) != 0;
 
-        mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
+        ContentObserver setupObserver = new SetupObserver(this, mBackupHandler);
         resolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
-                false, mProvisionedObserver);
+                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE),
+                /* notifyForDescendents */ false,
+                setupObserver,
+                mUserId);
 
         mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null");
         mBaseStateDir.mkdirs();
@@ -526,6 +556,15 @@
         mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
     }
 
+    /** Cleans up state when the user of this service is stopped. */
+    void tearDownService() {
+        mUserBackupThread.quit();
+    }
+
+    public @UserIdInt int getUserId() {
+        return mUserId;
+    }
+
     public BackupManagerConstants getConstants() {
         return mConstants;
     }
@@ -538,51 +577,27 @@
         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;
     }
@@ -595,12 +610,12 @@
         mEnabled = enabled;
     }
 
-    public boolean isProvisioned() {
-        return mProvisioned;
+    public boolean isSetupComplete() {
+        return mSetupComplete;
     }
 
-    public void setProvisioned(boolean provisioned) {
-        mProvisioned = provisioned;
+    public void setSetupComplete(boolean setupComplete) {
+        mSetupComplete = setupComplete;
     }
 
     public PowerManager.WakeLock getWakelock() {
@@ -617,35 +632,18 @@
         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;
     }
@@ -658,10 +656,6 @@
         mBackupRunning = backupRunning;
     }
 
-    public long getLastBackupPass() {
-        return mLastBackupPass;
-    }
-
     public void setLastBackupPass(long lastBackupPass) {
         mLastBackupPass = lastBackupPass;
     }
@@ -670,10 +664,6 @@
         return mClearDataLock;
     }
 
-    public boolean isClearingData() {
-        return mClearingData;
-    }
-
     public void setClearingData(boolean clearingData) {
         mClearingData = clearingData;
     }
@@ -694,11 +684,6 @@
         return mActiveRestoreSession;
     }
 
-    public void setActiveRestoreSession(
-            ActiveRestoreSession activeRestoreSession) {
-        mActiveRestoreSession = activeRestoreSession;
-    }
-
     public SparseArray<Operation> getCurrentOperations() {
         return mCurrentOperations;
     }
@@ -732,18 +717,10 @@
         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;
     }
@@ -1553,11 +1530,16 @@
             throw new IllegalArgumentException("No packages are provided for backup");
         }
 
-        if (!mEnabled || !mProvisioned) {
-            Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
+        if (!mEnabled || !mSetupComplete) {
+            Slog.i(
+                    TAG,
+                    "Backup requested but enabled="
+                            + mEnabled
+                            + " setupComplete="
+                            + mSetupComplete);
             BackupObserverUtils.sendBackupFinished(observer,
                     BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-            final int logTag = mProvisioned
+            final int logTag = mSetupComplete
                     ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
                     : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
             monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
@@ -1992,13 +1974,13 @@
         FullBackupEntry entry = null;
         long latency = fullBackupInterval;
 
-        if (!mEnabled || !mProvisioned) {
+        if (!mEnabled || !mSetupComplete) {
             // 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");
+                Slog.i(TAG, "beginFullBackup but enabled=" + mEnabled
+                        + " setupComplete=" + mSetupComplete + "; ignoring");
             }
             return false;
         }
@@ -2399,12 +2381,6 @@
         }
     }
 
-    /** 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
@@ -2437,8 +2413,7 @@
 
         long oldId = Binder.clearCallingIdentity();
         try {
-            // Doesn't make sense to do a full backup prior to setup
-            if (!deviceIsProvisioned()) {
+            if (!mSetupComplete) {
                 Slog.i(TAG, "Backup not supported before setup");
                 return;
             }
@@ -2564,9 +2539,7 @@
         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()) {
+            if (!mSetupComplete) {
                 Slog.i(TAG, "Full restore not permitted before setup");
                 return;
             }
@@ -2721,7 +2694,7 @@
             }
 
             synchronized (mQueueLock) {
-                if (enable && !wasEnabled && mProvisioned) {
+                if (enable && !wasEnabled && mSetupComplete) {
                     // if we've just been enabled, start scheduling backup passes
                     KeyValueBackupJob.schedule(mContext, mConstants);
                     scheduleNextFullBackupJob(0);
@@ -2734,7 +2707,7 @@
                     // 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) {
+                    if (wasEnabled && mSetupComplete) {
                         // NOTE: we currently flush every registered transport, not just
                         // the currently-active one.
                         List<String> transportNames = new ArrayList<>();
@@ -2782,8 +2755,8 @@
         final long oldId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                Settings.Secure.putInt(mContext.getContentResolver(),
-                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
+                Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0, mUserId);
                 mAutoRestore = doAutoRestore;
             }
         } finally {
@@ -2996,8 +2969,8 @@
 
     private void updateStateForTransport(String newTransportName) {
         // Publish the name change
-        Settings.Secure.putString(mContext.getContentResolver(),
-                Settings.Secure.BACKUP_TRANSPORT, newTransportName);
+        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                Settings.Secure.BACKUP_TRANSPORT, newTransportName, mUserId);
 
         // And update our current-dataset bookkeeping
         String callerLogString = "BMS.updateStateForTransport()";
@@ -3112,8 +3085,7 @@
         synchronized (mAgentConnectLock) {
             if (Binder.getCallingUid() == Process.SYSTEM_UID) {
                 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
-                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
-                mConnectedAgent = agent;
+                mConnectedAgent = IBackupAgent.Stub.asInterface(agentBinder);
                 mConnecting = false;
             } else {
                 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
@@ -3431,7 +3403,7 @@
     private void dumpInternal(PrintWriter pw) {
         synchronized (mQueueLock) {
             pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
-                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
+                    + " / " + (!mSetupComplete ? "not " : "") + "setup complete / "
                     + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
             pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
             if (mBackupRunning) pw.println("Backup currently running");
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 45ca2af..03d4e97 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -34,14 +34,12 @@
 import android.content.pm.PackageManager;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
-import com.android.server.backup.UserBackupManagerFiles;
 import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.remote.RemoteCall;
 import com.android.server.backup.utils.FullBackupUtils;
@@ -78,21 +76,21 @@
         private final File mFilesDir;
 
         FullBackupRunner(
+                UserBackupManagerService userBackupManagerService,
                 PackageInfo packageInfo,
                 IBackupAgent agent,
                 ParcelFileDescriptor pipe,
                 int token,
                 boolean includeApks)
                 throws IOException {
-            // TODO: http://b/22388012
-            mUserId = UserHandle.USER_SYSTEM;
+            mUserId = userBackupManagerService.getUserId();
             mPackageManager = backupManagerService.getPackageManager();
             mPackage = packageInfo;
             mAgent = agent;
             mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
             mToken = token;
             mIncludeApks = includeApks;
-            mFilesDir = UserBackupManagerFiles.getFullBackupEngineFilesDir(mUserId);
+            mFilesDir = userBackupManagerService.getDataDir();
         }
 
         @Override
@@ -155,9 +153,12 @@
                         backupManagerService.getBackupManagerBinder(),
                         mTransportFlags);
             } catch (IOException e) {
-                Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
+                Slog.e(TAG, "Error running full backup for " + mPackage.packageName, e);
             } catch (RemoteException e) {
-                Slog.e(TAG, "Remote agent vanished during full backup of " + mPackage.packageName);
+                Slog.e(
+                        TAG,
+                        "Remote agent vanished during full backup of " + mPackage.packageName,
+                        e);
             } finally {
                 try {
                     mPipe.close();
@@ -233,7 +234,13 @@
                 pipes = ParcelFileDescriptor.createPipe();
 
                 FullBackupRunner runner =
-                        new FullBackupRunner(mPkg, mAgent, pipes[1], mOpToken, mIncludeApks);
+                        new FullBackupRunner(
+                                backupManagerService,
+                                mPkg,
+                                mAgent,
+                                pipes[1],
+                                mOpToken,
+                                mIncludeApks);
                 pipes[1].close(); // the runner has dup'd it
                 pipes[1] = null;
                 Thread t = new Thread(runner, "app-data-runner");
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 5b449c5..2ee96d1 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -317,16 +317,16 @@
         int backupRunStatus = BackupManager.SUCCESS;
 
         try {
-            if (!backupManagerService.isEnabled() || !backupManagerService.isProvisioned()) {
+            if (!backupManagerService.isEnabled() || !backupManagerService.isSetupComplete()) {
                 // Backups are globally disabled, so don't proceed.
                 if (DEBUG) {
                     Slog.i(TAG, "full backup requested but enabled=" + backupManagerService
                             .isEnabled()
-                            + " provisioned=" + backupManagerService.isProvisioned()
+                            + " setupComplete=" + backupManagerService.isSetupComplete()
                             + "; ignoring");
                 }
                 int monitoringEvent;
-                if (backupManagerService.isProvisioned()) {
+                if (backupManagerService.isSetupComplete()) {
                     monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED;
                 } else {
                     monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
diff --git a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
deleted file mode 100644
index 7e2ac79..0000000
--- a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
+++ /dev/null
@@ -1,62 +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.backup.internal;
-
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.TAG;
-
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.util.Slog;
-
-import com.android.server.backup.KeyValueBackupJob;
-import com.android.server.backup.UserBackupManagerService;
-
-public class ProvisionedObserver extends ContentObserver {
-
-    private UserBackupManagerService backupManagerService;
-
-    public ProvisionedObserver(
-            UserBackupManagerService backupManagerService, Handler handler) {
-        super(handler);
-        this.backupManagerService = backupManagerService;
-    }
-
-    public void onChange(boolean selfChange) {
-        final boolean wasProvisioned = backupManagerService.isProvisioned();
-        final boolean isProvisioned = backupManagerService.deviceIsProvisioned();
-        // latch: never unprovision
-        backupManagerService.setProvisioned(wasProvisioned || isProvisioned);
-        if (MORE_DEBUG) {
-            Slog.d(TAG, "Provisioning change: was=" + wasProvisioned
-                    + " is=" + isProvisioned + " now=" + backupManagerService.isProvisioned());
-        }
-
-        synchronized (backupManagerService.getQueueLock()) {
-            if (backupManagerService.isProvisioned() && !wasProvisioned
-                    && backupManagerService.isEnabled()) {
-                // we're now good to go, so start the backup alarms
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "Now provisioned, so starting backups");
-                }
-                KeyValueBackupJob.schedule(backupManagerService.getContext(),
-                        backupManagerService.getConstants());
-                backupManagerService.scheduleNextFullBackupJob(0);
-            }
-        }
-    }
-}
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 2a5d913..3b87724 100644
--- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
@@ -59,7 +59,8 @@
                 } else {
                     // Don't run backups now if we're disabled or not yet
                     // fully set up.
-                    if (backupManagerService.isEnabled() && backupManagerService.isProvisioned()) {
+                    if (backupManagerService.isEnabled()
+                            && backupManagerService.isSetupComplete()) {
                         if (!backupManagerService.isBackupRunning()) {
                             if (DEBUG) {
                                 Slog.v(TAG, "Running a backup pass");
@@ -77,8 +78,8 @@
                             Slog.i(TAG, "Backup time but one already running");
                         }
                     } else {
-                        Slog.w(TAG, "Backup pass but e=" + backupManagerService.isEnabled() + " p="
-                                + backupManagerService.isProvisioned());
+                        Slog.w(TAG, "Backup pass but enabled=" + backupManagerService.isEnabled()
+                                + " setupComplete=" + backupManagerService.isSetupComplete());
                     }
                 }
             }
diff --git a/services/backup/java/com/android/server/backup/internal/SetupObserver.java b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
new file mode 100644
index 0000000..defcacc
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.internal;
+
+import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
+import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.getSetupCompleteSettingForUser;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.UserBackupManagerService;
+
+/**
+ * A {@link ContentObserver} for changes to the setting {@link Settings.Secure#USER_SETUP_COMPLETE}
+ * for a particular user.
+ */
+public class SetupObserver extends ContentObserver {
+    private final UserBackupManagerService mUserBackupManagerService;
+    private final Context mContext;
+    private final int mUserId;
+
+    public SetupObserver(UserBackupManagerService userBackupManagerService, Handler handler) {
+        super(handler);
+        mUserBackupManagerService = userBackupManagerService;
+        mContext = userBackupManagerService.getContext();
+        mUserId = userBackupManagerService.getUserId();
+    }
+
+    /**
+     * Callback that executes when the setting {@link Settings.Secure#USER_SETUP_COMPLETE} changes
+     * for the user {@link #mUserId}. If the user is newly setup and backup is enabled, then we
+     * schedule a key value and full backup job for the user. If the user was previously setup and
+     * now the setting has changed to {@code false}, we don't reset the state as having gone through
+     * setup is a non-reversible action.
+     */
+    public void onChange(boolean selfChange) {
+        boolean previousSetupComplete = mUserBackupManagerService.isSetupComplete();
+        boolean newSetupComplete = getSetupCompleteSettingForUser(mContext, mUserId);
+
+        boolean resolvedSetupComplete = previousSetupComplete || newSetupComplete;
+        mUserBackupManagerService.setSetupComplete(resolvedSetupComplete);
+        if (MORE_DEBUG) {
+            Slog.d(
+                    TAG,
+                    "Setup complete change: was="
+                            + previousSetupComplete
+                            + " new="
+                            + newSetupComplete
+                            + " resolved="
+                            + resolvedSetupComplete);
+        }
+
+        synchronized (mUserBackupManagerService.getQueueLock()) {
+            // Start backup if the user is newly setup and backup is enabled.
+            if (resolvedSetupComplete
+                    && !previousSetupComplete
+                    && mUserBackupManagerService.isEnabled()) {
+                if (MORE_DEBUG) {
+                    Slog.d(TAG, "Setup complete so starting backups");
+                }
+                KeyValueBackupJob.schedule(mContext, mUserBackupManagerService.getConstants());
+                mUserBackupManagerService.scheduleNextFullBackupJob(0);
+            }
+        }
+    }
+}
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 0d26ea5..45a398f 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -678,12 +678,13 @@
      * Returns whether the package is in the list of the packages for which clear app data should
      * be called despite the fact that they have backup agent.
      *
-     * <p>The list is read from {@link Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
+     * <p>The list is read from {@link Settings.Secure#PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
      */
     private boolean shouldForceClearAppDataOnFullRestore(String packageName) {
-        String packageListString = Settings.Secure.getString(
+        String packageListString = Settings.Secure.getStringForUser(
                 mBackupManagerService.getContext().getContentResolver(),
-                Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
+                Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
+                mBackupManagerService.getUserId());
         if (TextUtils.isEmpty(packageListString)) {
             return false;
         }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 2346cfc..5cea56e 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -64,7 +64,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Message;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -87,6 +86,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
 import com.android.server.location.AbstractLocationProvider;
 import com.android.server.location.ActivityRecognitionProxy;
 import com.android.server.location.GeocoderProxy;
@@ -117,6 +117,11 @@
 import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
 
 /**
  * The service class that manages LocationProviders and issues location
@@ -171,10 +176,7 @@
     private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
 
     private final Context mContext;
-    private final AppOpsManager mAppOps;
-
-    // used internally for synchronization
-    private final Object mLock = new Object();
+    private AppOpsManager mAppOps;
 
     // --- fields below are final after systemRunning() ---
     private LocationFudger mLocationFudger;
@@ -186,7 +188,7 @@
     private GeocoderProxy mGeocodeProvider;
     private GnssStatusListenerHelper mGnssStatusProvider;
     private INetInitiatedListener mNetInitiatedListener;
-    private LocationWorkerHandler mLocationHandler;
+    private final Handler mHandler;
     private PassiveProvider mPassiveProvider;  // track passive provider for special cases
     private LocationBlacklist mBlacklist;
     private GnssMeasurementsProvider mGnssMeasurementsProvider;
@@ -195,8 +197,6 @@
     private boolean mLocationControllerExtraPackageEnabled;
     private IGpsGeofenceHardware mGpsGeofenceProxy;
 
-    // --- fields below are protected by mLock ---
-
     // Mock (test) providers
     private final HashMap<String, MockProvider> mMockProviders =
             new HashMap<>();
@@ -205,8 +205,8 @@
     private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
 
     // currently installed providers (with mocks replacing real providers)
-    private final ArrayList<LocationProvider> mProviders =
-            new ArrayList<>();
+    private final CopyOnWriteArrayList<LocationProvider> mProviders =
+            new CopyOnWriteArrayList<>();
 
     // real providers, saved here when mocked out
     private final HashMap<String, LocationProvider> mRealProviders =
@@ -232,8 +232,8 @@
 
     // all providers that operate over proxy, for authorizing incoming location and whitelisting
     // throttling
-    private final ArrayList<LocationProviderProxy> mProxyProviders =
-            new ArrayList<>();
+    private final CopyOnWriteArrayList<LocationProviderProxy> mProxyProviders =
+            new CopyOnWriteArrayList<>();
 
     private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
 
@@ -246,9 +246,6 @@
     private int mCurrentUserId = UserHandle.USER_SYSTEM;
     private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
 
-    // Maximum age of last location returned to clients with foreground-only location permissions.
-    private long mLastLocationMaxAgeMs;
-
     private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
 
     private GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
@@ -261,7 +258,7 @@
     public LocationManagerService(Context context) {
         super();
         mContext = context;
-        mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        mHandler = BackgroundThread.getHandler();
 
         // Let the package manager query which are the default location
         // providers as they get certain permissions granted by default.
@@ -271,132 +268,92 @@
                 userId -> mContext.getResources().getStringArray(
                         com.android.internal.R.array.config_locationProviderPackageNames));
 
-        if (D) Log.d(TAG, "Constructed");
-
         // most startup is deferred until systemRunning()
     }
 
     public void systemRunning() {
-        synchronized (mLock) {
-            if (D) Log.d(TAG, "systemRunning()");
+        runInternal(this::initialize);
+    }
 
-            // fetch package manager
-            mPackageManager = mContext.getPackageManager();
+    private void initialize() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
-            // fetch power manager
-            mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mPackageManager = mContext.getPackageManager();
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
 
-            // fetch activity manager
-            mActivityManager
-                    = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        // prepare mHandler's dependents
+        mLocationFudger = new LocationFudger(mContext, mHandler);
+        mBlacklist = new LocationBlacklist(mContext, mHandler);
+        mBlacklist.init();
+        mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
 
-            // prepare worker thread
-            mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
+        mAppOps.startWatchingMode(
+                AppOpsManager.OP_COARSE_LOCATION,
+                null,
+                AppOpsManager.WATCH_FOREGROUND_CHANGES,
+                new AppOpsManager.OnOpChangedInternalListener() {
+                    public void onOpChanged(int op, String packageName) {
+                        mHandler.post(() -> onAppOpChanged());
+                    }
+                });
 
-            // prepare mLocationHandler's dependents
-            mLocationFudger = new LocationFudger(mContext, mLocationHandler);
-            mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
-            mBlacklist.init();
-            mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
+        mPackageManager.addOnPermissionsChangeListener(
+                uid -> runInternal(this::onPermissionsChanged));
 
-            // Monitor for app ops mode changes.
-            AppOpsManager.OnOpChangedListener callback
-                    = new AppOpsManager.OnOpChangedInternalListener() {
-                public void onOpChanged(int op, String packageName) {
-                            mLocationHandler.post(() -> {
-                                synchronized (mLock) {
-                                    for (Receiver receiver : mReceivers.values()) {
-                                        receiver.updateMonitoring(true);
-                                    }
-                                    applyAllProviderRequirementsLocked();
-                                }
-                            });
-                }
-            };
-            mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
-                    AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
+        mActivityManager.addOnUidImportanceListener(
+                (uid, importance) -> mHandler.post(
+                        () -> onUidImportanceChanged(uid, importance)),
+                FOREGROUND_IMPORTANCE_CUTOFF);
 
-            PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
-                synchronized (mLock) {
-                    applyAllProviderRequirementsLocked();
-                }
-            };
-            mPackageManager.addOnPermissionsChangeListener(permissionListener);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        updateUserProfiles(mCurrentUserId);
 
-            // listen for background/foreground changes
-            ActivityManager.OnUidImportanceListener uidImportanceListener =
-                    (uid, importance) -> mLocationHandler.post(
-                            () -> onUidImportanceChanged(uid, importance));
-            mActivityManager.addOnUidImportanceListener(uidImportanceListener,
-                    FOREGROUND_IMPORTANCE_CUTOFF);
+        updateBackgroundThrottlingWhitelist();
 
-            mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-            updateUserProfiles(mCurrentUserId);
-
-            updateBackgroundThrottlingWhitelistLocked();
-            updateLastLocationMaxAgeLocked();
-
-            // prepare providers
-            loadProvidersLocked();
-            updateProvidersSettingsLocked();
-            for (LocationProvider provider : mProviders) {
-                applyRequirementsLocked(provider.getName());
-            }
+        // prepare providers
+        loadProvidersLocked();
+        updateProvidersSettings();
+        for (LocationProvider provider : mProviders) {
+            applyRequirements(provider.getName());
         }
 
         // listen for settings changes
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
-                new ContentObserver(mLocationHandler) {
+                new ContentObserver(mHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            updateProvidersSettingsLocked();
-                        }
+                        onProviderAllowedChanged();
                     }
                 }, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS),
                 true,
-                new ContentObserver(mLocationHandler) {
+                new ContentObserver(mHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            for (LocationProvider provider : mProviders) {
-                                applyRequirementsLocked(provider.getName());
-                            }
-                        }
+                        onBackgroundThrottleIntervalChanged();
                     }
                 }, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS),
-                true,
-                new ContentObserver(mLocationHandler) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            updateLastLocationMaxAgeLocked();
-                        }
-                    }
-                }
-        );
-        mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(
                         Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
                 true,
-                new ContentObserver(mLocationHandler) {
+                new ContentObserver(mHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            updateBackgroundThrottlingWhitelistLocked();
-                            for (LocationProvider provider : mProviders) {
-                                applyRequirementsLocked(provider.getName());
-                            }
-                        }
+                        onBackgroundThrottleWhitelistChanged();
                     }
                 }, UserHandle.USER_ALL);
 
-        mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
+        new PackageMonitor() {
+            @Override
+            public void onPackageDisappeared(String packageName, int reason) {
+                LocationManagerService.this.onPackageDisappeared(packageName);
+            }
+        }.register(mContext, mHandler.getLooper(), true);
 
         // listen for user change
         IntentFilter intentFilter = new IntentFilter();
@@ -415,73 +372,170 @@
                     updateUserProfiles(mCurrentUserId);
                 }
             }
-        }, UserHandle.ALL, intentFilter, null, mLocationHandler);
+        }, UserHandle.ALL, intentFilter, null, mHandler);
+    }
+
+    // will block until completion and propagate exceptions, and thus should be used from binder
+    // threads, particuarily binder threads from components that sit above LMS (ie, not location
+    // providers).
+    private void runFromBinderBlocking(Runnable runnable) throws RemoteException {
+        runFromBinderBlocking(Executors.callable(runnable));
+    }
+
+    // will block until completion and propagate exceptions, and thus should be used from binder
+    // threads, particuarily binder threads from components that sit above LMS (ie, not location
+    // providers).
+    private <T> T runFromBinderBlocking(Callable<T> callable) throws RemoteException {
+        FutureTask<T> task = new FutureTask<>(callable);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            runInternal(task);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        try {
+            return task.get();
+        } catch (ExecutionException e) {
+            // binder calls can handle 3 types of exceptions, runtimeexception and error (which can
+            // be thrown any time), and remote exception. we transfer all of these exceptions from
+            // the execution thread to the binder thread so that they may propagate normally. note
+            // that we are loosing some context in doing so (losing the stack trace from the binder
+            // thread).
+            if (e.getCause() instanceof RemoteException) {
+                throw (RemoteException) e.getCause();
+            } else if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            } else if (e.getCause() instanceof Error) {
+                throw (Error) e.getCause();
+            }
+
+            // callers should not throw checked exceptions
+            Log.wtf(TAG, "caller threw checked exception", e);
+            throw new UnsupportedOperationException(e);
+        } catch (InterruptedException e) {
+            throw new RemoteException("Binder call interrupted", e, true, true);
+        }
+    }
+
+    // will return immediately and will not propagate exceptions. should be used for non-binder work
+    // that needs to be shifted onto the location thread, primarily listeners that do not support
+    // running on arbitrary threads.
+    private void runInternal(Runnable runnable) {
+        // it would be a better use of resources to use locks to manage cross thread access to
+        // various pieces on information. however, the history of the location package has mostly
+        // shown that this is difficult to maintain in a multi-dev environment, and tends to always
+        // lead towards the use of uber-locks and deadlocks. using a single thread to run everything
+        // is more understandable for most devs, and seems less likely to result in future bugs
+        if (Looper.myLooper() == mHandler.getLooper()) {
+            runnable.run();
+        } else {
+            mHandler.post(runnable);
+        }
+    }
+
+    private void onAppOpChanged() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        for (Receiver receiver : mReceivers.values()) {
+            receiver.updateMonitoring(true);
+        }
+        for (LocationProvider p : mProviders) {
+            applyRequirements(p.getName());
+        }
+    }
+
+    private void onPermissionsChanged() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        for (LocationProvider p : mProviders) {
+            applyRequirements(p.getName());
+        }
+    }
+
+    private void onProviderAllowedChanged() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        updateProvidersSettings();
+    }
+
+    private void onPackageDisappeared(String packageName) {
+        ArrayList<Receiver> deadReceivers = null;
+
+        for (Receiver receiver : mReceivers.values()) {
+            if (receiver.mIdentity.mPackageName.equals(packageName)) {
+                if (deadReceivers == null) {
+                    deadReceivers = new ArrayList<>();
+                }
+                deadReceivers.add(receiver);
+            }
+        }
+
+        // perform removal outside of mReceivers loop
+        if (deadReceivers != null) {
+            for (Receiver receiver : deadReceivers) {
+                removeUpdates(receiver);
+            }
+        }
     }
 
     private void onUidImportanceChanged(int uid, int importance) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         boolean foreground = isImportanceForeground(importance);
         HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
-        synchronized (mLock) {
-            for (Entry<String, ArrayList<UpdateRecord>> entry
-                    : mRecordsByProvider.entrySet()) {
-                String provider = entry.getKey();
-                for (UpdateRecord record : entry.getValue()) {
-                    if (record.mReceiver.mIdentity.mUid == uid
-                            && record.mIsForegroundUid != foreground) {
-                        if (D) {
-                            Log.d(TAG, "request from uid " + uid + " is now "
-                                    + (foreground ? "foreground" : "background)"));
-                        }
-                        record.updateForeground(foreground);
-
-                        if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
-                            affectedProviders.add(provider);
-                        }
-                    }
-                }
-            }
-            for (String provider : affectedProviders) {
-                applyRequirementsLocked(provider);
-            }
-
-            for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
-                Identity callerIdentity = entry.getValue();
-                if (callerIdentity.mUid == uid) {
+        for (Entry<String, ArrayList<UpdateRecord>> entry
+                : mRecordsByProvider.entrySet()) {
+            String provider = entry.getKey();
+            for (UpdateRecord record : entry.getValue()) {
+                if (record.mReceiver.mIdentity.mUid == uid
+                        && record.mIsForegroundUid != foreground) {
                     if (D) {
-                        Log.d(TAG, "gnss measurements listener from uid " + uid
-                                + " is now " + (foreground ? "foreground" : "background)"));
-                    }
-                    if (foreground || isThrottlingExemptLocked(entry.getValue())) {
-                        mGnssMeasurementsProvider.addListener(
-                                IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
-                                callerIdentity.mUid, callerIdentity.mPackageName);
-                    } else {
-                        mGnssMeasurementsProvider.removeListener(
-                                IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
-                    }
-                }
-            }
-
-            for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
-                Identity callerIdentity = entry.getValue();
-                if (callerIdentity.mUid == uid) {
-                    if (D) {
-                        Log.d(TAG, "gnss navigation message listener from uid "
-                                + uid + " is now "
+                        Log.d(TAG, "request from uid " + uid + " is now "
                                 + (foreground ? "foreground" : "background)"));
                     }
-                    if (foreground || isThrottlingExemptLocked(entry.getValue())) {
-                        mGnssNavigationMessageProvider.addListener(
-                                IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
-                                callerIdentity.mUid, callerIdentity.mPackageName);
-                    } else {
-                        mGnssNavigationMessageProvider.removeListener(
-                                IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
+                    record.updateForeground(foreground);
+
+                    if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
+                        affectedProviders.add(provider);
                     }
                 }
             }
+        }
+        for (String provider : affectedProviders) {
+            applyRequirements(provider);
+        }
 
-            // TODO(b/120449926): The GNSS status listeners should be handled similar to the above.
+        for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
+            Identity callerIdentity = entry.getValue();
+            if (callerIdentity.mUid == uid) {
+                if (D) {
+                    Log.d(TAG, "gnss measurements listener from uid " + uid
+                            + " is now " + (foreground ? "foreground" : "background)"));
+                }
+                if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+                    mGnssMeasurementsProvider.addListener(
+                            IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
+                            callerIdentity.mUid, callerIdentity.mPackageName);
+                } else {
+                    mGnssMeasurementsProvider.removeListener(
+                            IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
+                }
+            }
+        }
+
+        for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
+            Identity callerIdentity = entry.getValue();
+            if (callerIdentity.mUid == uid) {
+                if (D) {
+                    Log.d(TAG, "gnss navigation message listener from uid "
+                            + uid + " is now "
+                            + (foreground ? "foreground" : "background)"));
+                }
+                if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+                    mGnssNavigationMessageProvider.addListener(
+                            IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
+                            callerIdentity.mUid, callerIdentity.mPackageName);
+                } else {
+                    mGnssNavigationMessageProvider.removeListener(
+                            IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
+                }
+            }
         }
     }
 
@@ -489,31 +543,33 @@
         return importance <= FOREGROUND_IMPORTANCE_CUTOFF;
     }
 
-    /**
-     * Makes a list of userids that are related to the current user. This is
-     * relevant when using managed profiles. Otherwise the list only contains
-     * the current user.
-     *
-     * @param currentUserId the current user, who might have an alter-ego.
-     */
-    private void updateUserProfiles(int currentUserId) {
-        int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
-        synchronized (mLock) {
-            mCurrentUserProfiles = profileIds;
+    private void onBackgroundThrottleIntervalChanged() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        for (LocationProvider provider : mProviders) {
+            applyRequirements(provider.getName());
         }
     }
 
-    /**
-     * Checks if the specified userId matches any of the current foreground
-     * users stored in mCurrentUserProfiles.
-     */
-    private boolean isCurrentProfile(int userId) {
-        synchronized (mLock) {
-            return ArrayUtils.contains(mCurrentUserProfiles, userId);
+    private void onBackgroundThrottleWhitelistChanged() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        updateBackgroundThrottlingWhitelist();
+        for (LocationProvider provider : mProviders) {
+            applyRequirements(provider.getName());
         }
     }
 
+    private void updateUserProfiles(int currentUserId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        mCurrentUserProfiles = mUserManager.getProfileIdsWithDisabled(currentUserId);
+    }
+
+    private boolean isCurrentProfile(int userId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        return ArrayUtils.contains(mCurrentUserProfiles, userId);
+    }
+
     private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         PackageManager pm = mContext.getPackageManager();
         String systemPackageName = mContext.getPackageName();
         ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs);
@@ -584,12 +640,13 @@
     }
 
     private void loadProvidersLocked() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         // create a passive location provider, which is always enabled
         LocationProvider passiveProviderManager = new LocationProvider(
                 LocationManager.PASSIVE_PROVIDER);
         PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager);
 
-        addProviderLocked(passiveProviderManager);
+        addProvider(passiveProviderManager);
         mPassiveProvider = passiveProvider;
 
         if (GnssLocationProvider.isSupported()) {
@@ -598,14 +655,14 @@
                     LocationManager.GPS_PROVIDER);
             GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
                     gnssProviderManager,
-                    mLocationHandler.getLooper());
+                    mHandler.getLooper());
 
             mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
             mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
             mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
             mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
             mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
-            addProviderLocked(gnssProviderManager);
+            addProvider(gnssProviderManager);
             mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
             mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
             mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
@@ -647,7 +704,7 @@
         if (networkProvider != null) {
             mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
             mProxyProviders.add(networkProvider);
-            addProviderLocked(networkProviderManager);
+            addProvider(networkProviderManager);
         } else {
             Slog.w(TAG, "no network location provider found");
         }
@@ -663,7 +720,7 @@
                 com.android.internal.R.string.config_fusedLocationProviderPackageName,
                 com.android.internal.R.array.config_locationProviderPackageNames);
         if (fusedProvider != null) {
-            addProviderLocked(fusedProviderManager);
+            addProvider(fusedProviderManager);
             mProxyProviders.add(fusedProvider);
             mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
         } else {
@@ -728,28 +785,22 @@
                     Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
                     Integer.parseInt(fragments[8]) /* powerRequirement */,
                     Integer.parseInt(fragments[9]) /* accuracy */);
-            addTestProviderLocked(name, properties);
+            addTestProvider(name, properties);
         }
     }
 
-    /**
-     * Called when the device's active user changes.
-     *
-     * @param userId the new active user's UserId
-     */
     private void switchUser(int userId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (mCurrentUserId == userId) {
             return;
         }
         mBlacklist.switchUser(userId);
-        mLocationHandler.removeMessages(MSG_LOCATION_CHANGED);
-        synchronized (mLock) {
-            mLastLocation.clear();
-            mLastLocationCoarseInterval.clear();
-            updateUserProfiles(userId);
-            updateProvidersSettingsLocked();
-            mCurrentUserId = userId;
-        }
+        mHandler.removeMessages(MSG_LOCATION_CHANGED);
+        mLastLocation.clear();
+        mLastLocationCoarseInterval.clear();
+        updateUserProfiles(userId);
+        updateProvidersSettings();
+        mCurrentUserId = userId;
     }
 
     private static final class Identity {
@@ -808,10 +859,13 @@
         }
 
         public void setRequest(ProviderRequest request, WorkSource workSource) {
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             mProvider.setRequest(request, workSource);
         }
 
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
             pw.println(mName + " provider:");
             pw.println(" setting=" + mSettingsEnabled);
             pw.println(" enabled=" + mEnabled);
@@ -820,34 +874,38 @@
         }
 
         public long getStatusUpdateTime() {
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             return mProvider.getStatusUpdateTime();
         }
 
         public int getStatus(Bundle extras) {
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             return mProvider.getStatus(extras);
         }
 
         public void sendExtraCommand(String command, Bundle extras) {
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             mProvider.sendExtraCommand(command, extras);
         }
 
         // called from any thread
         @Override
         public void onReportLocation(Location location) {
-            runOnHandler(() -> LocationManagerService.this.reportLocation(location,
+            // no security check necessary because this is coming from an internal-only interface
+            runInternal(() -> LocationManagerService.this.handleLocationChanged(location,
                     mProvider == mPassiveProvider));
         }
 
         // called from any thread
         @Override
         public void onReportLocation(List<Location> locations) {
-            runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
+            LocationManagerService.this.reportLocationBatch(locations);
         }
 
         // called from any thread
         @Override
         public void onSetEnabled(boolean enabled) {
-            runOnHandler(() -> {
+            runInternal(() -> {
                 if (enabled == mEnabled) {
                     return;
                 }
@@ -872,18 +930,16 @@
                         Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
                         "-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
 
-                synchronized (mLock) {
-                    if (!enabled) {
-                        // If any provider has been disabled, clear all last locations for all
-                        // providers. This is to be on the safe side in case a provider has location
-                        // derived from this disabled provider.
-                        mLastLocation.clear();
-                        mLastLocationCoarseInterval.clear();
-                    }
-
-                    updateProviderListenersLocked(mName);
+                if (!enabled) {
+                    // If any provider has been disabled, clear all last locations for all
+                    // providers. This is to be on the safe side in case a provider has location
+                    // derived from this disabled provider.
+                    mLastLocation.clear();
+                    mLastLocationCoarseInterval.clear();
                 }
 
+                updateProviderListeners(mName);
+
                 mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
                         UserHandle.ALL);
             });
@@ -891,34 +947,27 @@
 
         @Override
         public void onSetProperties(ProviderProperties properties) {
-            runOnHandler(() -> mProperties = properties);
+            runInternal(() -> {
+                mProperties = properties;
+            });
         }
 
         private void setSettingsEnabled(boolean enabled) {
-            synchronized (mLock) {
-                if (mSettingsEnabled == enabled) {
-                    return;
-                }
-
-                mSettingsEnabled = enabled;
-                if (!mSettingsEnabled) {
-                    // if any provider has been disabled, clear all last locations for all
-                    // providers. this is to be on the safe side in case a provider has location
-                    // derived from this disabled provider.
-                    mLastLocation.clear();
-                    mLastLocationCoarseInterval.clear();
-                    updateProviderListenersLocked(mName);
-                } else if (mEnabled) {
-                    updateProviderListenersLocked(mName);
-                }
+            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+            if (mSettingsEnabled == enabled) {
+                return;
             }
-        }
 
-        private void runOnHandler(Runnable runnable) {
-            if (Looper.myLooper() == mLocationHandler.getLooper()) {
-                runnable.run();
-            } else {
-                mLocationHandler.post(runnable);
+            mSettingsEnabled = enabled;
+            if (!mSettingsEnabled) {
+                // if any provider has been disabled, clear all last locations for all
+                // providers. this is to be on the safe side in case a provider has location
+                // derived from this disabled provider.
+                mLastLocation.clear();
+                mLastLocationCoarseInterval.clear();
+                updateProviderListeners(mName);
+            } else if (mEnabled) {
+                updateProviderListeners(mName);
             }
         }
     }
@@ -1022,7 +1071,7 @@
                 // See if receiver has any enabled update records.  Also note if any update records
                 // are high power (has a high power provider with an interval under a threshold).
                 for (UpdateRecord updateRecord : mUpdateRecords.values()) {
-                    if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
+                    if (isAllowedByUserSettingsForUser(updateRecord.mProvider,
                             mCurrentUserId)) {
                         requestingLocation = true;
                         LocationManagerService.LocationProvider locationProvider
@@ -1122,7 +1171,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
+                        mPendingIntent.send(mContext, 0, statusChanged, this, mHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1158,7 +1207,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
+                        mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1201,7 +1250,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
+                        mPendingIntent.send(mContext, 0, providerIntent, this, mHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1219,12 +1268,12 @@
         public void binderDied() {
             if (D) Log.d(TAG, "Location listener died");
 
-            synchronized (mLock) {
-                removeUpdatesLocked(this);
-            }
-            synchronized (this) {
-                clearPendingBroadcastsLocked();
-            }
+            runInternal(() -> {
+                removeUpdates(this);
+                synchronized (this) {
+                    clearPendingBroadcastsLocked();
+                }
+            });
         }
 
         @Override
@@ -1261,28 +1310,22 @@
     }
 
     @Override
-    public void locationCallbackFinished(ILocationListener listener) {
+    public void locationCallbackFinished(ILocationListener listener) throws RemoteException {
         //Do not use getReceiverLocked here as that will add the ILocationListener to
         //the receiver list if it is not found.  If it is not found then the
         //LocationListener was removed when it had a pending broadcast and should
         //not be added back.
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             IBinder binder = listener.asBinder();
             Receiver receiver = mReceivers.get(binder);
             if (receiver != null) {
                 synchronized (receiver) {
-                    // so wakelock calls will succeed
-                    long identity = Binder.clearCallingIdentity();
                     receiver.decrementPendingBroadcastsLocked();
-                    Binder.restoreCallingIdentity(identity);
                 }
             }
-        }
+        });
     }
 
-    /**
-     * Returns the year of the GNSS hardware.
-     */
     @Override
     public int getGnssYearOfHardware() {
         if (mGnssSystemInfoProvider != null) {
@@ -1292,10 +1335,6 @@
         }
     }
 
-
-    /**
-     * Returns the model name of the GNSS hardware.
-     */
     @Override
     @Nullable
     public String getGnssHardwareModelName() {
@@ -1306,10 +1345,6 @@
         }
     }
 
-    /**
-     * Runs some checks for GNSS (FINE) level permissions, used by several methods which directly
-     * (try to) access GNSS information at this layer.
-     */
     private boolean hasGnssPermissions(String packageName) {
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkResolutionLevelIsSufficientForProviderUse(
@@ -1329,9 +1364,6 @@
         return hasLocationAccess;
     }
 
-    /**
-     * Returns the GNSS batching size, if available.
-     */
     @Override
     public int getGnssBatchSize(String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1344,10 +1376,6 @@
         }
     }
 
-    /**
-     * Adds a callback for GNSS Batching events, if permissions allow, which are transported
-     * to potentially multiple listeners by the BatchedLocationCallbackTransport above this.
-     */
     @Override
     public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1391,9 +1419,6 @@
         }
     }
 
-    /**
-     * Removes callback for GNSS batching
-     */
     @Override
     public void removeGnssBatchingCallback() {
         try {
@@ -1408,10 +1433,6 @@
         mGnssBatchingDeathCallback = null;
     }
 
-
-    /**
-     * Starts GNSS batching, if available.
-     */
     @Override
     public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1432,9 +1453,6 @@
         return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
     }
 
-    /**
-     * Flushes a GNSS batch in progress
-     */
     @Override
     public void flushGnssBatch(String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1454,9 +1472,6 @@
         }
     }
 
-    /**
-     * Stops GNSS batching
-     */
     @Override
     public boolean stopGnssBatch() {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1475,7 +1490,7 @@
         checkCallerIsProvider();
 
         // Currently used only for GNSS locations - update permissions check if changed
-        if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
+        if (isAllowedByUserSettingsForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
             if (mGnssBatchingCallback == null) {
                 Slog.e(TAG, "reportLocationBatch() called without active Callback");
                 return;
@@ -1490,35 +1505,28 @@
         }
     }
 
-    private void addProviderLocked(LocationProvider provider) {
+    private void addProvider(LocationProvider provider) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         mProviders.add(provider);
         mProvidersByName.put(provider.getName(), provider);
     }
 
-    private void removeProviderLocked(LocationProvider provider) {
+    private void removeProvider(LocationProvider provider) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         mProviders.remove(provider);
         mProvidersByName.remove(provider.getName());
     }
 
-    /**
-     * Returns "true" if access to the specified location provider is allowed by the specified
-     * user's settings. Access to all location providers is forbidden to non-location-provider
-     * processes belonging to background users.
-     *
-     * @param provider the name of the location provider
-     * @param userId   the user id to query
-     */
-    private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
+    private boolean isAllowedByUserSettingsForUser(String provider, int userId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
-            return isLocationEnabledForUser(userId);
+            return isLocationEnabledForUserInternal(userId);
         }
         if (LocationManager.FUSED_PROVIDER.equals(provider)) {
-            return isLocationEnabledForUser(userId);
+            return isLocationEnabledForUserInternal(userId);
         }
-        synchronized (mLock) {
-            if (mMockProviders.containsKey(provider)) {
-                return isLocationEnabledForUser(userId);
-            }
+        if (mMockProviders.containsKey(provider)) {
+            return isLocationEnabledForUserInternal(userId);
         }
 
         long identity = Binder.clearCallingIdentity();
@@ -1533,29 +1541,14 @@
         }
     }
 
-
-    /**
-     * Returns "true" if access to the specified location provider is allowed by the specified
-     * user's settings. Access to all location providers is forbidden to non-location-provider
-     * processes belonging to background users.
-     *
-     * @param provider the name of the location provider
-     * @param uid      the requestor's UID
-     * @param userId   the user id to query
-     */
-    private boolean isAllowedByUserSettingsLocked(String provider, int uid, int userId) {
+    private boolean isAllowedByUserSettings(String provider, int uid, int userId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
             return false;
         }
-        return isAllowedByUserSettingsLockedForUser(provider, userId);
+        return isAllowedByUserSettingsForUser(provider, userId);
     }
 
-    /**
-     * Returns the permission string associated with the specified resolution level.
-     *
-     * @param resolutionLevel the resolution level
-     * @return the permission string
-     */
     private String getResolutionPermission(int resolutionLevel) {
         switch (resolutionLevel) {
             case RESOLUTION_LEVEL_FINE:
@@ -1567,13 +1560,6 @@
         }
     }
 
-    /**
-     * Returns the resolution level allowed to the given PID/UID pair.
-     *
-     * @param pid the PID
-     * @param uid the UID
-     * @return resolution level allowed to the pid/uid pair
-     */
     private int getAllowedResolutionLevel(int pid, int uid) {
         if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
                 pid, uid) == PERMISSION_GRANTED) {
@@ -1586,32 +1572,16 @@
         }
     }
 
-    /**
-     * Returns the resolution level allowed to the caller
-     *
-     * @return resolution level allowed to caller
-     */
     private int getCallerAllowedResolutionLevel() {
         return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
     }
 
-    /**
-     * Throw SecurityException if specified resolution level is insufficient to use geofences.
-     *
-     * @param allowedResolutionLevel resolution level allowed to caller
-     */
     private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
         if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
             throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
         }
     }
 
-    /**
-     * Return the minimum resolution level required to use the specified location provider.
-     *
-     * @param provider the name of the location provider
-     * @return minimum resolution level required for provider
-     */
     private int getMinimumResolutionLevelForProviderUse(String provider) {
         if (LocationManager.GPS_PROVIDER.equals(provider) ||
                 LocationManager.PASSIVE_PROVIDER.equals(provider)) {
@@ -1643,13 +1613,6 @@
         return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
     }
 
-    /**
-     * Throw SecurityException if specified resolution level is insufficient to use the named
-     * location provider.
-     *
-     * @param allowedResolutionLevel resolution level allowed to caller
-     * @param providerName           the name of the location provider
-     */
     private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel,
             String providerName) {
         int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName);
@@ -1668,20 +1631,6 @@
         }
     }
 
-    /**
-     * Throw SecurityException if WorkSource use is not allowed (i.e. can't blame other packages
-     * for battery).
-     */
-    private void checkDeviceStatsAllowed() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.UPDATE_DEVICE_STATS, null);
-    }
-
-    private void checkUpdateAppOpsAllowed() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.UPDATE_APP_OPS_STATS, null);
-    }
-
     public static int resolutionLevelToOp(int allowedResolutionLevel) {
         if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
             if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
@@ -1739,19 +1688,15 @@
      */
     @Override
     public List<String> getAllProviders() {
-        ArrayList<String> out;
-        synchronized (mLock) {
-            out = new ArrayList<>(mProviders.size());
-            for (LocationProvider provider : mProviders) {
-                String name = provider.getName();
-                if (LocationManager.FUSED_PROVIDER.equals(name)) {
-                    continue;
-                }
-                out.add(name);
+        ArrayList<String> providers = new ArrayList<>(mProviders.size());
+        for (LocationProvider provider : mProviders) {
+            String name = provider.getName();
+            if (LocationManager.FUSED_PROVIDER.equals(name)) {
+                continue;
             }
+            providers.add(name);
         }
-        if (D) Log.d(TAG, "getAllProviders()=" + out);
-        return out;
+        return providers;
     }
 
     /**
@@ -1760,39 +1705,33 @@
      * Can return passive provider, but never returns fused provider.
      */
     @Override
-    public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
+    public List<String> getProviders(Criteria criteria, boolean enabledOnly)
+            throws RemoteException {
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
-        ArrayList<String> out;
         int uid = Binder.getCallingUid();
-        long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                out = new ArrayList<>(mProviders.size());
-                for (LocationProvider provider : mProviders) {
-                    String name = provider.getName();
-                    if (LocationManager.FUSED_PROVIDER.equals(name)) {
-                        continue;
-                    }
-                    if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
-                        if (enabledOnly
-                                && !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
-                            continue;
-                        }
-                        if (criteria != null
-                                && !android.location.LocationProvider.propertiesMeetCriteria(
-                                name, provider.getProperties(), criteria)) {
-                            continue;
-                        }
-                        out.add(name);
-                    }
+        return runFromBinderBlocking(() -> {
+            ArrayList<String> providers = new ArrayList<>(mProviders.size());
+            for (LocationProvider provider : mProviders) {
+                String name = provider.getName();
+                if (LocationManager.FUSED_PROVIDER.equals(name)) {
+                    continue;
                 }
+                if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUse(name)) {
+                    continue;
+                }
+                if (enabledOnly
+                        && !isAllowedByUserSettings(name, uid, mCurrentUserId)) {
+                    continue;
+                }
+                if (criteria != null
+                        && !android.location.LocationProvider.propertiesMeetCriteria(
+                        name, provider.getProperties(), criteria)) {
+                    continue;
+                }
+                providers.add(name);
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-
-        if (D) Log.d(TAG, "getProviders()=" + out);
-        return out;
+            return providers;
+        });
     }
 
     /**
@@ -1803,59 +1742,51 @@
      * some simplified logic.
      */
     @Override
-    public String getBestProvider(Criteria criteria, boolean enabledOnly) {
-        String result;
-
+    public String getBestProvider(Criteria criteria, boolean enabledOnly) throws RemoteException {
         List<String> providers = getProviders(criteria, enabledOnly);
-        if (!providers.isEmpty()) {
-            result = pickBest(providers);
-            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
-            return result;
-        }
-        providers = getProviders(null, enabledOnly);
-        if (!providers.isEmpty()) {
-            result = pickBest(providers);
-            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
-            return result;
+        if (providers.isEmpty()) {
+            providers = getProviders(null, enabledOnly);
         }
 
-        if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
+        if (!providers.isEmpty()) {
+            if (providers.contains(LocationManager.GPS_PROVIDER)) {
+                return LocationManager.GPS_PROVIDER;
+            } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
+                return LocationManager.NETWORK_PROVIDER;
+            } else {
+                return providers.get(0);
+            }
+        }
+
         return null;
     }
 
-    private String pickBest(List<String> providers) {
-        if (providers.contains(LocationManager.GPS_PROVIDER)) {
-            return LocationManager.GPS_PROVIDER;
-        } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
-            return LocationManager.NETWORK_PROVIDER;
-        } else {
-            return providers.get(0);
-        }
-    }
-
     @Override
-    public boolean providerMeetsCriteria(String provider, Criteria criteria) {
-        LocationProvider p = mProvidersByName.get(provider);
-        if (p == null) {
-            throw new IllegalArgumentException("provider=" + provider);
-        }
+    public boolean providerMeetsCriteria(String provider, Criteria criteria)
+            throws RemoteException {
+        return runFromBinderBlocking(() -> {
+            LocationProvider p = mProvidersByName.get(provider);
+            if (p == null) {
+                throw new IllegalArgumentException("provider=" + provider);
+            }
 
-        boolean result = android.location.LocationProvider.propertiesMeetCriteria(
-                p.getName(), p.getProperties(), criteria);
-        if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
-        return result;
+            return android.location.LocationProvider.propertiesMeetCriteria(
+                    p.getName(), p.getProperties(), criteria);
+        });
     }
 
-    private void updateProvidersSettingsLocked() {
+    private void updateProvidersSettings() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         for (LocationProvider p : mProviders) {
-            p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
+            p.setSettingsEnabled(isAllowedByUserSettingsForUser(p.getName(), mCurrentUserId));
         }
 
         mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
                 UserHandle.ALL);
     }
 
-    private void updateProviderListenersLocked(String provider) {
+    private void updateProviderListeners(String provider) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
 
@@ -1880,14 +1811,15 @@
 
         if (deadReceivers != null) {
             for (int i = deadReceivers.size() - 1; i >= 0; i--) {
-                removeUpdatesLocked(deadReceivers.get(i));
+                removeUpdates(deadReceivers.get(i));
             }
         }
 
-        applyRequirementsLocked(provider);
+        applyRequirements(provider);
     }
 
-    private void applyRequirementsLocked(String provider) {
+    private void applyRequirements(String provider) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
 
@@ -1993,14 +1925,13 @@
     }
 
     @Override
-    public String[] getBackgroundThrottlingWhitelist() {
-        synchronized (mLock) {
-            return mBackgroundThrottlePackageWhitelist.toArray(
-                    new String[0]);
-        }
+    public String[] getBackgroundThrottlingWhitelist() throws RemoteException {
+        return runFromBinderBlocking(
+                () -> mBackgroundThrottlePackageWhitelist.toArray(new String[0]));
     }
 
-    private void updateBackgroundThrottlingWhitelistLocked() {
+    private void updateBackgroundThrottlingWhitelist() {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         String setting = Settings.Global.getString(
                 mContext.getContentResolver(),
                 Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
@@ -2015,15 +1946,8 @@
                 Arrays.asList(setting.split(",")));
     }
 
-    private void updateLastLocationMaxAgeLocked() {
-        mLastLocationMaxAgeMs =
-                Settings.Global.getLong(
-                        mContext.getContentResolver(),
-                        Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
-                        DEFAULT_LAST_LOCATION_MAX_AGE_MS);
-    }
-
     private boolean isThrottlingExemptLocked(Identity identity) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (identity.mUid == Process.SYSTEM_UID) {
             return true;
         }
@@ -2105,7 +2029,7 @@
 
             // and also remove the Receiver if it has no more update records
             if (receiverRecords.size() == 0) {
-                removeUpdatesLocked(mReceiver);
+                removeUpdates(mReceiver);
             }
         }
 
@@ -2119,8 +2043,9 @@
         }
     }
 
-    private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
+    private Receiver getReceiver(ILocationListener listener, int pid, int uid,
             String packageName, WorkSource workSource, boolean hideFromAppOps) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         IBinder binder = listener.asBinder();
         Receiver receiver = mReceivers.get(binder);
         if (receiver == null) {
@@ -2137,8 +2062,9 @@
         return receiver;
     }
 
-    private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
+    private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName,
             WorkSource workSource, boolean hideFromAppOps) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         Receiver receiver = mReceivers.get(intent);
         if (receiver == null) {
             receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
@@ -2202,29 +2128,9 @@
         throw new SecurityException("invalid package name: " + packageName);
     }
 
-    private void checkPendingIntent(PendingIntent intent) {
-        if (intent == null) {
-            throw new IllegalArgumentException("invalid pending intent: " + null);
-        }
-    }
-
-    private Receiver checkListenerOrIntentLocked(ILocationListener listener, PendingIntent intent,
-            int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) {
-        if (intent == null && listener == null) {
-            throw new IllegalArgumentException("need either listener or intent");
-        } else if (intent != null && listener != null) {
-            throw new IllegalArgumentException("cannot register both listener and intent");
-        } else if (intent != null) {
-            checkPendingIntent(intent);
-            return getReceiverLocked(intent, pid, uid, packageName, workSource, hideFromAppOps);
-        } else {
-            return getReceiverLocked(listener, pid, uid, packageName, workSource, hideFromAppOps);
-        }
-    }
-
     @Override
     public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
-            PendingIntent intent, String packageName) {
+            PendingIntent intent, String packageName) throws RemoteException {
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         checkPackageName(packageName);
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
@@ -2232,11 +2138,13 @@
                 request.getProvider());
         WorkSource workSource = request.getWorkSource();
         if (workSource != null && !workSource.isEmpty()) {
-            checkDeviceStatsAllowed();
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.UPDATE_DEVICE_STATS, null);
         }
         boolean hideFromAppOps = request.getHideFromAppOps();
         if (hideFromAppOps) {
-            checkUpdateAppOpsAllowed();
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.UPDATE_APP_OPS_STATS, null);
         }
         boolean callerHasLocationHardwarePermission =
                 mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
@@ -2246,25 +2154,33 @@
 
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
-        // providers may use public location API's, need to clear identity
-        long identity = Binder.clearCallingIdentity();
-        try {
-            // We don't check for MODE_IGNORED here; we will do that when we go to deliver
-            // a location.
-            checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
 
-            synchronized (mLock) {
-                Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
-                        packageName, workSource, hideFromAppOps);
-                requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        // We don't check for MODE_IGNORED here; we will do that when we go to deliver
+        // a location.
+        checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
+
+        if (intent == null && listener == null) {
+            throw new IllegalArgumentException("need either listener or intent");
+        } else if (intent != null && listener != null) {
+            throw new IllegalArgumentException("cannot register both listener and intent");
         }
+
+        runFromBinderBlocking(() -> {
+            Receiver receiver;
+            if (intent != null) {
+                receiver = getReceiver(intent, pid, uid, packageName, workSource,
+                        hideFromAppOps);
+            } else {
+                receiver = getReceiver(listener, pid, uid, packageName, workSource,
+                        hideFromAppOps);
+            }
+            requestLocationUpdates(sanitizedRequest, receiver, uid, packageName);
+        });
     }
 
-    private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
+    private void requestLocationUpdates(LocationRequest request, Receiver receiver,
             int uid, String packageName) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         // Figure out the provider. Either its explicitly request (legacy use cases), or
         // use the fused provider
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2293,7 +2209,7 @@
         }
 
         if (provider.isEnabled()) {
-            applyRequirementsLocked(name);
+            applyRequirements(name);
         } else {
             // Notify the listener that updates are currently disabled
             receiver.callProviderEnabledLocked(name, false);
@@ -2305,27 +2221,31 @@
 
     @Override
     public void removeUpdates(ILocationListener listener, PendingIntent intent,
-            String packageName) {
+            String packageName) throws RemoteException {
         checkPackageName(packageName);
 
-        final int pid = Binder.getCallingPid();
-        final int uid = Binder.getCallingUid();
+        int pid = Binder.getCallingPid();
+        int uid = Binder.getCallingUid();
 
-        synchronized (mLock) {
-            Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
-                    packageName, null, false);
-
-            // providers may use public location API's, need to clear identity
-            long identity = Binder.clearCallingIdentity();
-            try {
-                removeUpdatesLocked(receiver);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
+        if (intent == null && listener == null) {
+            throw new IllegalArgumentException("need either listener or intent");
+        } else if (intent != null && listener != null) {
+            throw new IllegalArgumentException("cannot register both listener and intent");
         }
+
+        runFromBinderBlocking(() -> {
+            Receiver receiver;
+            if (intent != null) {
+                receiver = getReceiver(intent, pid, uid, packageName, null, false);
+            } else {
+                receiver = getReceiver(listener, pid, uid, packageName, null, false);
+            }
+            removeUpdates(receiver);
+        });
     }
 
-    private void removeUpdatesLocked(Receiver receiver) {
+    private void removeUpdates(Receiver receiver) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
 
         if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
@@ -2352,20 +2272,14 @@
 
         // update provider
         for (String provider : providers) {
-            applyRequirementsLocked(provider);
-        }
-    }
-
-    private void applyAllProviderRequirementsLocked() {
-        for (LocationProvider p : mProviders) {
-            applyRequirementsLocked(p.getName());
+            applyRequirements(provider);
         }
     }
 
     @Override
-    public Location getLastLocation(LocationRequest request, String packageName) {
-        if (D) Log.d(TAG, "getLastLocation: " + request);
-        if (request == null) request = DEFAULT_LOCATION_REQUEST;
+    public Location getLastLocation(LocationRequest r, String packageName) throws RemoteException {
+        if (D) Log.d(TAG, "getLastLocation: " + r);
+        LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkPackageName(packageName);
         checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
@@ -2391,68 +2305,60 @@
                 }
                 return null;
             }
-
-            synchronized (mLock) {
-                // Figure out the provider. Either its explicitly request (deprecated API's),
-                // or use the fused provider
-                String name = request.getProvider();
-                if (name == null) name = LocationManager.FUSED_PROVIDER;
-                LocationProvider provider = mProvidersByName.get(name);
-                if (provider == null) return null;
-
-                if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
-
-                Location location;
-                if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
-                    // Make sure that an app with coarse permissions can't get frequent location
-                    // updates by calling LocationManager.getLastKnownLocation repeatedly.
-                    location = mLastLocationCoarseInterval.get(name);
-                } else {
-                    location = mLastLocation.get(name);
-                }
-                if (location == null) {
-                    return null;
-                }
-
-                // Don't return stale location to apps with foreground-only location permission.
-                String op = resolutionLevelToOpStr(allowedResolutionLevel);
-                long locationAgeMs = SystemClock.elapsedRealtime() -
-                        location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
-                if ((locationAgeMs > mLastLocationMaxAgeMs)
-                        && (mAppOps.unsafeCheckOp(op, uid, packageName)
-                        == AppOpsManager.MODE_FOREGROUND)) {
-                    return null;
-                }
-
-                if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
-                    Location noGPSLocation = location.getExtraLocation(
-                            Location.EXTRA_NO_GPS_LOCATION);
-                    if (noGPSLocation != null) {
-                        return new Location(mLocationFudger.getOrCreate(noGPSLocation));
-                    }
-                } else {
-                    return new Location(location);
-                }
-            }
-            return null;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
+
+        return runFromBinderBlocking(() -> {
+            // Figure out the provider. Either its explicitly request (deprecated API's),
+            // or use the fused provider
+            String name = request.getProvider();
+            if (name == null) name = LocationManager.FUSED_PROVIDER;
+            LocationProvider provider = mProvidersByName.get(name);
+            if (provider == null) return null;
+
+            if (!isAllowedByUserSettings(name, uid, mCurrentUserId)) return null;
+
+            Location location;
+            if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+                // Make sure that an app with coarse permissions can't get frequent location
+                // updates by calling LocationManager.getLastKnownLocation repeatedly.
+                location = mLastLocationCoarseInterval.get(name);
+            } else {
+                location = mLastLocation.get(name);
+            }
+            if (location == null) {
+                return null;
+            }
+
+            // Don't return stale location to apps with foreground-only location permission.
+            String op = resolutionLevelToOpStr(allowedResolutionLevel);
+            long locationAgeMs = SystemClock.elapsedRealtime()
+                    - location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
+            if ((locationAgeMs > Settings.Global.getLong(
+                    mContext.getContentResolver(),
+                    Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
+                    DEFAULT_LAST_LOCATION_MAX_AGE_MS))
+                    && (mAppOps.unsafeCheckOp(op, uid, packageName)
+                    == AppOpsManager.MODE_FOREGROUND)) {
+                return null;
+            }
+
+            if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+                Location noGPSLocation = location.getExtraLocation(
+                        Location.EXTRA_NO_GPS_LOCATION);
+                if (noGPSLocation != null) {
+                    return new Location(mLocationFudger.getOrCreate(noGPSLocation));
+                }
+            } else {
+                return new Location(location);
+            }
+            return null;
+        });
     }
 
-    /**
-     * Provides an interface to inject and set the last location if location is not available
-     * currently.
-     *
-     * This helps in cases where the product (Cars for example) has saved the last known location
-     * before powering off.  This interface lets the client inject the saved location while the GPS
-     * chipset is getting its first fix, there by improving user experience.
-     *
-     * @param location - Location object to inject
-     * @return true if update was successful, false if not
-     */
     @Override
-    public boolean injectLocation(Location location) {
+    public boolean injectLocation(Location location) throws RemoteException {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
                 "Location Hardware permission not granted to inject location");
         mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
@@ -2464,29 +2370,31 @@
             }
             return false;
         }
-        LocationProvider p = null;
-        String provider = location.getProvider();
-        if (provider != null) {
-            p = mProvidersByName.get(provider);
-        }
-        if (p == null) {
-            if (D) {
-                Log.d(TAG, "injectLocation(): unknown provider");
+
+        return runFromBinderBlocking(() -> {
+            LocationProvider p = null;
+            String provider = location.getProvider();
+            if (provider != null) {
+                p = mProvidersByName.get(provider);
             }
-            return false;
-        }
-        synchronized (mLock) {
-            if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
+            if (p == null) {
                 if (D) {
-                    Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
+                    Log.d(TAG, "injectLocation(): unknown provider");
+                }
+                return false;
+            }
+            if (!isAllowedByUserSettingsForUser(provider, mCurrentUserId)) {
+                if (D) {
+                    Log.d(TAG, "Location disabled in Settings for current user:"
+                            + mCurrentUserId);
                 }
                 return false;
             } else {
                 // NOTE: If last location is already available, location is not injected.  If
-                // provider's normal source (like a GPS chipset) have already provided an output,
+                // provider's normal source (like a GPS chipset) have already provided an output
                 // there is no need to inject this location.
                 if (mLastLocation.get(provider) == null) {
-                    updateLastLocationLocked(location, provider);
+                    updateLastLocation(location, provider);
                 } else {
                     if (D) {
                         Log.d(TAG, "injectLocation(): Location exists. Not updating");
@@ -2494,8 +2402,8 @@
                     return false;
                 }
             }
-        }
-        return true;
+            return true;
+        });
     }
 
     @Override
@@ -2504,7 +2412,9 @@
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
-        checkPendingIntent(intent);
+        if (intent == null) {
+            throw new IllegalArgumentException("invalid pending intent: " + null);
+        }
         checkPackageName(packageName);
         checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
                 request.getProvider());
@@ -2515,7 +2425,10 @@
         LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
                 callerHasLocationHardwarePermission);
 
-        if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
+        if (D) {
+            Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " "
+                    + intent);
+        }
 
         // geo-fence manager uses the public location API, need to clear identity
         int uid = Binder.getCallingUid();
@@ -2536,7 +2449,9 @@
 
     @Override
     public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
-        checkPendingIntent(intent);
+        if (intent == null) {
+            throw new IllegalArgumentException("invalid pending intent: " + null);
+        }
         checkPackageName(packageName);
 
         if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
@@ -2568,14 +2483,14 @@
 
     @Override
     public boolean addGnssMeasurementsListener(
-            IGnssMeasurementsListener listener, String packageName) {
+            IGnssMeasurementsListener listener, String packageName) throws RemoteException {
         if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
             return false;
         }
 
-        synchronized (mLock) {
-            Identity callerIdentity
-                    = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+        Identity callerIdentity =
+                new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+        return runFromBinderBlocking(() -> {
             // TODO(b/120481270): Register for client death notification and update map.
             mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
             long identity = Binder.clearCallingIdentity();
@@ -2591,7 +2506,7 @@
             }
 
             return true;
-        }
+        });
     }
 
     @Override
@@ -2619,28 +2534,30 @@
     }
 
     @Override
-    public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
+    public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener)
+            throws RemoteException {
         if (mGnssMeasurementsProvider == null) {
             return;
         }
 
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             mGnssMeasurementsListeners.remove(listener.asBinder());
             mGnssMeasurementsProvider.removeListener(listener);
-        }
+        });
     }
 
     @Override
     public boolean addGnssNavigationMessageListener(
             IGnssNavigationMessageListener listener,
-            String packageName) {
+            String packageName) throws RemoteException {
         if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) {
             return false;
         }
 
-        synchronized (mLock) {
-            Identity callerIdentity
-                    = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+        Identity callerIdentity =
+                new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+
+        return runFromBinderBlocking(() -> {
             // TODO(b/120481270): Register for client death notification and update map.
             mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
             long identity = Binder.clearCallingIdentity();
@@ -2656,21 +2573,23 @@
             }
 
             return true;
-        }
+        });
     }
 
     @Override
-    public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
+    public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener)
+            throws RemoteException {
         if (mGnssNavigationMessageProvider != null) {
-            synchronized (mLock) {
+            runFromBinderBlocking(() -> {
                 mGnssNavigationMessageListeners.remove(listener.asBinder());
                 mGnssNavigationMessageProvider.removeListener(listener);
-            }
+            });
         }
     }
 
     @Override
-    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+    public boolean sendExtraCommand(String provider, String command, Bundle extras)
+            throws RemoteException {
         if (provider == null) {
             // throw NullPointerException to remain compatible with previous implementation
             throw new NullPointerException();
@@ -2684,13 +2603,13 @@
             throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
         }
 
-        synchronized (mLock) {
+        return runFromBinderBlocking(() -> {
             LocationProvider p = mProvidersByName.get(provider);
             if (p == null) return false;
 
             p.sendExtraCommand(command, extras);
             return true;
-        }
+        });
     }
 
     @Override
@@ -2707,251 +2626,181 @@
         }
     }
 
-    /**
-     * @return null if the provider does not exist
-     * @throws SecurityException if the provider is not allowed to be
-     *                           accessed by the caller
-     */
     @Override
-    public ProviderProperties getProviderProperties(String provider) {
+    public ProviderProperties getProviderProperties(String provider) throws RemoteException {
         checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
                 provider);
 
-        LocationProvider p;
-        synchronized (mLock) {
-            p = mProvidersByName.get(provider);
-        }
-
-        if (p == null) return null;
-        return p.getProperties();
-    }
-
-    /**
-     * @return null if the provider does not exist
-     * @throws SecurityException if the provider is not allowed to be
-     *                           accessed by the caller
-     */
-    @Override
-    public String getNetworkProviderPackage() {
-        LocationProvider p;
-        synchronized (mLock) {
-            p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
-        }
-
-        if (p == null) {
-            return null;
-        }
-        if (p.mProvider instanceof LocationProviderProxy) {
-            return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
-        }
-        return null;
+        return runFromBinderBlocking(() -> {
+            LocationProvider p = mProvidersByName.get(provider);
+            if (p == null) return null;
+            return p.getProperties();
+        });
     }
 
     @Override
-    public void setLocationControllerExtraPackage(String packageName) {
-        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
-                Manifest.permission.LOCATION_HARDWARE + " permission required");
-        synchronized (mLock) {
-            mLocationControllerExtraPackage = packageName;
-        }
-    }
+    public String getNetworkProviderPackage() throws RemoteException {
+        return runFromBinderBlocking(() -> {
+            LocationProvider p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
 
-    @Override
-    public String getLocationControllerExtraPackage() {
-        synchronized (mLock) {
-            return mLocationControllerExtraPackage;
-        }
-    }
-
-    @Override
-    public void setLocationControllerExtraPackageEnabled(boolean enabled) {
-        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
-                Manifest.permission.LOCATION_HARDWARE + " permission required");
-        synchronized (mLock) {
-            mLocationControllerExtraPackageEnabled = enabled;
-        }
-    }
-
-    @Override
-    public boolean isLocationControllerExtraPackageEnabled() {
-        synchronized (mLock) {
-            return mLocationControllerExtraPackageEnabled
-                    && (mLocationControllerExtraPackage != null);
-        }
-    }
-
-    /**
-     * Returns the current location enabled/disabled status for a user
-     *
-     * @param userId the id of the user
-     * @return true if location is enabled
-     */
-    @Override
-    public boolean isLocationEnabledForUser(int userId) {
-        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                final String allowedProviders = Settings.Secure.getStringForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        userId);
-                if (allowedProviders == null) {
-                    return false;
-                }
-                final List<String> providerList = Arrays.asList(allowedProviders.split(","));
-                for (String provider : mRealProviders.keySet()) {
-                    if (provider.equals(LocationManager.PASSIVE_PROVIDER)
-                            || provider.equals(LocationManager.FUSED_PROVIDER)) {
-                        continue;
-                    }
-                    if (providerList.contains(provider)) {
-                        return true;
-                    }
-                }
-                return false;
+            if (p == null) {
+                return null;
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+            if (p.mProvider instanceof LocationProviderProxy) {
+                return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
+            }
+            return null;
+        });
     }
 
-    /**
-     * Enable or disable location for a user
-     *
-     * @param enabled true to enable location, false to disable location
-     * @param userId  the id of the user
-     */
     @Override
-    public void setLocationEnabledForUser(boolean enabled, int userId) {
+    public void setLocationControllerExtraPackage(String packageName) throws RemoteException {
+        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+                Manifest.permission.LOCATION_HARDWARE + " permission required");
+
+        runFromBinderBlocking(() -> mLocationControllerExtraPackage = packageName);
+    }
+
+    @Override
+    public String getLocationControllerExtraPackage() throws RemoteException {
+        return runFromBinderBlocking(() -> mLocationControllerExtraPackage);
+    }
+
+    @Override
+    public void setLocationControllerExtraPackageEnabled(boolean enabled) throws RemoteException {
+        mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+                Manifest.permission.LOCATION_HARDWARE + " permission required");
+        runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled = enabled);
+    }
+
+    @Override
+    public boolean isLocationControllerExtraPackageEnabled() throws RemoteException {
+        return runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled
+                && (mLocationControllerExtraPackage != null));
+    }
+
+    @Override
+    public boolean isLocationEnabledForUser(int userId) throws RemoteException {
+        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS,
+                    "Requires INTERACT_ACROSS_USERS permission");
+        }
+
+        return runFromBinderBlocking(() -> isLocationEnabledForUserInternal(userId));
+    }
+
+    private boolean isLocationEnabledForUserInternal(int userId) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+        final String allowedProviders = Settings.Secure.getStringForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                userId);
+        if (allowedProviders == null) {
+            return false;
+        }
+        final List<String> providerList = Arrays.asList(allowedProviders.split(","));
+
+        for (String provider : mRealProviders.keySet()) {
+            if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+                    || provider.equals(LocationManager.FUSED_PROVIDER)) {
+                continue;
+            }
+            if (providerList.contains(provider)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void setLocationEnabledForUser(boolean enabled, int userId) throws RemoteException {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.WRITE_SECURE_SETTINGS,
                 "Requires WRITE_SECURE_SETTINGS permission");
 
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                final Set<String> allRealProviders = mRealProviders.keySet();
-                // Update all providers on device plus gps and network provider when disabling
-                // location
-                Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
-                allProvidersSet.addAll(allRealProviders);
-                // When disabling location, disable gps and network provider that could have been
-                // enabled by location mode api.
-                if (!enabled) {
-                    allProvidersSet.add(LocationManager.GPS_PROVIDER);
-                    allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
-                }
-                if (allProvidersSet.isEmpty()) {
-                    return;
-                }
-                // to ensure thread safety, we write the provider name with a '+' or '-'
-                // and let the SettingsProvider handle it rather than reading and modifying
-                // the list of enabled providers.
-                final String prefix = enabled ? "+" : "-";
-                StringBuilder locationProvidersAllowed = new StringBuilder();
-                for (String provider : allProvidersSet) {
-                    if (provider.equals(LocationManager.PASSIVE_PROVIDER)
-                            || provider.equals(LocationManager.FUSED_PROVIDER)) {
-                        continue;
-                    }
-                    locationProvidersAllowed.append(prefix);
-                    locationProvidersAllowed.append(provider);
-                    locationProvidersAllowed.append(",");
-                }
-                // Remove the trailing comma
-                locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
-                Settings.Secure.putStringForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        locationProvidersAllowed.toString(),
-                        userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS,
+                    "Requires INTERACT_ACROSS_USERS permission");
         }
+
+        runFromBinderBlocking(() -> {
+            final Set<String> allRealProviders = mRealProviders.keySet();
+            // Update all providers on device plus gps and network provider when disabling
+            // location
+            Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
+            allProvidersSet.addAll(allRealProviders);
+            // When disabling location, disable gps and network provider that could have been
+            // enabled by location mode api.
+            if (!enabled) {
+                allProvidersSet.add(LocationManager.GPS_PROVIDER);
+                allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
+            }
+            if (allProvidersSet.isEmpty()) {
+                return;
+            }
+            // to ensure thread safety, we write the provider name with a '+' or '-'
+            // and let the SettingsProvider handle it rather than reading and modifying
+            // the list of enabled providers.
+            final String prefix = enabled ? "+" : "-";
+            StringBuilder locationProvidersAllowed = new StringBuilder();
+            for (String provider : allProvidersSet) {
+                if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+                        || provider.equals(LocationManager.FUSED_PROVIDER)) {
+                    continue;
+                }
+                locationProvidersAllowed.append(prefix);
+                locationProvidersAllowed.append(provider);
+                locationProvidersAllowed.append(",");
+            }
+            // Remove the trailing comma
+            locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
+            Settings.Secure.putStringForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                    locationProvidersAllowed.toString(),
+                    userId);
+        });
     }
 
-    /**
-     * Returns the current enabled/disabled status of a location provider and user
-     *
-     * @param providerName name of the provider
-     * @param userId       the id of the user
-     * @return true if the provider exists and is enabled
-     */
     @Override
-    public boolean isProviderEnabledForUser(String providerName, int userId) {
+    public boolean isProviderEnabledForUser(String providerName, int userId)
+            throws RemoteException {
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        if (!isLocationEnabledForUser(userId)) {
-            return false;
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS,
+                    "Requires INTERACT_ACROSS_USERS permission");
         }
 
         // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
         // so we discourage its use
         if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false;
 
-        long identity = Binder.clearCallingIdentity();
-        try {
-            LocationProvider provider;
-            synchronized (mLock) {
-                provider = mProvidersByName.get(providerName);
-            }
-            return provider != null && provider.isEnabled();
-        } finally {
-            Binder.restoreCallingIdentity(identity);
+        if (!isLocationEnabledForUser(userId)) {
+            return false;
         }
+
+        return runFromBinderBlocking(() -> {
+            LocationProvider provider = mProvidersByName.get(providerName);
+            return provider != null && provider.isEnabled();
+        });
     }
 
-    /**
-     * Enable or disable a single location provider.
-     *
-     * @param provider name of the provider
-     * @param enabled  true to enable the provider. False to disable the provider
-     * @param userId   the id of the user to set
-     * @return true if the value was set, false on errors
-     */
     @Override
     public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
         return false;
     }
 
-    /**
-     * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
-     * current user id
-     *
-     * @param userId the user id to get or set value
-     */
-    private void checkInteractAcrossUsersPermission(int userId) {
-        int uid = Binder.getCallingUid();
-        if (UserHandle.getUserId(uid) != userId) {
-            if (ActivityManager.checkComponentPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
-                    != PERMISSION_GRANTED) {
-                throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
-            }
-        }
-    }
-
-    /**
-     * Returns "true" if the UID belongs to a bound location provider.
-     *
-     * @param uid the uid
-     * @return true if uid belongs to a bound location provider
-     */
     private boolean isUidALocationProvider(int uid) {
         if (uid == Process.SYSTEM_UID) {
             return true;
         }
-        if (mGeocodeProvider != null) {
-            if (doesUidHavePackage(uid, mGeocodeProvider.getConnectedPackageName())) return true;
-        }
+
         for (LocationProviderProxy proxy : mProxyProviders) {
             if (doesUidHavePackage(uid, proxy.getConnectedPackageName())) return true;
         }
@@ -2970,7 +2819,6 @@
         // providers installed oustide the system image. So
         // also allow providers with a UID matching the
         // currently bound package name
-
         if (isUidALocationProvider(Binder.getCallingUid())) {
             return;
         }
@@ -2979,9 +2827,6 @@
                 "or UID of a currently bound location provider");
     }
 
-    /**
-     * Returns true if the given package belongs to the given uid.
-     */
     private boolean doesUidHavePackage(int uid, String packageName) {
         if (packageName == null) {
             return false;
@@ -2998,22 +2843,12 @@
         return false;
     }
 
+    // TODO: will be removed in future
     @Override
     public void reportLocation(Location location, boolean passive) {
-        checkCallerIsProvider();
-
-        if (!location.isComplete()) {
-            Log.w(TAG, "Dropping incomplete location: " + location);
-            return;
-        }
-
-        mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
-        Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
-        m.arg1 = (passive ? 1 : 0);
-        mLocationHandler.sendMessageAtFrontOfQueue(m);
+        throw new IllegalStateException("operation unsupported");
     }
 
-
     private static boolean shouldBroadcastSafe(
             Location loc, Location lastLoc, UpdateRecord record, long now) {
         // Always broadcast the first update
@@ -3046,14 +2881,33 @@
         return record.mRealRequest.getExpireAt() >= now;
     }
 
-    private void handleLocationChangedLocked(Location location, boolean passive) {
-        if (D) Log.d(TAG, "incoming location: " + location);
+    private void handleLocationChanged(Location location, boolean passive) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+        // create a working copy of the incoming Location so that the service can modify it without
+        // disturbing the caller's copy
+        Location myLocation = new Location(location);
+        String pr = myLocation.getProvider();
+
+        // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
+        // bit if location did not come from a mock provider because passive/fused providers can
+        // forward locations from mock providers, and should not grant them legitimacy in doing so.
+        if (!myLocation.isFromMockProvider() && isMockProvider(pr)) {
+            myLocation.setIsFromMockProvider(true);
+        }
+
+        if (!passive) {
+            // notify passive provider of the new location
+            mPassiveProvider.updateLocation(myLocation);
+        }
+
+        if (D) Log.d(TAG, "incoming location: " + myLocation);
         long now = SystemClock.elapsedRealtime();
-        String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
+        String provider = (passive ? LocationManager.PASSIVE_PROVIDER : myLocation.getProvider());
         // Skip if the provider is unknown.
         LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
-        updateLastLocationLocked(location, provider);
+        updateLastLocation(myLocation, provider);
         // mLastLocation should have been updated from the updateLastLocationLocked call above.
         Location lastLocation = mLastLocation.get(provider);
         if (lastLocation == null) {
@@ -3064,13 +2918,13 @@
         // Update last known coarse interval location if enough time has passed.
         Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
         if (lastLocationCoarseInterval == null) {
-            lastLocationCoarseInterval = new Location(location);
+            lastLocationCoarseInterval = new Location(myLocation);
             mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
         }
-        long timeDiffNanos = location.getElapsedRealtimeNanos()
+        long timeDiffNanos = myLocation.getElapsedRealtimeNanos()
                 - lastLocationCoarseInterval.getElapsedRealtimeNanos();
         if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
-            lastLocationCoarseInterval.set(location);
+            lastLocationCoarseInterval.set(myLocation);
         }
         // Don't ever return a coarse location that is more recent than the allowed update
         // interval (i.e. don't allow an app to keep registering and unregistering for
@@ -3194,24 +3048,20 @@
         // remove dead records and receivers outside the loop
         if (deadReceivers != null) {
             for (Receiver receiver : deadReceivers) {
-                removeUpdatesLocked(receiver);
+                removeUpdates(receiver);
             }
         }
         if (deadUpdateRecords != null) {
             for (UpdateRecord r : deadUpdateRecords) {
                 r.disposeLocked(true);
             }
-            applyRequirementsLocked(provider);
+            applyRequirements(provider);
         }
     }
 
-    /**
-     * Updates last location with the given location
-     *
-     * @param location new location to update
-     * @param provider Location provider to update for
-     */
-    private void updateLastLocationLocked(Location location, String provider) {
+    private void updateLastLocation(Location location, String provider) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
         Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
         Location lastNoGPSLocation;
         Location lastLocation = mLastLocation.get(provider);
@@ -3229,75 +3079,11 @@
         lastLocation.set(location);
     }
 
-    private class LocationWorkerHandler extends Handler {
-        public LocationWorkerHandler(Looper looper) {
-            super(looper, null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_LOCATION_CHANGED:
-                    handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
-                    break;
-            }
-        }
-    }
-
     private boolean isMockProvider(String provider) {
-        synchronized (mLock) {
-            return mMockProviders.containsKey(provider);
-        }
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+        return mMockProviders.containsKey(provider);
     }
 
-    private void handleLocationChanged(Location location, boolean passive) {
-        // create a working copy of the incoming Location so that the service can modify it without
-        // disturbing the caller's copy
-        Location myLocation = new Location(location);
-        String provider = myLocation.getProvider();
-
-        // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
-        // bit if location did not come from a mock provider because passive/fused providers can
-        // forward locations from mock providers, and should not grant them legitimacy in doing so.
-        if (!myLocation.isFromMockProvider() && isMockProvider(provider)) {
-            myLocation.setIsFromMockProvider(true);
-        }
-
-        synchronized (mLock) {
-            if (!passive) {
-                // notify passive provider of the new location
-                mPassiveProvider.updateLocation(myLocation);
-            }
-            handleLocationChangedLocked(myLocation, passive);
-        }
-    }
-
-    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
-        @Override
-        public void onPackageDisappeared(String packageName, int reason) {
-            // remove all receivers associated with this package name
-            synchronized (mLock) {
-                ArrayList<Receiver> deadReceivers = null;
-
-                for (Receiver receiver : mReceivers.values()) {
-                    if (receiver.mIdentity.mPackageName.equals(packageName)) {
-                        if (deadReceivers == null) {
-                            deadReceivers = new ArrayList<>();
-                        }
-                        deadReceivers.add(receiver);
-                    }
-                }
-
-                // perform removal outside of mReceivers loop
-                if (deadReceivers != null) {
-                    for (Receiver receiver : deadReceivers) {
-                        removeUpdatesLocked(receiver);
-                    }
-                }
-            }
-        }
-    };
-
     // Geocoder
 
     @Override
@@ -3338,7 +3124,8 @@
     }
 
     @Override
-    public void addTestProvider(String name, ProviderProperties properties, String opPackageName) {
+    public void addTestProvider(String name, ProviderProperties properties, String opPackageName)
+            throws RemoteException {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
@@ -3347,23 +3134,24 @@
             throw new IllegalArgumentException("Cannot mock the passive location provider");
         }
 
-        long identity = Binder.clearCallingIdentity();
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             // remove the real provider if we are replacing GPS or network provider
             if (LocationManager.GPS_PROVIDER.equals(name)
                     || LocationManager.NETWORK_PROVIDER.equals(name)
                     || LocationManager.FUSED_PROVIDER.equals(name)) {
                 LocationProvider p = mProvidersByName.get(name);
                 if (p != null) {
-                    removeProviderLocked(p);
+                    removeProvider(p);
                 }
             }
-            addTestProviderLocked(name, properties);
-        }
-        Binder.restoreCallingIdentity(identity);
+            addTestProvider(name, properties);
+            return null;
+        });
     }
 
-    private void addTestProviderLocked(String name, ProviderProperties properties) {
+    private void addTestProvider(String name, ProviderProperties properties) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
         if (mProvidersByName.get(name) != null) {
             throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
         }
@@ -3371,122 +3159,103 @@
         LocationProvider provider = new LocationProvider(name);
         MockProvider mockProvider = new MockProvider(provider, properties);
 
-        addProviderLocked(provider);
+        addProvider(provider);
         mMockProviders.put(name, mockProvider);
         mLastLocation.put(name, null);
         mLastLocationCoarseInterval.put(name, null);
     }
 
     @Override
-    public void removeTestProvider(String provider, String opPackageName) {
+    public void removeTestProvider(String provider, String opPackageName) throws RemoteException {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             MockProvider mockProvider = mMockProviders.remove(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
 
-            long identity = Binder.clearCallingIdentity();
-            try {
-                removeProviderLocked(mProvidersByName.get(provider));
+            removeProvider(mProvidersByName.get(provider));
 
-                // reinstate real provider if available
-                LocationProvider realProvider = mRealProviders.get(provider);
-                if (realProvider != null) {
-                    addProviderLocked(realProvider);
-                }
-                mLastLocation.put(provider, null);
-                mLastLocationCoarseInterval.put(provider, null);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+            // reinstate real provider if available
+            LocationProvider realProvider = mRealProviders.get(provider);
+            if (realProvider != null) {
+                addProvider(realProvider);
             }
-        }
+            mLastLocation.put(provider, null);
+            mLastLocationCoarseInterval.put(provider, null);
+        });
     }
 
     @Override
-    public void setTestProviderLocation(String provider, Location loc, String opPackageName) {
+    public void setTestProviderLocation(String provider, Location loc, String opPackageName)
+            throws RemoteException {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             MockProvider mockProvider = mMockProviders.get(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
 
-            // Ensure that the location is marked as being mock. There's some logic to do this in
-            // handleLocationChanged(), but it fails if loc has the wrong provider (bug 33091107).
+            // Ensure that the location is marked as being mock. There's some logic to do this
+            // in handleLocationChanged(), but it fails if loc has the wrong provider
+            // (b/33091107).
             Location mock = new Location(loc);
             mock.setIsFromMockProvider(true);
 
             if (!TextUtils.isEmpty(loc.getProvider()) && !provider.equals(loc.getProvider())) {
-                // The location has an explicit provider that is different from the mock provider
-                // name. The caller may be trying to fool us via bug 33091107.
+                // The location has an explicit provider that is different from the mock
+                // provider name. The caller may be trying to fool us via bug 33091107.
                 EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
                         provider + "!=" + loc.getProvider());
             }
 
-            // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mockProvider.setLocation(mock);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
+            mockProvider.setLocation(mock);
+        });
     }
 
     @Override
-    public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName) {
+    public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName)
+            throws RemoteException {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             MockProvider mockProvider = mMockProviders.get(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
-            long identity = Binder.clearCallingIdentity();
-            try {
-                mockProvider.setEnabled(enabled);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
+            mockProvider.setEnabled(enabled);
+        });
     }
 
     @Override
     public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime,
-            String opPackageName) {
+            String opPackageName) throws RemoteException {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        synchronized (mLock) {
+        runFromBinderBlocking(() -> {
             MockProvider mockProvider = mMockProviders.get(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
             mockProvider.setStatus(status, extras, updateTime);
-        }
-    }
-
-    private void log(String log) {
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Slog.d(TAG, log);
-        }
+        });
     }
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        synchronized (mLock) {
+        Runnable dump = () -> {
             if (args.length > 0 && args[0].equals("--gnssmetrics")) {
                 if (mGnssMetricsProvider != null) {
                     pw.append(mGnssMetricsProvider.getGnssMetricsAsProtoString());
@@ -3579,6 +3348,14 @@
             if (mGnssBatchingInProgress) {
                 pw.println("  GNSS batching in progress");
             }
+        };
+
+        FutureTask<Void> task = new FutureTask<>(dump, null);
+        mHandler.postAtFrontOfQueue(task);
+        try {
+            task.get();
+        } catch (InterruptedException | ExecutionException e) {
+            pw.println("error dumping: " + e);
         }
     }
 }
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 06dc918..f27d373 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -19,10 +19,7 @@
 import android.content.Context;
 import android.os.Environment;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -32,6 +29,7 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
@@ -50,8 +48,6 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Monitors the health of packages on the system and notifies interested observers when packages
@@ -70,7 +66,6 @@
     private static final String ATTR_VERSION = "version";
     private static final String ATTR_NAME = "name";
     private static final String ATTR_DURATION = "duration";
-    private static final int MESSAGE_SAVE_FILE = 1;
 
     private static PackageWatchdog sPackageWatchdog;
 
@@ -79,20 +74,21 @@
     private final Context mContext;
     // Handler to run package cleanup runnables
     private final Handler mTimerHandler;
-    private final HandlerThread mIoThread = new HandlerThread("package_watchdog_io",
-            Process.THREAD_PRIORITY_BACKGROUND);
     private final Handler mIoHandler;
-    // Maps observer names to package observers that have been registered since the last boot
+    // Contains (observer-name -> external-observer-handle) that have been registered during the
+    // current boot.
+    // It is populated when observers call #registerHealthObserver and it does not survive reboots.
     @GuardedBy("mLock")
-    final Map<String, PackageHealthObserver> mRegisteredObservers = new ArrayMap<>();
-    // Maps observer names to internal observers (registered or not) loaded from file
+    final ArrayMap<String, PackageHealthObserver> mRegisteredObservers = new ArrayMap<>();
+    // Contains (observer-name -> internal-observer-handle) that have ever been registered from
+    // previous boots. Observers with all packages expired are periodically pruned.
+    // It is saved to disk on system shutdown and repouplated on startup so it survives reboots.
     @GuardedBy("mLock")
-    final Map<String, ObserverInternal> mAllObservers = new ArrayMap<>();
-    // /data/system/ directory
-    private final File mSystemDir = new File(Environment.getDataDirectory(), "system");
-    // File containing the XML data of monitored packages
+    final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>();
+    // File containing the XML data of monitored packages /data/system/package-watchdog.xml
     private final AtomicFile mPolicyFile =
-            new AtomicFile(new File(mSystemDir, "package-watchdog.xml"));
+            new AtomicFile(new File(new File(Environment.getDataDirectory(), "system"),
+                           "package-watchdog.xml"));
     // Runnable to prune monitored packages that have expired
     private final Runnable mPackageCleanup;
     // Last SystemClock#uptimeMillis a package clean up was executed.
@@ -105,18 +101,19 @@
     private PackageWatchdog(Context context) {
         mContext = context;
         mTimerHandler = new Handler(Looper.myLooper());
-        mIoThread.start();
-        mIoHandler = new IoHandler(mIoThread.getLooper());
+        mIoHandler = BackgroundThread.getHandler();
         mPackageCleanup = this::rescheduleCleanup;
         loadFromFile();
     }
 
     /** Creates or gets singleton instance of PackageWatchdog. */
-    public static synchronized PackageWatchdog getInstance(Context context) {
-        if (sPackageWatchdog == null) {
-            sPackageWatchdog = new PackageWatchdog(context);
+    public static PackageWatchdog getInstance(Context context) {
+        synchronized (PackageWatchdog.class) {
+            if (sPackageWatchdog == null) {
+                sPackageWatchdog = new PackageWatchdog(context);
+            }
+            return sPackageWatchdog;
         }
-        return sPackageWatchdog;
     }
 
     /**
@@ -140,21 +137,20 @@
      * {@code observer} of any package failures within the monitoring duration.
      *
      * <p>If {@code observer} is already monitoring a package in {@code packageNames},
-     * the monitoring window of that package will be reset to {@code hours}.
+     * the monitoring window of that package will be reset to {@code durationMs}.
      *
      * @throws IllegalArgumentException if {@code packageNames} is empty
-     * or {@code hours} is less than 1
+     * or {@code durationMs} is less than 1
      */
     public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
-            int hours) {
-        if (packageNames.isEmpty() || hours < 1) {
+            int durationMs) {
+        if (packageNames.isEmpty() || durationMs < 1) {
             throw new IllegalArgumentException("Observation not started, no packages specified"
-                    + "or invalid hours");
+                    + "or invalid duration");
         }
-        long durationMs = TimeUnit.HOURS.toMillis(hours);
         List<MonitoredPackage> packages = new ArrayList<>();
-        for (String packageName : packageNames) {
-            packages.add(new MonitoredPackage(packageName, durationMs));
+        for (int i = 0; i < packageNames.size(); i++) {
+            packages.add(new MonitoredPackage(packageNames.get(i), durationMs));
         }
         synchronized (mLock) {
             ObserverInternal oldObserver = mAllObservers.get(observer.getName());
@@ -173,7 +169,7 @@
         // Always reschedule because we may need to expire packages
         // earlier than we are already scheduled for
         rescheduleCleanup();
-        sendIoMessage(MESSAGE_SAVE_FILE);
+        saveToFileAsync();
     }
 
     /**
@@ -186,7 +182,7 @@
             mAllObservers.remove(observer.getName());
             mRegisteredObservers.remove(observer.getName());
         }
-        sendIoMessage(MESSAGE_SAVE_FILE);
+        saveToFileAsync();
     }
 
     // TODO(zezeozue:) Accept current versionCodes of failing packages?
@@ -194,27 +190,43 @@
      * Called when a process fails either due to a crash or ANR.
      *
      * <p>All registered observers for the packages contained in the process will be notified in
-     * order of priority unitl an observer signifies that it has taken action and other observers
+     * order of priority until an observer signifies that it has taken action and other observers
      * should not notified.
      *
      * <p>This method could be called frequently if there is a severe problem on the device.
      */
     public void onPackageFailure(String[] packages) {
+        ArrayMap<String, List<PackageHealthObserver>> packagesToReport = new ArrayMap<>();
         synchronized (mLock) {
             if (mRegisteredObservers.isEmpty()) {
                 return;
             }
-            for (String packageName : packages) {
-                for (ObserverInternal observer : mAllObservers.values()) {
-                    if (observer.onPackageFailure(packageName)) {
-                        PackageHealthObserver activeObserver =
-                                mRegisteredObservers.get(observer.mName);
-                        if (activeObserver != null
-                                && activeObserver.onHealthCheckFailed(packageName)) {
-                            // Observer has handled, do not notify other observers
-                            break;
-                        }
+
+            for (int pIndex = 0; pIndex < packages.length; pIndex++) {
+                for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
+                    // Observers interested in receiving packageName failures
+                    List<PackageHealthObserver> observersToNotify = new ArrayList<>();
+                    PackageHealthObserver activeObserver =
+                            mRegisteredObservers.get(mAllObservers.valueAt(oIndex).mName);
+                    if (activeObserver != null) {
+                        observersToNotify.add(activeObserver);
                     }
+
+                    // Save interested observers and notify them outside the lock
+                    if (!observersToNotify.isEmpty()) {
+                        packagesToReport.put(packages[pIndex], observersToNotify);
+                    }
+                }
+            }
+        }
+
+        // Notify observers
+        for (int pIndex = 0; pIndex < packagesToReport.size(); pIndex++) {
+            List<PackageHealthObserver> observers = packagesToReport.valueAt(pIndex);
+            for (int oIndex = 0; oIndex < observers.size(); oIndex++) {
+                if (observers.get(oIndex).onHealthCheckFailed(packages[pIndex])) {
+                    // Observer has handled, do not notify others
+                    break;
                 }
             }
         }
@@ -225,7 +237,7 @@
     /** Writes the package information to file during shutdown. */
     public void writeNow() {
         if (!mAllObservers.isEmpty()) {
-            mIoHandler.removeMessages(MESSAGE_SAVE_FILE);
+            mIoHandler.removeCallbacks(this::saveToFile);
             pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastRescheduleMs);
             saveToFile();
             Slog.i(TAG, "Last write to update package durations");
@@ -235,7 +247,7 @@
     /** Register instances of this interface to receive notifications on package failure. */
     public interface PackageHealthObserver {
         /**
-         * Called when health check fails for the {@code packages}.
+         * Called when health check fails for the {@code packageName}.
          * @return {@code true} if action was taken and other observers should not be notified of
          * this failure, {@code false} otherwise.
          */
@@ -283,10 +295,12 @@
      */
     private long getEarliestPackageExpiryLocked() {
         long shortestDurationMs = Long.MAX_VALUE;
-        for (ObserverInternal observer : mAllObservers.values()) {
-            for (MonitoredPackage p : observer.mPackages.values()) {
-                if (p.mDurationMs < shortestDurationMs) {
-                    shortestDurationMs = p.mDurationMs;
+        for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
+            ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex).mPackages;
+            for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
+                long duration = packages.valueAt(pIndex).mDurationMs;
+                if (duration < shortestDurationMs) {
+                    shortestDurationMs = duration;
                 }
             }
         }
@@ -313,7 +327,7 @@
                 }
             }
         }
-        sendIoMessage(MESSAGE_SAVE_FILE);
+        saveToFileAsync();
     }
 
     /**
@@ -339,59 +353,53 @@
             }
         } catch (FileNotFoundException e) {
             // Nothing to monitor
-        } catch (IOException e) {
-            Log.wtf(TAG, "Unable to read monitored packages", e);
-        } catch (NumberFormatException e) {
-            Log.wtf(TAG, "Unable to parse monitored package windows", e);
-        } catch (XmlPullParserException e) {
-            Log.wtf(TAG, "Unable to parse monitored packages", e);
+        } catch (IOException | NumberFormatException | XmlPullParserException e) {
+            Log.wtf(TAG, "Unable to read monitored packages, deleting file", e);
+            mPolicyFile.delete();
         } finally {
             IoUtils.closeQuietly(infile);
         }
     }
 
     /**
-     * Persists mAllObservers to file and ignores threshold information.
-     *
-     * <p>Note that this is <b>not</b> thread safe and should only be called on the
-     * single threaded IoHandler.
+     * Persists mAllObservers to file. Threshold information is ignored.
      */
     private boolean saveToFile() {
-        FileOutputStream stream;
-        try {
-            stream = mPolicyFile.startWrite();
-        } catch (IOException e) {
-            Slog.w(TAG, "Cannot update monitored packages", e);
-            return false;
-        }
-
-        try {
-            XmlSerializer out = new FastXmlSerializer();
-            out.setOutput(stream, StandardCharsets.UTF_8.name());
-            out.startDocument(null, true);
-            out.startTag(null, TAG_PACKAGE_WATCHDOG);
-            out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
-            for (ObserverInternal observer : mAllObservers.values()) {
-                observer.write(out);
+        synchronized (mLock) {
+            FileOutputStream stream;
+            try {
+                stream = mPolicyFile.startWrite();
+            } catch (IOException e) {
+                Slog.w(TAG, "Cannot update monitored packages", e);
+                return false;
             }
-            out.endTag(null, TAG_PACKAGE_WATCHDOG);
-            out.endDocument();
-            mPolicyFile.finishWrite(stream);
-            return true;
-        } catch (IOException e) {
-            Slog.w(TAG, "Failed to save monitored packages, restoring backup", e);
-            mPolicyFile.failWrite(stream);
-            return false;
-        } finally {
-            IoUtils.closeQuietly(stream);
+
+            try {
+                XmlSerializer out = new FastXmlSerializer();
+                out.setOutput(stream, StandardCharsets.UTF_8.name());
+                out.startDocument(null, true);
+                out.startTag(null, TAG_PACKAGE_WATCHDOG);
+                out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
+                for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
+                    mAllObservers.valueAt(oIndex).write(out);
+                }
+                out.endTag(null, TAG_PACKAGE_WATCHDOG);
+                out.endDocument();
+                mPolicyFile.finishWrite(stream);
+                return true;
+            } catch (IOException e) {
+                Slog.w(TAG, "Failed to save monitored packages, restoring backup", e);
+                mPolicyFile.failWrite(stream);
+                return false;
+            } finally {
+                IoUtils.closeQuietly(stream);
+            }
         }
     }
 
-    private void sendIoMessage(int what) {
-        if (!mIoHandler.hasMessages(what)) {
-            Message m = Message.obtain(mIoHandler, what);
-            mIoHandler.sendMessage(m);
-        }
+    private void saveToFileAsync() {
+        mIoHandler.removeCallbacks(this::saveToFile);
+        mIoHandler.post(this::saveToFile);
     }
 
     /**
@@ -435,7 +443,8 @@
 
         public void updatePackages(List<MonitoredPackage> packages) {
             synchronized (mName) {
-                for (MonitoredPackage p : packages) {
+                for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
+                    MonitoredPackage p = packages.get(pIndex);
                     mPackages.put(p.mName, p);
                 }
             }
@@ -554,19 +563,4 @@
             return mFailures >= TRIGGER_FAILURE_COUNT;
         }
     }
-
-    private class IoHandler extends Handler {
-        IoHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_SAVE_FILE:
-                    saveToFile();
-                    break;
-            }
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index d07cf78..e10e0d6 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1313,16 +1313,17 @@
         }
     }
 
-    public void notifyDataConnection(int state, boolean isDataAllowed,
-            String reason, String apn, String apnType, LinkProperties linkProperties,
-            NetworkCapabilities networkCapabilities, int networkType, boolean roaming) {
+    public void notifyDataConnection(int state, boolean isDataAllowed, String apn, String apnType,
+                                     LinkProperties linkProperties,
+                                     NetworkCapabilities networkCapabilities, int networkType,
+                                     boolean roaming) {
         notifyDataConnectionForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, state,
-            isDataAllowed,reason, apn, apnType, linkProperties,
-            networkCapabilities, networkType, roaming);
+                isDataAllowed, apn, apnType, linkProperties,
+                networkCapabilities, networkType, roaming);
     }
 
-    public void notifyDataConnectionForSubscriber(int subId, int state,
-            boolean isDataAllowed, String reason, String apn, String apnType,
+    public void notifyDataConnectionForSubscriber(int subId, int state, boolean isDataAllowed,
+                                                  String apn, String apnType,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             int networkType, boolean roaming) {
         if (!checkNotifyPermission("notifyDataConnection()" )) {
@@ -1331,7 +1332,6 @@
         if (VDBG) {
             log("notifyDataConnectionForSubscriber: subId=" + subId
                 + " state=" + state + " isDataAllowed=" + isDataAllowed
-                + " reason='" + reason
                 + "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType
                 + " mRecords.size()=" + mRecords.size());
         }
@@ -1366,7 +1366,7 @@
                     mDataConnectionNetworkType[phoneId] = networkType;
                 }
                 mPreciseDataConnectionState = new PreciseDataConnectionState(state, networkType,
-                        apnType, apn, reason, linkProperties, "");
+                        apnType, apn, linkProperties, "");
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
                             PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1381,30 +1381,29 @@
             }
             handleRemoveListLocked();
         }
-        broadcastDataConnectionStateChanged(state, isDataAllowed, reason, apn,
-                apnType, linkProperties, networkCapabilities, roaming, subId);
-        broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn, reason,
+        broadcastDataConnectionStateChanged(state, isDataAllowed, apn, apnType, linkProperties,
+                networkCapabilities, roaming, subId);
+        broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn,
                 linkProperties, "");
     }
 
-    public void notifyDataConnectionFailed(String reason, String apnType) {
+    public void notifyDataConnectionFailed(String apnType) {
          notifyDataConnectionFailedForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                 reason, apnType);
+                 apnType);
     }
 
-    public void notifyDataConnectionFailedForSubscriber(int subId,
-            String reason, String apnType) {
+    public void notifyDataConnectionFailedForSubscriber(int subId, String apnType) {
         if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
             return;
         }
         if (VDBG) {
             log("notifyDataConnectionFailedForSubscriber: subId=" + subId
-                + " reason=" + reason + " apnType=" + apnType);
+                    + " apnType=" + apnType);
         }
         synchronized (mRecords) {
             mPreciseDataConnectionState = new PreciseDataConnectionState(
                     TelephonyManager.DATA_UNKNOWN,TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                    apnType, "", reason, null, "");
+                    apnType, "", null, "");
             for (Record r : mRecords) {
                 if (r.matchPhoneStateListenerEvent(
                         PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1417,9 +1416,9 @@
             }
             handleRemoveListLocked();
         }
-        broadcastDataConnectionFailed(reason, apnType, subId);
+        broadcastDataConnectionFailed(apnType, subId);
         broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", reason, null, "");
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", null, "");
     }
 
     public void notifyCellLocation(Bundle cellLocation) {
@@ -1529,15 +1528,14 @@
         }
     }
 
-    public void notifyPreciseDataConnectionFailed(String reason, String apnType,
-            String apn, String failCause) {
+    public void notifyPreciseDataConnectionFailed(String apnType, String apn, String failCause) {
         if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) {
             return;
         }
         synchronized (mRecords) {
             mPreciseDataConnectionState = new PreciseDataConnectionState(
                     TelephonyManager.DATA_UNKNOWN, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                    apnType, apn, reason, null, failCause);
+                    apnType, apn, null, failCause);
             for (Record r : mRecords) {
                 if (r.matchPhoneStateListenerEvent(
                         PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1551,7 +1549,7 @@
             handleRemoveListLocked();
         }
         broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, reason, null, failCause);
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, null, failCause);
     }
 
     @Override
@@ -1881,10 +1879,10 @@
                         android.Manifest.permission.READ_CALL_LOG});
     }
 
-    private void broadcastDataConnectionStateChanged(int state,
-            boolean isDataAllowed,
-            String reason, String apn, String apnType, LinkProperties linkProperties,
-            NetworkCapabilities networkCapabilities, boolean roaming, int subId) {
+    private void broadcastDataConnectionStateChanged(int state, boolean isDataAllowed, String apn,
+                                                     String apnType, LinkProperties linkProperties,
+                                                     NetworkCapabilities networkCapabilities,
+                                                     boolean roaming, int subId) {
         // Note: not reporting to the battery stats service here, because the
         // status bar takes care of that after taking into account all of the
         // required info.
@@ -1894,9 +1892,6 @@
         if (!isDataAllowed) {
             intent.putExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY, true);
         }
-        if (reason != null) {
-            intent.putExtra(PhoneConstants.STATE_CHANGE_REASON_KEY, reason);
-        }
         if (linkProperties != null) {
             intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
             String iface = linkProperties.getInterfaceName();
@@ -1915,17 +1910,15 @@
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
-    private void broadcastDataConnectionFailed(String reason, String apnType,
-            int subId) {
+    private void broadcastDataConnectionFailed(String apnType, int subId) {
         Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
-        intent.putExtra(PhoneConstants.FAILURE_REASON_KEY, reason);
         intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState,
-            int backgroundCallState) {
+                                                  int backgroundCallState) {
         Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED);
         intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState);
         intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState);
@@ -1935,16 +1928,16 @@
     }
 
     private void broadcastPreciseDataConnectionStateChanged(int state, int networkType,
-            String apnType, String apn, String reason, LinkProperties linkProperties,
-            String failCause) {
+                                                            String apnType, String apn,
+                                                            LinkProperties linkProperties,
+                                                            String failCause) {
         Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED);
         intent.putExtra(PhoneConstants.STATE_KEY, state);
         intent.putExtra(PhoneConstants.DATA_NETWORK_TYPE_KEY, networkType);
-        if (reason != null) intent.putExtra(PhoneConstants.STATE_CHANGE_REASON_KEY, reason);
         if (apnType != null) intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
         if (apn != null) intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
         if (linkProperties != null) {
-            intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY,linkProperties);
+            intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
         }
         if (failCause != null) intent.putExtra(PhoneConstants.DATA_FAILURE_CAUSE_KEY, failCause);
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a94fa12..ed39d83 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2503,6 +2503,9 @@
                     && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
                 hostingType = "webview_service";
             }
+            if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) {
+                hostingType = "app_zygote";
+            }
         }
 
         // Not running -- get it started, and enqueue this service record
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 16c236e..bc21610 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -235,6 +235,7 @@
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.Uri;
+import android.os.AppZygote;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.BinderProxy;
@@ -452,6 +453,9 @@
     // before we decide it must be hung.
     static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
 
+    // How long we wait to kill an application zygote, after the last process using
+    // it has gone away.
+    static final int KILL_APP_ZYGOTE_DELAY_MS = 5 * 1000;
     /**
      * How long we wait for an provider to be published. Should be longer than
      * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
@@ -1441,6 +1445,7 @@
     static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
     static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
     static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
+    static final int KILL_APP_ZYGOTE_MSG = 71;
 
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
 
@@ -1644,6 +1649,12 @@
                             false, userId, reason);
                 }
             } break;
+                case KILL_APP_ZYGOTE_MSG: {
+                    synchronized (ActivityManagerService.this) {
+                        final AppZygote appZygote = (AppZygote) msg.obj;
+                        mProcessList.killAppZygoteIfNeededLocked(appZygote);
+                    }
+                } break;
             case CHECK_EXCESSIVE_POWER_USE_MSG: {
                 synchronized (ActivityManagerService.this) {
                     checkExcessivePowerUsageLocked();
@@ -1932,7 +1943,8 @@
             synchronized (this) {
                 ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,
                         false,
-                        0);
+                        0,
+                        false);
                 app.setPersistent(true);
                 app.pid = MY_PID;
                 app.getWindowProcessController().setPid(MY_PID);
@@ -3744,10 +3756,10 @@
                         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
                         broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0,
                                 null, null, permission.ACCESS_INSTANT_APPS, null, false, false,
-                                resolvedUserId);
+                                resolvedUserId, false);
                     } else {
                         broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0,
-                                null, null, null, null, false, false, resolvedUserId);
+                                null, null, null, null, false, false, resolvedUserId, false);
                     }
 
                     if (observer != null) {
@@ -7396,7 +7408,7 @@
         }
 
         if (app == null) {
-            app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0);
+            app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0, false);
             mProcessList.updateLruProcessLocked(app, false, null);
             updateOomAdjLocked();
         }
@@ -13996,7 +14008,7 @@
                     BroadcastQueue queue = broadcastQueueForIntent(intent);
                     BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                             null, -1, -1, false, null, null, OP_NONE, null, receivers,
-                            null, 0, null, null, false, true, true, -1);
+                            null, 0, null, null, false, true, true, -1, false);
                     queue.enqueueParallelBroadcastLocked(r);
                     queue.scheduleBroadcastsLocked();
                 }
@@ -14235,6 +14247,18 @@
             IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
             boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
+        return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo,
+            resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered,
+            sticky, callingPid, callingUid, userId, false /* allowBackgroundActivityStarts */);
+    }
+
+    @GuardedBy("this")
+    final int broadcastIntentLocked(ProcessRecord callerApp,
+            String callerPackage, Intent intent, String resolvedType,
+            IIntentReceiver resultTo, int resultCode, String resultData,
+            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
+            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId,
+            boolean allowBackgroundActivityStarts) {
         intent = new Intent(intent);
 
         final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
@@ -14735,7 +14759,8 @@
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                     callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
-                    resultCode, resultData, resultExtras, ordered, sticky, false, userId);
+                    resultCode, resultData, resultExtras, ordered, sticky, false, userId,
+                    allowBackgroundActivityStarts);
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
             final boolean replaced = replacePending
                     && (queue.replaceParallelBroadcastLocked(r) != null);
@@ -14831,7 +14856,8 @@
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                     callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
-                    resultData, resultExtras, ordered, sticky, false, userId);
+                    resultData, resultExtras, ordered, sticky, false, userId,
+                    allowBackgroundActivityStarts);
 
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
                     + ": prev had " + queue.mOrderedBroadcasts.size());
@@ -14978,7 +15004,7 @@
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle resultExtras,
             String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
-            int userId) {
+            int userId, boolean allowBackgroundActivityStarts) {
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
 
@@ -14988,7 +15014,7 @@
             int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
                     resultTo, resultCode, resultData, resultExtras,
                     requiredPermissions, OP_NONE, bOptions, serialized,
-                    sticky, -1, uid, userId);
+                    sticky, -1, uid, userId, allowBackgroundActivityStarts);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
@@ -19044,6 +19070,19 @@
         }
 
         @Override
+        public void setPendingIntentAllowBgActivityStarts(IIntentSender target,
+                IBinder whitelistToken, int flags) {
+            if (!(target instanceof PendingIntentRecord)) {
+                Slog.w(TAG, "setPendingIntentAllowBgActivityStarts():"
+                        + " not a PendingIntentRecord: " + target);
+                return;
+            }
+            synchronized (ActivityManagerService.this) {
+                ((PendingIntentRecord) target).setAllowBgActivityStarts(whitelistToken, flags);
+            }
+        }
+
+        @Override
         public void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids) {
             synchronized (ActivityManagerService.this) {
                 mDeviceIdleWhitelist = allAppids;
@@ -19430,11 +19469,12 @@
         public int broadcastIntentInPackage(String packageName, int uid, Intent intent,
                 String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData,
                 Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized,
-                boolean sticky, int userId) {
+                boolean sticky, int userId, boolean allowBackgroundActivityStarts) {
             synchronized (ActivityManagerService.this) {
                 return ActivityManagerService.this.broadcastIntentInPackage(packageName, uid,
                         intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
-                        requiredPermission, bOptions, serialized, sticky, userId);
+                        requiredPermission, bOptions, serialized, sticky, userId,
+                        allowBackgroundActivityStarts);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index c290fbe..65aacdc 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -287,6 +287,9 @@
         r.curApp = app;
         app.curReceivers.add(r);
         app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
+        if (r.allowBackgroundActivityStarts) {
+            app.addAllowBackgroundActivityStartsToken(r);
+        }
         mService.mProcessList.updateLruProcessLocked(app, false, null);
         if (!skipOomAdj) {
             mService.updateOomAdjLocked();
@@ -415,6 +418,9 @@
         if (state == BroadcastRecord.IDLE) {
             Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
         }
+        if (r.allowBackgroundActivityStarts) {
+            r.curApp.removeAllowBackgroundActivityStartsToken(r);
+         }
         // If we're abandoning this broadcast before any receivers were actually spun up,
         // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
         if (r.nextReceiver > 0) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 9b7dc44..9e799f6 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -81,6 +81,10 @@
     int manifestSkipCount;  // number of manifest receivers skipped.
     BroadcastQueue queue;   // the outbound queue handling this broadcast
 
+    // if set to true, app's process will be temporarily whitelisted to start activities
+    // from background for the duration of the broadcast dispatch
+    final boolean allowBackgroundActivityStarts;
+
     static final int IDLE = 0;
     static final int APP_RECEIVE = 1;
     static final int CALL_IN_RECEIVE = 2;
@@ -223,7 +227,8 @@
             int _callingPid, int _callingUid, boolean _callerInstantApp, String _resolvedType,
             String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers,
             IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras,
-            boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId) {
+            boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId,
+            boolean _allowBackgroundActivityStarts) {
         if (_intent == null) {
             throw new NullPointerException("Can't construct with a null intent");
         }
@@ -252,6 +257,7 @@
         userId = _userId;
         nextReceiver = 0;
         state = IDLE;
+        allowBackgroundActivityStarts = _allowBackgroundActivityStarts;
     }
 
     /**
@@ -295,6 +301,7 @@
         manifestCount = from.manifestCount;
         manifestSkipCount = from.manifestSkipCount;
         queue = from.queue;
+        allowBackgroundActivityStarts = from.allowBackgroundActivityStarts;
     }
 
     public BroadcastRecord maybeStripForHistory() {
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 447243b..b675d9d 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -34,6 +34,7 @@
 import android.os.TransactionTooLargeException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.util.TimeUtils;
 
@@ -48,6 +49,9 @@
 public final class PendingIntentRecord extends IIntentSender.Stub {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentRecord" : TAG_AM;
 
+    public static final int FLAG_ACTIVITY_SENDER = 1 << 0;
+    public static final int FLAG_BROADCAST_SENDER = 1 << 1;
+
     final PendingIntentController controller;
     final Key key;
     final int uid;
@@ -56,6 +60,8 @@
     boolean canceled = false;
     private ArrayMap<IBinder, Long> whitelistDuration;
     private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
+    private ArraySet<IBinder> mAllowBgActivityStartsForActivitySender = new ArraySet<>();
+    private ArraySet<IBinder> mAllowBgActivityStartsForBroadcastSender = new ArraySet<>();
 
     String stringName;
     String lastTagPrefix;
@@ -214,6 +220,16 @@
         this.stringName = null;
     }
 
+    void setAllowBgActivityStarts(IBinder token, int flags) {
+        if (token == null) return;
+        if ((flags & FLAG_ACTIVITY_SENDER) != 0) {
+            mAllowBgActivityStartsForActivitySender.add(token);
+        }
+        if ((flags & FLAG_BROADCAST_SENDER) != 0) {
+            mAllowBgActivityStartsForBroadcastSender.add(token);
+        }
+    }
+
     public void registerCancelListenerLocked(IResultReceiver receiver) {
         if (mCancelCallbacks == null) {
             mCancelCallbacks = new RemoteCallbackList<>();
@@ -370,14 +386,16 @@
                             res = controller.mAtmInternal.startActivitiesInPackage(
                                     uid, key.packageName, allIntents, allResolvedTypes, resultTo,
                                     mergedOptions, userId, false /* validateIncomingUser */,
-                                    this /* originatingPendingIntent */);
+                                    this /* originatingPendingIntent */,
+                                    mAllowBgActivityStartsForActivitySender.contains(whitelistToken));
                         } else {
                             res = controller.mAtmInternal.startActivityInPackage(
                                     uid, callingPid, callingUid, key.packageName, finalIntent,
                                     resolvedType, resultTo, resultWho, requestCode, 0,
                                     mergedOptions, userId, null, "PendingIntentRecord",
                                     false /* validateIncomingUser */,
-                                    this /* originatingPendingIntent */);
+                                    this /* originatingPendingIntent */,
+                                    mAllowBgActivityStartsForActivitySender.contains(whitelistToken));
                         }
                     } catch (RuntimeException e) {
                         Slog.w(TAG, "Unable to send startActivity intent", e);
@@ -394,7 +412,8 @@
                         int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
                                 uid, finalIntent, resolvedType, finishedReceiver, code, null, null,
                                 requiredPermission, options, (finishedReceiver != null),
-                                false, userId);
+                                false, userId,
+                                mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken));
                         if (sent == ActivityManager.BROADCAST_SUCCESS) {
                             sendFinish = false;
                         }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index d133dea..9898d06 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -19,8 +19,6 @@
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
 import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
-import static android.os.Process.FIRST_ISOLATED_UID;
-import static android.os.Process.LAST_ISOLATED_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 import static android.os.Process.getFreeMemory;
@@ -34,6 +32,8 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_DELAY_MS;
+import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_MSG;
 import static com.android.server.am.ActivityManagerService.PERSISTENT_MASK;
 import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT;
 import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_MSG;
@@ -58,6 +58,7 @@
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.net.Uri;
+import android.os.AppZygote;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -75,10 +76,12 @@
 import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.StatsLog;
 import android.view.Display;
 
@@ -108,6 +111,7 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.List;
 
 /**
@@ -354,10 +358,136 @@
     final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
 
     /**
-     * Counter for assigning isolated process uids, to avoid frequently reusing the
-     * same ones.
+     * The currently running application zygotes.
      */
-    int mNextIsolatedProcessUid = 0;
+    final ProcessMap<AppZygote> mAppZygotes = new ProcessMap<AppZygote>();
+
+    /**
+     * The processes that are forked off an application zygote.
+     */
+    final ArrayMap<AppZygote, ArrayList<ProcessRecord>> mAppZygoteProcesses =
+            new ArrayMap<AppZygote, ArrayList<ProcessRecord>>();
+
+    final class IsolatedUidRange {
+        @VisibleForTesting
+        public final int mFirstUid;
+        @VisibleForTesting
+        public final int mLastUid;
+
+        @GuardedBy("ProcessList.this.mService")
+        private final SparseBooleanArray mUidUsed = new SparseBooleanArray();
+
+        @GuardedBy("ProcessList.this.mService")
+        private int mNextUid;
+
+        IsolatedUidRange(int firstUid, int lastUid) {
+            mFirstUid = firstUid;
+            mLastUid = lastUid;
+            mNextUid = firstUid;
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        int allocateIsolatedUidLocked(int userId) {
+            int uid;
+            int stepsLeft = (mLastUid - mFirstUid + 1);
+            for (int i = 0; i < stepsLeft; ++i) {
+                if (mNextUid < mFirstUid || mNextUid > mLastUid) {
+                    mNextUid = mFirstUid;
+                }
+                uid = UserHandle.getUid(userId, mNextUid);
+                mNextUid++;
+                if (!mUidUsed.get(uid, false)) {
+                    mUidUsed.put(uid, true);
+                    return uid;
+                }
+            }
+            return -1;
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        void freeIsolatedUidLocked(int uid) {
+            // Strip out userId
+            final int appId = UserHandle.getAppId(uid);
+            mUidUsed.delete(appId);
+        }
+    };
+
+    /**
+     * A class that allocates ranges of isolated UIDs per application, and keeps track of them.
+     */
+    final class IsolatedUidRangeAllocator {
+        private final int mFirstUid;
+        private final int mNumUidRanges;
+        private final int mNumUidsPerRange;
+        /**
+         * We map the uid range [mFirstUid, mFirstUid + mNumUidRanges * mNumUidsPerRange)
+         * back to an underlying bitset of [0, mNumUidRanges) and allocate out of that.
+         */
+        @GuardedBy("ProcessList.this.mService")
+        private final BitSet mAvailableUidRanges;
+        @GuardedBy("ProcessList.this.mService")
+        private final ProcessMap<IsolatedUidRange> mAppRanges = new ProcessMap<IsolatedUidRange>();
+
+        IsolatedUidRangeAllocator(int firstUid, int lastUid, int numUidsPerRange) {
+            mFirstUid = firstUid;
+            mNumUidsPerRange = numUidsPerRange;
+            mNumUidRanges = (lastUid - firstUid + 1) / numUidsPerRange;
+            mAvailableUidRanges = new BitSet(mNumUidRanges);
+            // Mark all as available
+            mAvailableUidRanges.set(0, mNumUidRanges);
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        IsolatedUidRange getIsolatedUidRangeLocked(ApplicationInfo info) {
+            return mAppRanges.get(info.processName, info.uid);
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        IsolatedUidRange getOrCreateIsolatedUidRangeLocked(ApplicationInfo info) {
+            IsolatedUidRange range = getIsolatedUidRangeLocked(info);
+            if (range == null) {
+                int uidRangeIndex = mAvailableUidRanges.nextSetBit(0);
+                if (uidRangeIndex < 0) {
+                    // No free range
+                    return null;
+                }
+                mAvailableUidRanges.clear(uidRangeIndex);
+                int actualUid = mFirstUid + uidRangeIndex * mNumUidsPerRange;
+                range = new IsolatedUidRange(actualUid, actualUid + mNumUidsPerRange - 1);
+                mAppRanges.put(info.processName, info.uid, range);
+            }
+            return range;
+        }
+
+        @GuardedBy("ProcessList.this.mService")
+        void freeUidRangeLocked(ApplicationInfo info) {
+            // Find the UID range
+            IsolatedUidRange range = mAppRanges.get(info.processName, info.uid);
+            if (range != null) {
+                // Map back to starting uid
+                final int uidRangeIndex = (range.mFirstUid - mFirstUid) / mNumUidsPerRange;
+                // Mark it as available in the underlying bitset
+                mAvailableUidRanges.set(uidRangeIndex);
+                // And the map
+                mAppRanges.remove(info.processName, info.uid);
+            }
+        }
+    }
+
+    /**
+     * The available isolated UIDs for processes that are not spawned from an application zygote.
+     */
+    @VisibleForTesting
+    IsolatedUidRange mGlobalIsolatedUids = new IsolatedUidRange(Process.FIRST_ISOLATED_UID,
+            Process.LAST_ISOLATED_UID);
+
+    /**
+     * An allocator for isolated UID ranges for apps that use an application zygote.
+     */
+    @VisibleForTesting
+    IsolatedUidRangeAllocator mAppIsolatedUidRangeAllocator =
+            new IsolatedUidRangeAllocator(Process.FIRST_APP_ZYGOTE_ISOLATED_UID,
+                    Process.LAST_APP_ZYGOTE_ISOLATED_UID, Process.NUM_UIDS_PER_APP_ZYGOTE);
 
     /**
      * Processes that are being forcibly torn down.
@@ -1505,6 +1635,67 @@
         }
     }
 
+    @GuardedBy("mService")
+    public void killAppZygoteIfNeededLocked(AppZygote appZygote) {
+        final ApplicationInfo appInfo = appZygote.getAppInfo();
+        ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
+        if (zygoteProcesses.size() == 0) { // Only remove if no longer in use now
+            mAppZygotes.remove(appInfo.processName, appInfo.uid);
+            mAppZygoteProcesses.remove(appZygote);
+            mAppIsolatedUidRangeAllocator.freeUidRangeLocked(appInfo);
+            appZygote.stopZygote();
+        }
+    }
+
+    @GuardedBy("mService")
+    private void removeProcessFromAppZygoteLocked(final ProcessRecord app) {
+        // Free the isolated uid for this process
+        final IsolatedUidRange appUidRange =
+                mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(app.info);
+        if (appUidRange != null) {
+            appUidRange.freeIsolatedUidLocked(app.uid);
+        }
+
+        final AppZygote appZygote = mAppZygotes.get(app.info.processName, app.info.uid);
+        if (appZygote != null) {
+            ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
+            zygoteProcesses.remove(app);
+            if (zygoteProcesses.size() == 0) {
+                Message msg = mService.mHandler.obtainMessage(KILL_APP_ZYGOTE_MSG);
+                msg.obj = appZygote;
+                mService.mHandler.sendMessageDelayed(msg, KILL_APP_ZYGOTE_DELAY_MS);
+            }
+        }
+    }
+
+    private AppZygote createAppZygoteForProcessIfNeeded(final ProcessRecord app) {
+        synchronized (mService) {
+            AppZygote appZygote = mAppZygotes.get(app.info.processName, app.info.uid);
+            final ArrayList<ProcessRecord> zygoteProcessList;
+            if (appZygote == null) {
+                final int userId = UserHandle.getUserId(app.info.uid);
+                final IsolatedUidRange uidRange =
+                        mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(app.info);
+                // Allocate an isolated UID out of this range for the Zygote itself
+                final int zygoteIsolatedUid = uidRange.allocateIsolatedUidLocked(userId);
+                appZygote = new AppZygote(app.info, zygoteIsolatedUid);
+                mAppZygotes.put(app.info.processName, app.info.uid, appZygote);
+                zygoteProcessList = new ArrayList<ProcessRecord>();
+                mAppZygoteProcesses.put(appZygote, zygoteProcessList);
+            } else {
+                mService.mHandler.removeMessages(KILL_APP_ZYGOTE_MSG, appZygote);
+                zygoteProcessList = mAppZygoteProcesses.get(appZygote);
+            }
+            // Note that we already add the app to mAppZygoteProcesses here;
+            // this is so that another thread can't come in and kill the zygote
+            // before we've even tried to start the process. If the process launch
+            // goes wrong, we'll clean this up in removeProcessNameLocked()
+            zygoteProcessList.add(app);
+
+            return appZygote;
+        }
+    }
+
     private Process.ProcessStartResult startProcess(String hostingType, String entryPoint,
             ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
             String seInfo, String requiredAbi, String instructionSet, String invokeWith,
@@ -1525,6 +1716,15 @@
                         app.info.dataDir, null, app.info.packageName,
                         packageNames, visibleVolIds,
                         new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+            } else if (hostingType.equals("app_zygote")) {
+                final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
+
+                startResult = appZygote.getProcess().start(entryPoint,
+                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
+                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
+                        app.info.dataDir, null, app.info.packageName,
+                        packageNames, visibleVolIds,
+                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
             } else {
                 startResult = Process.start(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
@@ -1614,13 +1814,6 @@
                 app.addPackage(info.packageName, info.versionCode, mService.mProcessStats);
                 checkSlow(startTime, "startProcess: done, added package to proc");
                 return app;
-            } else if (app.getActiveInstrumentation() != null) {
-                // We don't want to kill running instrumentation.
-                if (DEBUG_PROCESSES) {
-                    Slog.v(TAG_PROCESSES, "Instrumentation already running: " + app);
-                }
-                checkSlow(startTime, "startProcess: keep instrumentation proc");
-                return app;
             }
 
             // An application record is attached to a previous process,
@@ -1636,8 +1829,9 @@
                 ? hostingName.flattenToShortString() : null;
 
         if (app == null) {
+            final boolean fromAppZygote = "app_zygote".equals(hostingType);
             checkSlow(startTime, "startProcess: creating new process record");
-            app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
+            app = newProcessRecordLocked(info, processName, isolated, isolatedUid, fromAppZygote);
             if (app == null) {
                 Slog.w(TAG, "Failed making new process record for "
                         + processName + "/" + info.uid + " isolated=" + isolated);
@@ -2010,29 +2204,31 @@
     }
 
     @GuardedBy("mService")
+    private IsolatedUidRange getOrCreateIsolatedUidRangeLocked(ApplicationInfo info,
+            boolean fromAppZygote) {
+        if (!fromAppZygote) {
+            // Allocate an isolated UID from the global range
+            return mGlobalIsolatedUids;
+        } else {
+            return mAppIsolatedUidRangeAllocator.getOrCreateIsolatedUidRangeLocked(info);
+        }
+    }
+
+    @GuardedBy("mService")
     final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
-            boolean isolated, int isolatedUid) {
+            boolean isolated, int isolatedUid, boolean fromAppZygote) {
         String proc = customProcess != null ? customProcess : info.processName;
         final int userId = UserHandle.getUserId(info.uid);
         int uid = info.uid;
         if (isolated) {
             if (isolatedUid == 0) {
-                int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1;
-                while (true) {
-                    if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID
-                            || mNextIsolatedProcessUid > LAST_ISOLATED_UID) {
-                        mNextIsolatedProcessUid = FIRST_ISOLATED_UID;
-                    }
-                    uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
-                    mNextIsolatedProcessUid++;
-                    if (mIsolatedProcesses.indexOfKey(uid) < 0) {
-                        // No process for this uid, use it.
-                        break;
-                    }
-                    stepsLeft--;
-                    if (stepsLeft <= 0) {
-                        return null;
-                    }
+                IsolatedUidRange uidRange = getOrCreateIsolatedUidRangeLocked(info, fromAppZygote);
+                if (uidRange == null) {
+                    return null;
+                }
+                uid = uidRange.allocateIsolatedUidLocked(userId);
+                if (uid == -1) {
+                    return null;
                 }
             } else {
                 // Special case for startIsolatedProcess (internal only), where
@@ -2100,6 +2296,13 @@
             old.uidRecord = null;
         }
         mIsolatedProcesses.remove(uid);
+        mGlobalIsolatedUids.freeIsolatedUidLocked(uid);
+        // Remove the (expected) ProcessRecord from the app zygote
+        final ProcessRecord record = expecting != null ? expecting : old;
+        if (record != null && record.appZygote) {
+            removeProcessFromAppZygoteLocked(record);
+        }
+
         return old;
     }
 
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c15b7c7..0d0824a 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -76,6 +76,7 @@
     private final ActivityManagerService mService; // where we came from
     final ApplicationInfo info; // all about the first app in the process
     final boolean isolated;     // true if this is a special isolated process
+    final boolean appZygote;    // true if this is forked from the app zygote
     final int uid;              // uid of process; may be different from 'info' if isolated
     final int userId;           // user of process.
     final String processName;   // name of the process
@@ -249,6 +250,9 @@
     final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
     // All ContentProviderRecord process is using
     final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
+    // A set of tokens that currently contribute to this process being temporarily whitelisted
+    // to start activities even if it's not in the foreground
+    final ArraySet<Binder> mAllowBackgroundActivityStartsTokens = new ArraySet<>();
 
     String isolatedEntryPoint;  // Class to run on start if this is a special isolated process.
     String[] isolatedEntryPointArgs; // Arguments to pass to isolatedEntryPoint's main().
@@ -556,6 +560,8 @@
         mService = _service;
         info = _info;
         isolated = _info.uid != _uid;
+        appZygote = (UserHandle.getAppId(_uid) >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
+                && UserHandle.getAppId(_uid) <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
         uid = _uid;
         userId = UserHandle.getUserId(_uid);
         processName = _processName;
@@ -1135,6 +1141,17 @@
         return mUsingWrapper;
     }
 
+    void addAllowBackgroundActivityStartsToken(Binder entity) {
+        mAllowBackgroundActivityStartsTokens.add(entity);
+        mWindowProcessController.setAllowBackgroundActivityStarts(true);
+    }
+
+    void removeAllowBackgroundActivityStartsToken(Binder entity) {
+        mAllowBackgroundActivityStartsTokens.remove(entity);
+        mWindowProcessController.setAllowBackgroundActivityStarts(
+                !mAllowBackgroundActivityStartsTokens.isEmpty());
+    }
+
     void setActiveInstrumentation(ActiveInstrumentation instr) {
         mInstr = instr;
         mWindowProcessController.setInstrumenting(instr != null);
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 9684f4c..2a00025 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -126,21 +126,21 @@
     private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES =
             (1 << DATA_STALL_EVALUATION_TYPE_DNS);
 
-    static enum EvaluationResult {
+    enum EvaluationResult {
         VALIDATED(true),
         CAPTIVE_PORTAL(false);
-        final boolean isValidated;
+        final boolean mIsValidated;
         EvaluationResult(boolean isValidated) {
-            this.isValidated = isValidated;
+            this.mIsValidated = isValidated;
         }
     }
 
-    static enum ValidationStage {
+    enum ValidationStage {
         FIRST_VALIDATION(true),
         REVALIDATION(false);
-        final boolean isFirstValidation;
+        final boolean mIsFirstValidation;
         ValidationStage(boolean isFirstValidation) {
-            this.isFirstValidation = isFirstValidation;
+            this.mIsFirstValidation = isFirstValidation;
         }
     }
 
@@ -251,7 +251,7 @@
 
     // 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;
+    private static final int MAX_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
     // Before network has been evaluated this many times, ignore repeated reevaluate requests.
     private static final int IGNORE_REEVALUATE_ATTEMPTS = 5;
     private int mReevaluateToken = 0;
@@ -261,7 +261,7 @@
     // Stop blaming UID that requested re-evaluation after this many attempts.
     private static final int BLAME_FOR_EVALUATION_ATTEMPTS = 5;
     // Delay between reevaluations once a captive portal has been found.
-    private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10*60*1000;
+    private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
 
     private static final int NUM_VALIDATION_LOG_LINES = 20;
 
@@ -393,10 +393,17 @@
         start();
     }
 
+    /**
+     * Request the NetworkMonitor to reevaluate the network.
+     */
     public void forceReevaluation(int responsibleUid) {
         sendMessage(CMD_FORCE_REEVALUATION, responsibleUid, 0);
     }
 
+    /**
+     * Send a notification to NetworkMonitor indicating that private DNS settings have changed.
+     * @param newCfg The new private DNS configuration.
+     */
     public void notifyPrivateDnsSettingsChanged(PrivateDnsConfig newCfg) {
         // Cancel any outstanding resolutions.
         removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
@@ -655,8 +662,9 @@
         public boolean processMessage(Message message) {
             switch (message.what) {
                 case CMD_REEVALUATE:
-                    if (message.arg1 != mReevaluateToken || mUserDoesNotWant)
+                    if (message.arg1 != mReevaluateToken || mUserDoesNotWant) {
                         return HANDLED;
+                    }
                     // Don't bother validating networks that don't satisfy the default request.
                     // This includes:
                     //  - VPNs which can be considered explicitly desired by the user and the
@@ -813,9 +821,9 @@
         }
 
         private boolean isStrictModeHostnameResolved() {
-            return (mPrivateDnsConfig != null) &&
-                   mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname) &&
-                   (mPrivateDnsConfig.ips.length > 0);
+            return (mPrivateDnsConfig != null)
+                    && mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname)
+                    && (mPrivateDnsConfig.ips.length > 0);
         }
 
         private void resolveStrictModeHostname() {
@@ -852,9 +860,9 @@
 
         private boolean sendPrivateDnsProbe() {
             // q.v. system/netd/server/dns/DnsTlsTransport.cpp
-            final String ONE_TIME_HOSTNAME_SUFFIX = "-dnsotls-ds.metric.gstatic.com";
-            final String host = UUID.randomUUID().toString().substring(0, 8) +
-                    ONE_TIME_HOSTNAME_SUFFIX;
+            final String oneTimeHostnameSuffix = "-dnsotls-ds.metric.gstatic.com";
+            final String host = UUID.randomUUID().toString().substring(0, 8)
+                    + oneTimeHostnameSuffix;
             final Stopwatch watch = new Stopwatch().start();
             try {
                 final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host);
@@ -966,7 +974,7 @@
     // most one per address family. This ensures we only wait up to 20 seconds for TCP connections
     // to complete, regardless of how many IP addresses a host has.
     private static class OneAddressPerFamilyNetwork extends Network {
-        public OneAddressPerFamilyNetwork(Network network) {
+        OneAddressPerFamilyNetwork(Network network) {
             // Always bypass Private DNS.
             super(network.getPrivateDnsBypassingCopy());
         }
@@ -1000,7 +1008,8 @@
     }
 
     public boolean getWifiScansAlwaysAvailableDisabled() {
-        return mDependencies.getSetting(mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
+        return mDependencies.getSetting(
+                mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
     }
 
     private String getCaptivePortalServerHttpsUrl() {
@@ -1246,10 +1255,10 @@
             // Time how long it takes to get a response to our request
             long responseTimestamp = SystemClock.elapsedRealtime();
 
-            validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms" +
-                    " ret=" + httpResponseCode +
-                    " request=" + requestHeader +
-                    " headers=" + urlConnection.getHeaderFields());
+            validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms"
+                    + " ret=" + httpResponseCode
+                    + " request=" + requestHeader
+                    + " headers=" + urlConnection.getHeaderFields());
             // NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive
             // portal.  The only example of this seen so far was a captive portal.  For
             // the time being go with prior behavior of assuming it's not a captive
@@ -1267,7 +1276,7 @@
                     // sign-in to an empty page. Probably the result of a broken transparent proxy.
                     // See http://b/9972012.
                     validationLog(probeType, url,
-                        "200 response with Content-length=0 interpreted as 204 response.");
+                            "200 response with Content-length=0 interpreted as 204 response.");
                     httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE;
                 } else if (urlConnection.getContentLengthLong() == -1) {
                     // When no Content-length (default value == -1), attempt to read a byte from the
@@ -1309,7 +1318,7 @@
             private final boolean mIsHttps;
             private volatile CaptivePortalProbeResult mResult = CaptivePortalProbeResult.FAILED;
 
-            public ProbeThread(boolean isHttps) {
+            ProbeThread(boolean isHttps) {
                 mIsHttps = isHttps;
             }
 
@@ -1443,8 +1452,10 @@
                     if (cellInfo.isRegistered()) {
                         numRegisteredCellInfo++;
                         if (numRegisteredCellInfo > 1) {
-                            if (VDBG) logw("more than one registered CellInfo." +
-                                    " Can't tell which is active.  Bailing.");
+                            if (VDBG) {
+                                logw("more than one registered CellInfo."
+                                        + " Can't tell which is active.  Bailing.");
+                            }
                             return;
                         }
                         if (cellInfo instanceof CellInfoCdma) {
@@ -1492,14 +1503,14 @@
     }
 
     private int networkEventType(ValidationStage s, EvaluationResult r) {
-        if (s.isFirstValidation) {
-            if (r.isValidated) {
+        if (s.mIsFirstValidation) {
+            if (r.mIsValidated) {
                 return NetworkEvent.NETWORK_FIRST_VALIDATION_SUCCESS;
             } else {
                 return NetworkEvent.NETWORK_FIRST_VALIDATION_PORTAL_FOUND;
             }
         } else {
-            if (r.isValidated) {
+            if (r.mIsValidated) {
                 return NetworkEvent.NETWORK_REVALIDATION_SUCCESS;
             } else {
                 return NetworkEvent.NETWORK_REVALIDATION_PORTAL_FOUND;
@@ -1517,7 +1528,7 @@
 
     private void logValidationProbe(long durationMs, int probeType, int probeResult) {
         int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
-        boolean isFirstValidation = validationStage().isFirstValidation;
+        boolean isFirstValidation = validationStage().mIsFirstValidation;
         ValidationProbeEvent ev = new ValidationProbeEvent();
         ev.probeType = ValidationProbeEvent.makeProbeType(probeType, isFirstValidation);
         ev.returnCode = probeResult;
@@ -1535,10 +1546,20 @@
             return new Random();
         }
 
+        /**
+         * Get the value of a global integer setting.
+         * @param symbol Name of the setting
+         * @param defaultValue Value to return if the setting is not defined.
+         */
         public int getSetting(Context context, String symbol, int defaultValue) {
             return Settings.Global.getInt(context.getContentResolver(), symbol, defaultValue);
         }
 
+        /**
+         * Get the value of a global String setting.
+         * @param symbol Name of the setting
+         * @param defaultValue Value to return if the setting is not defined.
+         */
         public String getSetting(Context context, String symbol, String defaultValue) {
             final String value = Settings.Global.getString(context.getContentResolver(), symbol);
             return value != null ? value : defaultValue;
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 d56b167..7daf71d 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -34,32 +34,53 @@
  * @hide
  */
 public class TetheringDependencies {
+    /**
+     * Get a reference to the offload hardware interface to be used by tethering.
+     */
     public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
         return new OffloadHardwareInterface(h, log);
     }
 
+    /**
+     * Get a reference to the UpstreamNetworkMonitor to be used by tethering.
+     */
     public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
             SharedLog log, int what) {
         return new UpstreamNetworkMonitor(ctx, target, log, what);
     }
 
+    /**
+     * Get a reference to the IPv6TetheringCoordinator to be used by tethering.
+     */
     public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
             ArrayList<IpServer> notifyList, SharedLog log) {
         return new IPv6TetheringCoordinator(notifyList, log);
     }
 
+    /**
+     * Get dependencies to be used by IpServer.
+     */
     public IpServer.Dependencies getIpServerDependencies() {
         return new IpServer.Dependencies();
     }
 
+    /**
+     * Indicates whether tethering is supported on the device.
+     */
     public boolean isTetheringSupported() {
         return true;
     }
 
+    /**
+     * Get the NetworkRequest that should be fulfilled by the default network.
+     */
     public NetworkRequest getDefaultNetworkRequest() {
         return null;
     }
 
+    /**
+     * Get a reference to the EntitlementManager to be used by tethering.
+     */
     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/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 52eccca..78b3c15 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -17,13 +17,6 @@
 package com.android.server.display;
 
 import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.ActivityManager.StackInfo;
-import android.app.ActivityTaskManager;
-import android.app.IActivityTaskManager;
-import android.app.TaskStackListener;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManagerInternal;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -34,8 +27,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.EventLog;
@@ -43,15 +34,14 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 
-import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
-import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
 
 class AutomaticBrightnessController {
     private static final String TAG = "AutomaticBrightnessController";
 
+    private static final boolean DEBUG = false;
     private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
 
     // If true, enables the use of the screen auto-brightness adjustment setting.
@@ -76,8 +66,6 @@
     private static final int MSG_UPDATE_AMBIENT_LUX = 1;
     private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
     private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
-    private static final int MSG_UPDATE_FOREGROUND_APP = 4;
-    private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
 
     // Length of the ambient light horizon used to calculate the long term estimate of ambient
     // light.
@@ -138,8 +126,6 @@
     private final HysteresisLevels mAmbientBrightnessThresholds;
     private final HysteresisLevels mScreenBrightnessThresholds;
 
-    private boolean mLoggingEnabled;
-
     // Amount of time to delay auto-brightness after screen on while waiting for
     // the light sensor to warm-up in milliseconds.
     // May be 0 if no warm-up is required.
@@ -206,19 +192,6 @@
     private float mShortTermModelAnchor;
     private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
 
-    // Context-sensitive brightness configurations require keeping track of the foreground app's
-    // package name and category, which is done by registering a TaskStackListener to call back to
-    // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
-    // package namd and PackageManager to get its category (so might as well cache them).
-    private int mUserId;
-    private String mForegroundAppPackageName;
-    private String mPendingForegroundAppPackageName;
-    private @ApplicationInfo.Category int mForegroundAppCategory;
-    private @ApplicationInfo.Category int mPendingForegroundAppCategory;
-    private TaskStackListenerImpl mTaskStackListener;
-    private IActivityTaskManager mActivityTaskManager;
-    private PackageManagerInternal mPackageManagerInternal;
-
     public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
             SensorManager sensorManager, BrightnessMappingStrategy mapper,
             int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
@@ -253,42 +226,6 @@
         if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
             mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
         }
-
-        mUserId = ActivityManager.getCurrentUser();
-        mActivityTaskManager = ActivityTaskManager.getService();
-        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
-        mTaskStackListener = new TaskStackListenerImpl();
-        mForegroundAppPackageName = null;
-        mPendingForegroundAppPackageName = null;
-        mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
-        mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
-    }
-
-    /**
-     * Enable/disable logging.
-     *
-     * @param loggingEnabled
-     *      Whether logging should be on/off.
-     *
-     * @return Whether the method succeeded or not.
-     */
-    public boolean setLoggingEnabled(boolean loggingEnabled) {
-        if (mLoggingEnabled == loggingEnabled) {
-            return false;
-        }
-        mBrightnessMapper.setLoggingEnabled(loggingEnabled);
-        mLoggingEnabled = loggingEnabled;
-        return true;
-    }
-
-    /**
-     * Update the current user's ID.
-     *
-     * @param userId
-     *      The current user's ID.
-     */
-    public void onSwitchUser(int userId) {
-        mUserId = userId;
     }
 
     public int getAutomaticScreenBrightness() {
@@ -353,7 +290,7 @@
         }
         final int oldPolicy = mDisplayPolicy;
         mDisplayPolicy = policy;
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
         }
         if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
@@ -380,7 +317,7 @@
         mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
         mShortTermModelValid = true;
         mShortTermModelAnchor = mAmbientLux;
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
         }
         return true;
@@ -393,7 +330,7 @@
     }
 
     private void invalidateShortTermModel() {
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             Slog.d(TAG, "ShortTermModel: invalidate user data");
         }
         mShortTermModelValid = false;
@@ -446,11 +383,7 @@
         pw.println("  mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
         pw.println("  mBrightnessAdjustmentSampleOldBrightness="
                 + mBrightnessAdjustmentSampleOldBrightness);
-        pw.println("  mUserId=" + mUserId);
-        pw.println("  mForegroundAppPackageName=" + mForegroundAppPackageName);
-        pw.println("  mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
-        pw.println("  mForegroundAppCategory=" + mForegroundAppCategory);
-        pw.println("  mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
+        pw.println("  mShortTermModelValid=" + mShortTermModelValid);
 
         pw.println();
         mBrightnessMapper.dump(pw);
@@ -466,7 +399,6 @@
                 mLightSensorEnabled = true;
                 mLightSensorEnableTime = SystemClock.uptimeMillis();
                 mCurrentLightSensorRate = mInitialLightSensorRate;
-                registerForegroundAppUpdater();
                 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
                         mCurrentLightSensorRate * 1000, mHandler);
                 return true;
@@ -479,7 +411,6 @@
             mAmbientLightRingBuffer.clear();
             mCurrentLightSensorRate = -1;
             mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
-            unregisterForegroundAppUpdater();
             mSensorManager.unregisterListener(mLightSensorListener);
         }
         return false;
@@ -510,7 +441,7 @@
     private void adjustLightSensorRate(int lightSensorRate) {
         // if the light sensor rate changed, update the sensor listener
         if (lightSensorRate != mCurrentLightSensorRate) {
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 Slog.d(TAG, "adjustLightSensorRate: " +
                         "previousRate=" + mCurrentLightSensorRate + ", " +
                         "currentRate=" + lightSensorRate);
@@ -527,7 +458,7 @@
     }
 
     private void setAmbientLux(float lux) {
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             Slog.d(TAG, "setAmbientLux(" + lux + ")");
         }
         if (lux < 0) {
@@ -545,7 +476,7 @@
             final float maxAmbientLux =
                 mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
             if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) {
-                if (mLoggingEnabled) {
+                if (DEBUG) {
                     Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " +
                             minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux);
                 }
@@ -559,7 +490,7 @@
     }
 
     private float calculateAmbientLux(long now, long horizon) {
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
         }
         final int N = mAmbientLightRingBuffer.size();
@@ -578,7 +509,7 @@
                 break;
             }
         }
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
                     mAmbientLightRingBuffer.getTime(endIndex) + ", " +
                     mAmbientLightRingBuffer.getLux(endIndex) + ")");
@@ -596,7 +527,7 @@
             final long startTime = eventTime - now;
             float weight = calculateWeight(startTime, endTime);
             float lux = mAmbientLightRingBuffer.getLux(i);
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
                         "lux=" + lux + ", " +
                         "weight=" + weight);
@@ -605,7 +536,7 @@
             sum += lux * weight;
             endTime = startTime;
         }
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             Slog.d(TAG, "calculateAmbientLux: " +
                     "totalWeight=" + totalWeight + ", " +
                     "newAmbientLux=" + (sum / totalWeight));
@@ -660,7 +591,7 @@
             final long timeWhenSensorWarmedUp =
                 mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
             if (time < timeWhenSensorWarmedUp) {
-                if (mLoggingEnabled) {
+                if (DEBUG) {
                     Slog.d(TAG, "updateAmbientLux: Sensor not  ready yet: " +
                             "time=" + time + ", " +
                             "timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
@@ -671,7 +602,7 @@
             }
             setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
             mAmbientLuxValid = true;
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 Slog.d(TAG, "updateAmbientLux: Initializing: " +
                         "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
                         "mAmbientLux=" + mAmbientLux);
@@ -699,10 +630,10 @@
                         && fastAmbientLux <= mAmbientDarkeningThreshold
                         && nextDarkenTransition <= time)) {
             setAmbientLux(fastAmbientLux);
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 Slog.d(TAG, "updateAmbientLux: "
                         + ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
-                        + "mBrighteningLuxThreshold=" + mAmbientBrighteningThreshold + ", "
+                        + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
                         + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
                         + "mAmbientLux=" + mAmbientLux);
             }
@@ -719,7 +650,7 @@
         // weighted ambient lux or not.
         nextTransitionTime =
                 nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
                     nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
         }
@@ -731,8 +662,7 @@
             return;
         }
 
-        float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
-                mForegroundAppCategory);
+        float value = mBrightnessMapper.getBrightness(mAmbientLux);
 
         int newScreenAutoBrightness =
                 clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
@@ -743,7 +673,7 @@
         if (mScreenAutoBrightness != -1
                 && newScreenAutoBrightness > mScreenDarkeningThreshold
                 && newScreenAutoBrightness < mScreenBrighteningThreshold) {
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 Slog.d(TAG, "ignoring newScreenAutoBrightness: " + mScreenDarkeningThreshold
                         + " < " + newScreenAutoBrightness + " < " + mScreenBrighteningThreshold);
             }
@@ -751,7 +681,8 @@
         }
 
         if (mScreenAutoBrightness != newScreenAutoBrightness) {
-            if (mLoggingEnabled) {
+
+            if (DEBUG) {
                 Slog.d(TAG, "updateAutoBrightness: " +
                         "mScreenAutoBrightness=" + mScreenAutoBrightness + ", " +
                         "newScreenAutoBrightness=" + newScreenAutoBrightness);
@@ -787,11 +718,18 @@
                 BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS);
     }
 
+    private void cancelBrightnessAdjustmentSample() {
+        if (mBrightnessAdjustmentSamplePending) {
+            mBrightnessAdjustmentSamplePending = false;
+            mHandler.removeMessages(MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE);
+        }
+    }
+
     private void collectBrightnessAdjustmentSample() {
         if (mBrightnessAdjustmentSamplePending) {
             mBrightnessAdjustmentSamplePending = false;
             if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
-                if (mLoggingEnabled) {
+                if (DEBUG) {
                     Slog.d(TAG, "Auto-brightness adjustment changed by user: " +
                             "lux=" + mAmbientLux + ", " +
                             "brightness=" + mScreenAutoBrightness + ", " +
@@ -807,68 +745,6 @@
         }
     }
 
-    // Register a TaskStackListener to call back to us onTaskStackChanged, so we can update the
-    // foreground app's package name and category and correct the brightness accordingly.
-    private void registerForegroundAppUpdater() {
-        try {
-            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
-            // This will not get called until the foreground app changes for the first time, so
-            // call it explicitly to get the current foreground app's info.
-            updateForegroundApp();
-        } catch (RemoteException e) {
-            // Nothing to do.
-        }
-    }
-
-    private void unregisterForegroundAppUpdater() {
-        try {
-            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
-        } catch (RemoteException e) {
-            // Nothing to do.
-        }
-        mForegroundAppPackageName = null;
-        mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
-    }
-
-    // Set the foreground app's package name and category, so brightness can be corrected per app.
-    private void updateForegroundApp() {
-        // The ActivityTaskManager's lock tends to get contended, so this is done in a background
-        // thread and applied via this thread's handler synchronously.
-        BackgroundThread.getHandler().post(new Runnable() {
-            public void run() {
-                try {
-                    // The foreground app is the top activity of the focused tasks stack.
-                    final StackInfo info = mActivityTaskManager.getFocusedStackInfo();
-                    if (info == null || info.topActivity == null) {
-                        return;
-                    }
-                    final String packageName = info.topActivity.getPackageName();
-                    // If the app didn't change, there's nothing to do. Otherwise, we have to
-                    // update the category and re-apply the brightness correction.
-                    if (mForegroundAppPackageName != null
-                            && mForegroundAppPackageName.equals(packageName)) {
-                        return;
-                    }
-                    mPendingForegroundAppPackageName = packageName;
-                    ApplicationInfo app = mPackageManagerInternal.getApplicationInfo(packageName,
-                            0 /* flags */, Process.SYSTEM_UID /* filterCallingUid */, mUserId);
-                    mPendingForegroundAppCategory = app.category;
-                    mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP_SYNC);
-                } catch (RemoteException e) {
-                    // Nothing to do.
-                }
-            }
-        });
-    }
-
-    private void updateForegroundAppSync() {
-        mForegroundAppPackageName = mPendingForegroundAppPackageName;
-        mPendingForegroundAppPackageName = null;
-        mForegroundAppCategory = mPendingForegroundAppCategory;
-        mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
-        updateAutoBrightness(true /* sendUpdate */);
-    }
-
     private final class AutomaticBrightnessHandler extends Handler {
         public AutomaticBrightnessHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -888,14 +764,6 @@
                 case MSG_INVALIDATE_SHORT_TERM_MODEL:
                     invalidateShortTermModel();
                     break;
-
-                case MSG_UPDATE_FOREGROUND_APP:
-                    updateForegroundApp();
-                    break;
-
-                case MSG_UPDATE_FOREGROUND_APP_SYNC:
-                    updateForegroundAppSync();
-                    break;
             }
         }
     }
@@ -916,15 +784,6 @@
         }
     };
 
-    // Call back whenever the tasks stack changes, which includes tasks being created, removed, and
-    // moving to top.
-    class TaskStackListenerImpl extends TaskStackListener {
-        @Override
-        public void onTaskStackChanged() {
-            mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP);
-        }
-    }
-
     /** Callbacks to request updates to the display's power state. */
     interface Callbacks {
         void updateBrightness();
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 9fce644..76c191d 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -17,11 +17,9 @@
 package com.android.server.display;
 
 import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.hardware.display.BrightnessConfiguration;
-import android.hardware.display.BrightnessCorrection;
 import android.os.PowerManager;
 import android.util.MathUtils;
 import android.util.Pair;
@@ -44,12 +42,11 @@
  */
 public abstract class BrightnessMappingStrategy {
     private static final String TAG = "BrightnessMappingStrategy";
+    private static final boolean DEBUG = false;
 
     private static final float LUX_GRAD_SMOOTHING = 0.25f;
     private static final float MAX_GRAD = 1.0f;
 
-    protected boolean mLoggingEnabled;
-
     private static final Plog PLOG = Plog.createSystemPlog(TAG);
 
     @Nullable
@@ -164,22 +161,6 @@
     }
 
     /**
-     * Enable/disable logging.
-     *
-     * @param loggingEnabled
-     *      Whether logging should be on/off.
-     *
-     * @return Whether the method succeeded or not.
-     */
-    public boolean setLoggingEnabled(boolean loggingEnabled) {
-        if (mLoggingEnabled == loggingEnabled) {
-            return false;
-        }
-        mLoggingEnabled = loggingEnabled;
-        return true;
-    }
-
-    /**
      * Sets the {@link BrightnessConfiguration}.
      *
      * @param config The new configuration. If {@code null} is passed, the default configuration is
@@ -189,33 +170,15 @@
     public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config);
 
     /**
-     * Returns the desired brightness of the display based on the current ambient lux, including
-     * any context-related corrections.
+     * Returns the desired brightness of the display based on the current ambient lux.
      *
      * The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max
      * brightness and 0 is the display at minimum brightness.
      *
      * @param lux The current ambient brightness in lux.
-     * @param packageName the foreground app package name.
-     * @param category the foreground app package category.
      * @return The desired brightness of the display normalized to the range [0, 1.0].
      */
-    public abstract float getBrightness(float lux, String packageName,
-            @ApplicationInfo.Category int category);
-
-    /**
-     * Returns the desired brightness of the display based on the current ambient lux.
-     *
-     * The returned brightness wil be in the range [0, 1.0], where 1.0 is the display at max
-     * brightness and 0 is the display at minimum brightness.
-     *
-     * @param lux The current ambient brightness in lux.
-     *
-     * @return The desired brightness of the display normalized to the range [0, 1.0].
-     */
-    public float getBrightness(float lux) {
-        return getBrightness(lux, null /* packageName */, ApplicationInfo.CATEGORY_UNDEFINED);
-    }
+    public abstract float getBrightness(float lux);
 
     /**
      * Returns the current auto-brightness adjustment.
@@ -276,13 +239,13 @@
 
     public abstract void dump(PrintWriter pw);
 
-    protected float normalizeAbsoluteBrightness(int brightness) {
+    private static float normalizeAbsoluteBrightness(int brightness) {
         brightness = MathUtils.constrain(brightness,
                 PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
         return (float) brightness / PowerManager.BRIGHTNESS_ON;
     }
 
-    private Pair<float[], float[]> insertControlPoint(
+    private static Pair<float[], float[]> insertControlPoint(
             float[] luxLevels, float[] brightnessLevels, float lux, float brightness) {
         final int idx = findInsertionPoint(luxLevels, lux);
         final float[] newLuxLevels;
@@ -315,7 +278,7 @@
      * This assumes that {@code arr} is sorted. If all values in {@code arr} are greater
      * than val, then it will return the length of arr as the insertion point.
      */
-    private int findInsertionPoint(float[] arr, float val) {
+    private static int findInsertionPoint(float[] arr, float val) {
         for (int i = 0; i < arr.length; i++) {
             if (val <= arr[i]) {
                 return i;
@@ -324,8 +287,8 @@
         return arr.length;
     }
 
-    private void smoothCurve(float[] lux, float[] brightness, int idx) {
-        if (mLoggingEnabled) {
+    private static void smoothCurve(float[] lux, float[] brightness, int idx) {
+        if (DEBUG) {
             PLOG.logCurve("unsmoothed curve", lux, brightness);
         }
         float prevLux = lux[idx];
@@ -360,19 +323,19 @@
             prevBrightness = newBrightness;
             brightness[i] = newBrightness;
         }
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             PLOG.logCurve("smoothed curve", lux, brightness);
         }
     }
 
-    private float permissibleRatio(float currLux, float prevLux) {
+    private static float permissibleRatio(float currLux, float prevLux) {
         return MathUtils.exp(MAX_GRAD
                 * (MathUtils.log(currLux + LUX_GRAD_SMOOTHING)
                     - MathUtils.log(prevLux + LUX_GRAD_SMOOTHING)));
     }
 
-    protected float inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness,
-            float currentBrightness) {
+    private static float inferAutoBrightnessAdjustment(float maxGamma,
+            float desiredBrightness, float currentBrightness) {
         float adjustment = 0;
         float gamma = Float.NaN;
         // Extreme edge cases: use a simpler heuristic, as proper gamma correction around the edges
@@ -392,7 +355,7 @@
             adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);
         }
         adjustment = MathUtils.constrain(adjustment, -1, +1);
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" +
                     MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
             Slog.d(TAG, "inferAutoBrightnessAdjustment: " + currentBrightness + "^" + gamma + "=" +
@@ -401,16 +364,16 @@
         return adjustment;
     }
 
-    protected Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
+    private static Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
             float userLux, float userBrightness, float adjustment, float maxGamma) {
         float[] newLux = lux;
         float[] newBrightness = Arrays.copyOf(brightness, brightness.length);
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             PLOG.logCurve("unadjusted curve", newLux, newBrightness);
         }
         adjustment = MathUtils.constrain(adjustment, -1, 1);
         float gamma = MathUtils.pow(maxGamma, -adjustment);
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             Slog.d(TAG, "getAdjustedCurve: " + maxGamma + "^" + -adjustment + "=" +
                     MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
         }
@@ -419,7 +382,7 @@
                 newBrightness[i] = MathUtils.pow(newBrightness[i], gamma);
             }
         }
-        if (mLoggingEnabled) {
+        if (DEBUG) {
             PLOG.logCurve("gamma adjusted curve", newLux, newBrightness);
         }
         if (userLux != -1) {
@@ -427,7 +390,7 @@
                     userBrightness);
             newLux = curve.first;
             newBrightness = curve.second;
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 PLOG.logCurve("gamma and user adjusted curve", newLux, newBrightness);
                 // This is done for comparison.
                 curve = insertControlPoint(lux, brightness, userLux, userBrightness);
@@ -477,7 +440,7 @@
             mAutoBrightnessAdjustment = 0;
             mUserLux = -1;
             mUserBrightness = -1;
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 PLOG.start("simple mapping strategy");
             }
             computeSpline();
@@ -489,8 +452,7 @@
         }
 
         @Override
-        public float getBrightness(float lux, String packageName,
-                @ApplicationInfo.Category int category) {
+        public float getBrightness(float lux) {
             return mSpline.interpolate(lux);
         }
 
@@ -505,7 +467,7 @@
             if (adjustment == mAutoBrightnessAdjustment) {
                 return false;
             }
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
                 PLOG.start("auto-brightness adjustment");
@@ -523,7 +485,7 @@
         @Override
         public void addUserDataPoint(float lux, float brightness) {
             float unadjustedBrightness = getUnadjustedBrightness(lux);
-            if (mLoggingEnabled) {
+            if (DEBUG){
                 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
                 PLOG.start("add user data point")
                         .logPoint("user data point", lux, brightness)
@@ -532,7 +494,7 @@
             float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
                     brightness /* desiredBrightness */,
                     unadjustedBrightness /* currentBrightness */);
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
             }
@@ -545,7 +507,7 @@
         @Override
         public void clearUserDataPoints() {
             if (mUserLux != -1) {
-                if (mLoggingEnabled) {
+                if (DEBUG) {
                     Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
                     PLOG.start("clear user data points")
                             .logPoint("user data point", mUserLux, mUserBrightness);
@@ -652,7 +614,7 @@
             mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
 
             mDefaultConfig = config;
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 PLOG.start("physical mapping strategy");
             }
             mConfig = config;
@@ -667,7 +629,7 @@
             if (config.equals(mConfig)) {
                 return false;
             }
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 PLOG.start("brightness configuration");
             }
             mConfig = config;
@@ -676,17 +638,9 @@
         }
 
         @Override
-        public float getBrightness(float lux, String packageName,
-                @ApplicationInfo.Category int category) {
+        public float getBrightness(float lux) {
             float nits = mBrightnessSpline.interpolate(lux);
             float backlight = mNitsToBacklightSpline.interpolate(nits);
-            // Correct the brightness according to the current application and its category, but
-            // only if no user data point is set (as this will oevrride the user setting).
-            if (mUserLux == -1) {
-                backlight = correctBrightness(backlight, packageName, category);
-            } else if (mLoggingEnabled) {
-                Slog.d(TAG, "user point set, correction not applied");
-            }
             return backlight;
         }
 
@@ -701,7 +655,7 @@
             if (adjustment == mAutoBrightnessAdjustment) {
                 return false;
             }
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
                 PLOG.start("auto-brightness adjustment");
@@ -719,7 +673,7 @@
         @Override
         public void addUserDataPoint(float lux, float brightness) {
             float unadjustedBrightness = getUnadjustedBrightness(lux);
-            if (mLoggingEnabled) {
+            if (DEBUG){
                 Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
                 PLOG.start("add user data point")
                         .logPoint("user data point", lux, brightness)
@@ -728,7 +682,7 @@
             float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
                     brightness /* desiredBrightness */,
                     unadjustedBrightness /* currentBrightness */);
-            if (mLoggingEnabled) {
+            if (DEBUG) {
                 Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
                         adjustment);
             }
@@ -741,7 +695,7 @@
         @Override
         public void clearUserDataPoints() {
             if (mUserLux != -1) {
-                if (mLoggingEnabled) {
+                if (DEBUG) {
                     Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
                     PLOG.start("clear user data points")
                             .logPoint("user data point", mUserLux, mUserBrightness);
@@ -804,21 +758,5 @@
             Spline spline = Spline.createSpline(curve.first, curve.second);
             return mNitsToBacklightSpline.interpolate(spline.interpolate(lux));
         }
-
-        private float correctBrightness(float brightness, String packageName, int category) {
-            if (packageName != null) {
-                BrightnessCorrection correction = mConfig.getCorrectionByPackageName(packageName);
-                if (correction != null) {
-                    return correction.apply(brightness);
-                }
-            }
-            if (category != ApplicationInfo.CATEGORY_UNDEFINED) {
-                BrightnessCorrection correction = mConfig.getCorrectionByCategory(category);
-                if (correction != null) {
-                    return correction.apply(brightness);
-                }
-            }
-            return brightness;
-        }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b1ba05c..0a1a9a2 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2144,14 +2144,6 @@
                     mContext.getPackageName());
         }
 
-        void setAutoBrightnessLoggingEnabled(boolean enabled) {
-            if (mDisplayPowerController != null) {
-                synchronized (mSyncRoot) {
-                    mDisplayPowerController.setAutoBrightnessLoggingEnabled(enabled);
-                }
-            }
-        }
-
         private boolean validatePackageName(int uid, String packageName) {
             if (packageName != null) {
                 String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index abbfc7b..27cad1e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -17,9 +17,14 @@
 package com.android.server.display;
 
 import android.content.Intent;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
 import android.os.ShellCommand;
+import android.util.Slog;
 
 import java.io.PrintWriter;
+import java.lang.NumberFormatException;
 
 class DisplayManagerShellCommand extends ShellCommand {
     private static final String TAG = "DisplayManagerShellCommand";
@@ -41,10 +46,6 @@
                 return setBrightness();
             case "reset-brightness-configuration":
                 return resetBrightnessConfiguration();
-            case "ab-logging-enable":
-                return setAutoBrightnessLoggingEnabled(true);
-            case "ab-logging-disable":
-                return setAutoBrightnessLoggingEnabled(false);
             default:
                 return handleDefaultCommands(cmd);
         }
@@ -61,10 +62,6 @@
         pw.println("    Sets the current brightness to BRIGHTNESS (a number between 0 and 1).");
         pw.println("  reset-brightness-configuration");
         pw.println("    Reset the brightness to its default configuration.");
-        pw.println("  ab-logging-enable");
-        pw.println("    Enable auto-brightness logging.");
-        pw.println("  ab-logging-disable");
-        pw.println("    Disable auto-brightness logging.");
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
     }
@@ -92,9 +89,4 @@
         mService.resetBrightnessConfiguration();
         return 0;
     }
-
-    private int setAutoBrightnessLoggingEnabled(boolean enabled) {
-        mService.setAutoBrightnessLoggingEnabled(enabled);
-        return 0;
-    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c9ed9f7..249270b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -523,9 +523,6 @@
     public void onSwitchUser(@UserIdInt int newUserId) {
         handleSettingsChange(true /* userSwitch */);
         mBrightnessTracker.onSwitchUser(newUserId);
-        if (mAutomaticBrightnessController != null) {
-            mAutomaticBrightnessController.onSwitchUser(newUserId);
-        }
     }
 
     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -1839,10 +1836,4 @@
             mHandler.sendMessage(msg);
         }
     }
-
-    void setAutoBrightnessLoggingEnabled(boolean enabled) {
-        if (mAutomaticBrightnessController != null) {
-            mAutomaticBrightnessController.setLoggingEnabled(enabled);
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 9aec43b..89cef62 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -16,6 +16,13 @@
 
 package com.android.server.display;
 
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import android.annotation.Nullable;
 import android.graphics.Point;
 import android.hardware.display.BrightnessConfiguration;
@@ -23,20 +30,13 @@
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.Pair;
 import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-
-import libcore.io.IoUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -50,9 +50,12 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
+import libcore.io.IoUtils;
+
 /**
  * Manages persistent state recorded by the display manager service as an XML file.
  * Caller must acquire lock on the data store before accessing it.
@@ -107,9 +110,14 @@
 
     private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations";
     private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration";
+    private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
+    private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
     private static final String ATTR_USER_SERIAL = "user-serial";
     private static final String ATTR_PACKAGE_NAME = "package-name";
     private static final String ATTR_TIME_STAMP = "timestamp";
+    private static final String ATTR_LUX = "lux";
+    private static final String ATTR_NITS = "nits";
+    private static final String ATTR_DESCRIPTION = "description";
 
     // Remembered Wifi display devices.
     private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
@@ -638,8 +646,7 @@
                     }
 
                     try {
-                        BrightnessConfiguration config =
-                                BrightnessConfiguration.loadFromXml(parser);
+                        BrightnessConfiguration config = loadConfigurationFromXml(parser);
                         if (userSerial >= 0 && config != null) {
                             mConfigurations.put(userSerial, config);
                             if (timeStamp != -1) {
@@ -656,6 +663,56 @@
             }
         }
 
+        private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            final int outerDepth = parser.getDepth();
+            String description = null;
+            Pair<float[], float[]> curve = null;
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
+                    description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
+                    curve = loadCurveFromXml(parser);
+                }
+            }
+            if (curve == null) {
+                return null;
+            }
+            final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
+                    curve.first, curve.second);
+            builder.setDescription(description);
+            return builder.build();
+        }
+
+        private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            final int outerDepth = parser.getDepth();
+            List<Float> luxLevels = new ArrayList<>();
+            List<Float> nitLevels = new ArrayList<>();
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                if (TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
+                    luxLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_LUX)));
+                    nitLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_NITS)));
+                }
+            }
+            final int N = luxLevels.size();
+            float[] lux = new float[N];
+            float[] nits = new float[N];
+            for (int i = 0; i < N; i++) {
+                lux[i] = luxLevels.get(i);
+                nits[i] = nitLevels.get(i);
+            }
+            return Pair.create(lux, nits);
+        }
+
+        private static float loadFloat(String val) {
+            try {
+                return Float.parseFloat(val);
+            } catch (NullPointerException | NumberFormatException e) {
+                Slog.e(TAG, "Failed to parse float loading brightness config", e);
+                return Float.NEGATIVE_INFINITY;
+            }
+        }
+
         public void saveToXml(XmlSerializer serializer) throws IOException {
             for (int i = 0; i < mConfigurations.size(); i++) {
                 final int userSerial = mConfigurations.keyAt(i);
@@ -671,11 +728,27 @@
                 if (timestamp != -1) {
                     serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp));
                 }
-                config.saveToXml(serializer);
+                saveConfigurationToXml(serializer, config);
                 serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
             }
         }
 
+        private static void saveConfigurationToXml(XmlSerializer serializer,
+                BrightnessConfiguration config) throws IOException {
+            serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
+            if (config.getDescription() != null) {
+                serializer.attribute(null, ATTR_DESCRIPTION, config.getDescription());
+            }
+            final Pair<float[], float[]> curve = config.getCurve();
+            for (int i = 0; i < curve.first.length; i++) {
+                serializer.startTag(null, TAG_BRIGHTNESS_POINT);
+                serializer.attribute(null, ATTR_LUX, Float.toString(curve.first[i]));
+                serializer.attribute(null, ATTR_NITS, Float.toString(curve.second[i]));
+                serializer.endTag(null, TAG_BRIGHTNESS_POINT);
+            }
+            serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
+        }
+
         public void dump(final PrintWriter pw, final String prefix) {
             for (int i = 0; i < mConfigurations.size(); i++) {
                 final int userSerial = mConfigurations.keyAt(i);
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index d154830..1f0f94a 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.hardware.hdmi.HdmiDeviceInfo;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -316,7 +317,7 @@
      * <p>When ARC is initiated, this port will be used to turn on ARC.
      */
     static final String PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT =
-        "persist.sys.hdmi.property_sytem_audio_device_arc_port";
+            "persist.sys.hdmi.property_sytem_audio_device_arc_port";
 
     /**
      * Property to strip local audio of amplifier and use local speaker
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 3845954..2ac04d1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -25,6 +25,7 @@
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.os.SystemProperties;
+import android.provider.Settings.Global;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -50,6 +51,11 @@
 
     private boolean mTvSystemAudioModeSupport;
 
+    // Whether the auido system will turn TV off when it's powering off
+    private boolean mAutoTvOff;
+    // Whether the auido system will broadcast standby to the system when it's powering off
+    private boolean mAutoDeviceOff;
+
     // Whether ARC is available or not. "true" means that ARC is established between TV and
     // AVR as audio receiver.
     @ServiceThreadOnly private boolean mArcEstablished = false;
@@ -60,6 +66,10 @@
         // TODO(amyjojo) make System Audio Control controllable by users
         /*mSystemAudioControlFeatureEnabled =
         mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/
+        mAutoDeviceOff = mService.readBooleanSetting(
+            Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true);
+        mAutoTvOff = mService.readBooleanSetting(
+            Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED, true);
     }
 
     @Override
@@ -74,6 +84,21 @@
                     mSystemAudioActivated ? "true" : "false");
         }
         terminateSystemAudioMode();
+
+        HdmiLogger.debug(TAG + " onStandby, initiatedByCec:" + initiatedByCec
+                + ", mAutoDeviceOff: " + mAutoDeviceOff + ", mAutoTvOff: " + mAutoTvOff);
+        if (!mService.isControlEnabled() || initiatedByCec) {
+            return;
+        }
+        if (mAutoDeviceOff) {
+            mService.sendCecCommand(
+                    HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
+        } else if (mAutoTvOff) {
+            mService.sendCecCommand(
+                    HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
+        }
+        return;
+
     }
 
     @Override
@@ -95,6 +120,11 @@
         startQueuedActions();
     }
 
+    @Override
+    protected int findKeyReceiverAddress() {
+        return Constants.ADDR_TV;
+    }
+
     @VisibleForTesting
     protected void systemAudioControlOnPowerOn(
             int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) {
@@ -486,4 +516,17 @@
             return mArcEstablished;
         }
     }
+
+    @ServiceThreadOnly
+    protected void setAutoTvOff(boolean autoTvOff) {
+        assertRunOnServiceThread();
+        mAutoTvOff = autoTvOff;
+    }
+
+    @Override
+    @ServiceThreadOnly
+    void setAutoDeviceOff(boolean autoDeviceOff) {
+        assertRunOnServiceThread();
+        mAutoDeviceOff = autoDeviceOff;
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index f468c0b..e3a4084 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -18,6 +18,7 @@
 
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
+
 import static com.android.server.hdmi.Constants.DISABLED;
 import static com.android.server.hdmi.Constants.ENABLED;
 import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
@@ -67,6 +68,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
@@ -76,6 +78,9 @@
 import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
 import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
 import com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
+
+import libcore.util.EmptyArray;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -85,7 +90,6 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import libcore.util.EmptyArray;
 
 /**
  * Provides a service for sending and processing HDMI control messages,
@@ -588,6 +592,11 @@
                     }
                     // No need to propagate to HAL.
                     break;
+                case Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED:
+                    if (isAudioSystemDevice()) {
+                        audioSystem().setAutoTvOff(enabled);
+                    }
+                    break;
                 case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
                     if (isTvDeviceEnabled()) {
                         tv().setSystemAudioControlFeatureEnabled(enabled);
@@ -1469,12 +1478,12 @@
 
         @Override
         public boolean getSystemAudioMode() {
+            // TODO(shubang): handle getSystemAudioMode() for all device types
             enforceAccessPermission();
             HdmiCecLocalDeviceTv tv = tv();
-            if (tv == null) {
-                return false;
-            }
-            return tv.isSystemAudioActivated();
+            HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+            return (tv != null && tv.isSystemAudioActivated())
+                    || (audioSystem != null && audioSystem.isSystemAudioActivated());
         }
 
         @Override
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index d51da99..052d579 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -40,6 +40,7 @@
 import android.media.AudioSystem;
 import android.media.IAudioService;
 import android.media.IRemoteVolumeController;
+import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -895,6 +896,21 @@
         }
 
         @Override
+        public void notifySession2Created(Session2Token sessionToken) throws RemoteException {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG) {
+                    Log.d(TAG, "Session2 is created " + sessionToken);
+                }
+                // TODO: Keep the session.
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         public List<IBinder> getSessions(ComponentName componentName, int userId) {
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0d6dadf..af55605 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -270,14 +270,12 @@
  * enforcement.
  *
  * <p>
- * This class uses 2-3 locks to synchronize state:
+ * This class uses 2 locks to synchronize state:
  * <ul>
  * <li>{@code mUidRulesFirstLock}: used to guard state related to individual UIDs (such as firewall
  * rules).
  * <li>{@code mNetworkPoliciesSecondLock}: used to guard state related to network interfaces (such
  * as network policies).
- * <li>{@code allLocks}: not a "real" lock, but an indication (through @GuardedBy) that all locks
- * must be held.
  * </ul>
  *
  * <p>
@@ -419,7 +417,8 @@
     final Object mUidRulesFirstLock = new Object();
     final Object mNetworkPoliciesSecondLock = new Object();
 
-    @GuardedBy("allLocks") volatile boolean mSystemReady;
+    @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
+    volatile boolean mSystemReady;
 
     @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackground;
     @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower;
@@ -558,7 +557,7 @@
 
     private final ServiceThread mUidEventThread;
 
-    @GuardedBy("allLocks")
+    @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
     private final AtomicFile mPolicyFile;
 
     private final AppOpsManager mAppOps;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 88d73fb..c222e69 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -767,6 +767,23 @@
         return installed;
     }
 
+    protected Set<String> getAllowedPackages() {
+        final Set<String> allowedPackages = new ArraySet<>();
+        for (int k = 0; k < mApproved.size(); k++) {
+            ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k);
+            for (int i = 0; i < allowedByType.size(); i++) {
+                final ArraySet<String> allowed = allowedByType.valueAt(i);
+                for (int j = 0; j < allowed.size(); j++) {
+                    String pkgName = getPackageName(allowed.valueAt(j));
+                    if (!TextUtils.isEmpty(pkgName)) {
+                        allowedPackages.add(pkgName);
+                    }
+                }
+            }
+        }
+        return allowedPackages;
+    }
+
     private void trimApprovedListsAccordingToInstalledServices() {
         int N = mApproved.size();
         for (int i = 0 ; i < N; i++) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 85a8d93..c68e0f9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -75,6 +75,8 @@
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
+import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
+import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL;
 import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL;
@@ -763,8 +765,7 @@
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)
                         .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART,
-                                (Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION
-                                        == action.getSemanticAction()) ? 1 : 0)
+                                action.isContextual() ? 1 : 0)
                         .addTaggedData(
                                 MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
                                 generatedByAssistant ? 1 : 0));
@@ -968,7 +969,7 @@
                             r.getNumSmartActionsAdded())
                     .addTaggedData(
                             MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
-                            r.getSuggestionsGeneratedByAssistant());
+                            r.getSuggestionsGeneratedByAssistant() ? 1 : 0);
             mMetricsLogger.write(logMaker);
         }
     }
@@ -1314,6 +1315,11 @@
     }
 
     @VisibleForTesting
+    void setHints(int hints) {
+        mListenerHints = hints;
+    }
+
+    @VisibleForTesting
     void setVibrator(Vibrator vibrator) {
         mVibrator = vibrator;
     }
@@ -1695,8 +1701,16 @@
     }
 
     private void sendRegisteredOnlyBroadcast(String action) {
-        getContext().sendBroadcastAsUser(new Intent(action)
-                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
+        Intent intent = new Intent(action);
+        getContext().sendBroadcastAsUser(intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+                UserHandle.ALL, null);
+        // explicitly send the broadcast to all DND packages, even if they aren't currently running
+        intent.setFlags(0);
+        final Set<String> dndApprovedPackages = mConditionProviders.getAllowedPackages();
+        for (String pkg : dndApprovedPackages) {
+            intent.setPackage(pkg);
+            getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
     }
 
     @Override
@@ -2635,6 +2649,9 @@
             // Zen
             mConditionProviders.onPackagesChanged(true, packages, uids);
 
+            // Snoozing
+            mSnoozeHelper.clearData(UserHandle.getUserId(uid), packageName);
+
             // Reset notification preferences
             if (!fromApp) {
                 mPreferencesHelper.onPackagesChanged(
@@ -3990,6 +4007,20 @@
         if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
             return "listenerHints";
         }
+        if (record != null && record.getAudioAttributes() != null) {
+            if ((mListenerHints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
+                if (record.getAudioAttributes().getUsage()
+                        != AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+                    return "listenerNoti";
+                }
+            }
+            if ((mListenerHints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
+                if (record.getAudioAttributes().getUsage()
+                        == AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+                    return "listenerCall";
+                }
+            }
+        }
         if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
             return "callState";
         }
@@ -4420,6 +4451,8 @@
                     if (pendingIntent != null) {
                         am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
                                 WHITELIST_TOKEN, duration);
+                        am.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),
+                                WHITELIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER));
                     }
                 }
             }
@@ -5675,19 +5708,17 @@
             }
             int indexBefore = findNotificationRecordIndexLocked(record);
             boolean interceptBefore = record.isIntercepted();
-            float contactAffinityBefore = record.getContactAffinity();
             int visibilityBefore = record.getPackageVisibilityOverride();
             recon.applyChangesLocked(record);
             applyZenModeLocked(record);
             mRankingHelper.sort(mNotificationList);
             int indexAfter = findNotificationRecordIndexLocked(record);
             boolean interceptAfter = record.isIntercepted();
-            float contactAffinityAfter = record.getContactAffinity();
             int visibilityAfter = record.getPackageVisibilityOverride();
             changed = indexBefore != indexAfter || interceptBefore != interceptAfter
                     || visibilityBefore != visibilityAfter;
             if (interceptBefore && !interceptAfter
-                    && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
+                    && record.isNewEnoughForAlerting(System.currentTimeMillis())) {
                 buzzBeepBlinkLocked(record);
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index e2c64ca..9942f59 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -92,6 +92,8 @@
     static final String TAG = "NotificationRecord";
     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
     private static final int MAX_LOGTAG_LENGTH = 35;
+    // the period after which a notification is updated where it can make sound
+    private static final int MAX_SOUND_DELAY_MS = 2000;
     final StatusBarNotification sbn;
     IActivityManager mAm;
     UriGrantsManagerInternal mUgmInternal;
@@ -125,7 +127,8 @@
     private long mVisibleSinceMs;
 
     // The most recent update time, or the creation time if no updates.
-    private long mUpdateTimeMs;
+    @VisibleForTesting
+    final long mUpdateTimeMs;
 
     // The most recent interruption time, or the creation time if no updates. Differs from the
     // above value because updates are filtered based on whether they actually interrupted the
@@ -165,7 +168,7 @@
     private String mChannelIdLogTag;
     /**
      * This list contains system generated smart actions from NAS, app-generated smart actions are
-     * stored in Notification.actions marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION.
+     * stored in Notification.actions with isContextual() set to true.
      */
     private ArrayList<Notification.Action> mSystemGeneratedSmartActions;
     private ArrayList<CharSequence> mSmartReplies;
@@ -826,6 +829,10 @@
         return mIntercept;
     }
 
+    public boolean isNewEnoughForAlerting(long now) {
+        return getFreshnessMs(now) <= MAX_SOUND_DELAY_MS;
+    }
+
     public void setHidden(boolean hidden) {
         mHidden = hidden;
     }
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 2b581d6..abc9841 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.app.AlarmManager;
+import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -163,7 +164,6 @@
                     mSnoozedNotifications.get(userId).get(pkg);
             if (recordsForPkg != null) {
                 final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
-                String key = null;
                 for (Map.Entry<String, NotificationRecord> record : records) {
                     final StatusBarNotification sbn = record.getValue().sbn;
                     if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
@@ -305,6 +305,30 @@
         }
     }
 
+    protected void clearData(int userId, String pkg) {
+        ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
+                mSnoozedNotifications.get(userId);
+        if (records == null) {
+            return;
+        }
+        ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg);
+        if (pkgRecords == null) {
+            return;
+        }
+        for (int i = pkgRecords.size() - 1; i >= 0; i--) {
+            final NotificationRecord r = pkgRecords.removeAt(i);
+            if (r != null) {
+                mPackages.remove(r.getKey());
+                mUsers.remove(r.getKey());
+                final PendingIntent pi = createPendingIntent(pkg, r.getKey(), userId);
+                mAm.cancel(pi);
+                MetricsLogger.action(r.getLogMaker()
+                        .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
+                        .setType(MetricsProto.MetricsEvent.TYPE_DISMISS));
+            }
+        }
+    }
+
     private PendingIntent createPendingIntent(String pkg, String key, int userId) {
         return PendingIntent.getBroadcast(mContext,
                 REQUEST_CODE_REPOST,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index e5b1878..afc0b72 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -375,8 +375,7 @@
             newConfig = mConfig.copy();
             for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
                 ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
-                if (rule.component.getPackageName().equals(packageName)
-                        && canManageAutomaticZenRule(rule)) {
+                if (rule.pkg.equals(packageName) && canManageAutomaticZenRule(rule)) {
                     newConfig.automaticRules.removeAt(i);
                 }
             }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 66eae5e..7ca39df 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -209,8 +209,7 @@
         synchronized (mSessions) {
             readSessionsLocked();
 
-            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, false /*isInstant*/);
-            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, true /*isInstant*/);
+            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL);
 
             final ArraySet<File> unclaimedIcons = newArraySet(
                     mSessionsDir.listFiles());
@@ -230,8 +229,8 @@
     }
 
     @GuardedBy("mSessions")
-    private void reconcileStagesLocked(String volumeUuid, boolean isEphemeral) {
-        final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
+    private void reconcileStagesLocked(String volumeUuid) {
+        final File stagingDir = getTmpSessionDir(volumeUuid);
         final ArraySet<File> unclaimedStages = newArraySet(
                 stagingDir.listFiles(sStageFilter));
 
@@ -252,7 +251,7 @@
 
     public void onPrivateVolumeMounted(String volumeUuid) {
         synchronized (mSessions) {
-            reconcileStagesLocked(volumeUuid, false /*isInstant*/);
+            reconcileStagesLocked(volumeUuid);
         }
     }
 
@@ -269,9 +268,9 @@
             try {
                 final int sessionId = allocateSessionIdLocked();
                 mLegacySessions.put(sessionId, true);
-                final File stageDir = buildStageDir(volumeUuid, sessionId, isEphemeral);
-                prepareStageDir(stageDir);
-                return stageDir;
+                final File sessionStageDir = buildTmpSessionDir(sessionId, volumeUuid);
+                prepareStageDir(sessionStageDir);
+                return sessionStageDir;
             } catch (IllegalStateException e) {
                 throw new IOException(e);
             }
@@ -526,9 +525,7 @@
         String stageCid = null;
         if (!params.isMultiPackage) {
             if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
-                final boolean isInstant =
-                        (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
-                stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);
+                stageDir = buildSessionDir(sessionId, params);
             } else {
                 stageCid = buildExternalStageCid(sessionId);
             }
@@ -634,13 +631,21 @@
         throw new IllegalStateException("Failed to allocate session ID");
     }
 
-    private File buildStagingDir(String volumeUuid, boolean isEphemeral) {
+    private File getTmpSessionDir(String volumeUuid) {
         return Environment.getDataAppDirectory(volumeUuid);
     }
 
-    private File buildStageDir(String volumeUuid, int sessionId, boolean isEphemeral) {
-        final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
-        return new File(stagingDir, "vmdl" + sessionId + ".tmp");
+    private File buildTmpSessionDir(int sessionId, String volumeUuid) {
+        final File sessionStagingDir = getTmpSessionDir(volumeUuid);
+        return new File(sessionStagingDir, "vmdl" + sessionId + ".tmp");
+    }
+
+    private File buildSessionDir(int sessionId, SessionParams params) {
+        if (params.isStaged) {
+            final File sessionStagingDir = Environment.getDataStagingDirectory(params.volumeUuid);
+            return new File(sessionStagingDir, "session_" + sessionId);
+        }
+        return buildTmpSessionDir(sessionId, params.volumeUuid);
     }
 
     static void prepareStageDir(File stageDir) throws IOException {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d290f3f..3cb7714 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
@@ -106,6 +107,7 @@
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
 import com.android.server.pm.dex.DexManager;
+import com.android.server.security.VerityUtils;
 
 import libcore.io.IoUtils;
 
@@ -285,6 +287,8 @@
     private final List<String> mResolvedNativeLibPaths = new ArrayList<>();
     @GuardedBy("mLock")
     private File mInheritedFilesBase;
+    @GuardedBy("mLock")
+    private boolean mVerityFound;
 
     private static final FileFilter sAddedFilter = new FileFilter() {
         @Override
@@ -294,6 +298,7 @@
             if (file.isDirectory()) return false;
             if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
             if (DexMetadataHelper.isDexMetadataFile(file)) return false;
+            if (VerityUtils.isFsveritySignatureFile(file)) return false;
             return true;
         }
     };
@@ -978,18 +983,18 @@
 
         // Read transfers from the original owner stay open, but as the session's data
         // cannot be modified anymore, there is no leak of information.
-        if (!params.isMultiPackage) {
+        // For staged sessions, the validation is performed by StagingManager.
+        if (!params.isMultiPackage && !params.isStaged) {
             final PackageInfo pkgInfo = mPm.getPackageInfo(
                     params.appPackageName, PackageManager.GET_SIGNATURES
                             | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
 
             resolveStageDirLocked();
 
-            // Verify that stage looks sane with respect to existing application.
-            // This currently only ensures packageName, versionCode, and certificate
-            // consistency.
             try {
                 if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+                    // TODO(b/118865310): Remove this when APEX validation is done via
+                    //                    StagingManager.
                     validateApexInstallLocked(pkgInfo);
                 } else {
                     // Verify that stage looks sane with respect to existing application.
@@ -1062,16 +1067,17 @@
     @GuardedBy("mLock")
     private void commitLocked()
             throws PackageManagerException {
+        if (params.isStaged) {
+            mStagingManager.commitSession(this);
+            destroyInternal();
+            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
+            return;
+        }
         final PackageManagerService.ActiveInstallSession committingSession =
                 makeSessionActiveLocked();
         if (committingSession == null) {
             return;
         }
-        if (isStaged()) {
-            mStagingManager.commitSession(this);
-            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
-            return;
-        }
         if (isMultiPackage()) {
             final int[] childSessionIds = getChildSessionIds();
             List<PackageManagerService.ActiveInstallSession> childSessions =
@@ -1361,6 +1367,17 @@
         mResolvedStagedFiles.clear();
         mResolvedInheritedFiles.clear();
 
+        // Partial installs must be consistent with existing install
+        if (params.mode == SessionParams.MODE_INHERIT_EXISTING
+                && (pkgInfo == null || pkgInfo.applicationInfo == null)) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    "Missing existing base package");
+        }
+        // Default to require only if existing base has fs-verity.
+        mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled()
+                && params.mode == SessionParams.MODE_INHERIT_EXISTING
+                && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());
+
         try {
             resolveStageDirLocked();
         } catch (IOException e) {
@@ -1424,7 +1441,7 @@
             }
 
             final File targetFile = new File(mResolvedStageDir, targetName);
-            maybeRenameFile(addedFile, targetFile);
+            resolveAndStageFile(addedFile, targetFile);
 
             // Base is coming from session
             if (apk.splitName == null) {
@@ -1432,8 +1449,6 @@
                 baseApk = apk;
             }
 
-            mResolvedStagedFiles.add(targetFile);
-
             final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
             if (dexMetadataFile != null) {
                 if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
@@ -1442,8 +1457,7 @@
                 }
                 final File targetDexMetadataFile = new File(mResolvedStageDir,
                         DexMetadataHelper.buildDexMetadataPathForApk(targetName));
-                mResolvedStagedFiles.add(targetDexMetadataFile);
-                maybeRenameFile(dexMetadataFile, targetDexMetadataFile);
+                resolveAndStageFile(dexMetadataFile, targetDexMetadataFile);
             }
         }
 
@@ -1486,12 +1500,6 @@
             }
 
         } else {
-            // Partial installs must be consistent with existing install
-            if (pkgInfo == null || pkgInfo.applicationInfo == null) {
-                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                        "Missing existing base package for " + mPackageName);
-            }
-
             final PackageLite existing;
             final ApkLite existingBase;
             ApplicationInfo appInfo = pkgInfo.applicationInfo;
@@ -1611,6 +1619,39 @@
         }
     }
 
+    private void resolveAndStageFile(File origFile, File targetFile)
+            throws PackageManagerException {
+        mResolvedStagedFiles.add(targetFile);
+        maybeRenameFile(origFile, targetFile);
+
+        final File originalSignature = new File(
+                VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
+        // Make sure .fsv_sig exists when it should, then resolve and stage it.
+        if (originalSignature.exists()) {
+            // mVerityFound can only change from false to true here during the staging loop. Since
+            // all or none of files should have .fsv_sig, this should only happen in the first time
+            // (or never), otherwise bail out.
+            if (!mVerityFound) {
+                mVerityFound = true;
+                if (mResolvedStagedFiles.size() > 1) {
+                    throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
+                            "Some file is missing fs-verity signature");
+                }
+            }
+        } else {
+            if (!mVerityFound) {
+                return;
+            }
+            throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
+                    "Missing corresponding fs-verity signature to " + origFile);
+        }
+
+        final File stagedSignature = new File(
+                VerityUtils.getFsveritySignatureFilePath(targetFile.getPath()));
+        maybeRenameFile(originalSignature, stagedSignature);
+        mResolvedStagedFiles.add(stagedSignature);
+    }
+
     @GuardedBy("mLock")
     private void assertApkConsistentLocked(String tag, ApkLite apk)
             throws PackageManagerException {
@@ -1984,7 +2025,11 @@
                 bridge.forceClose();
             }
         }
-        if (stageDir != null) {
+        // For staged sessions, we don't delete the directory where the packages have been copied,
+        // since these packages are supposed to be read on reboot. StagingManager is in charge of
+        // deleting these dirs when the staged session has reached a final state.
+        // TODO(b/118865310): Implement packageDir deletion in StagingManager.
+        if (stageDir != null && !params.isStaged) {
             try {
                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
             } catch (InstallerException ignored) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 136c7c9..522ab0b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8549,16 +8549,16 @@
     }
 
     /**
-     * Returns if full apk verification can be skipped for the whole package, including the splits.
+     * Returns if forced apk verification can be skipped for the whole package, including splits.
      */
-    private boolean canSkipFullPackageVerification(PackageParser.Package pkg) {
-        if (!canSkipFullApkVerification(pkg.baseCodePath)) {
+    private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) {
+        if (!canSkipForcedApkVerification(pkg.baseCodePath)) {
             return false;
         }
         // TODO: Allow base and splits to be verified individually.
         if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
             for (int i = 0; i < pkg.splitCodePaths.length; i++) {
-                if (!canSkipFullApkVerification(pkg.splitCodePaths[i])) {
+                if (!canSkipForcedApkVerification(pkg.splitCodePaths[i])) {
                     return false;
                 }
             }
@@ -8567,14 +8567,17 @@
     }
 
     /**
-     * Returns if full apk verification can be skipped, depending on current FSVerity setup and
+     * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
      * whether the apk contains signed root hash.  Note that the signer's certificate still needs to
      * match one in a trusted source, and should be done separately.
      */
-    private boolean canSkipFullApkVerification(String apkPath) {
-        final byte[] rootHashObserved;
+    private boolean canSkipForcedApkVerification(String apkPath) {
+        if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
+            return VerityUtils.hasFsverity(apkPath);
+        }
+
         try {
-            rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
+            final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
             if (rootHashObserved == null) {
                 return false;  // APK does not contain Merkle tree root hash.
             }
@@ -8746,7 +8749,7 @@
         // in verified partition, or can be verified on access (when apk verity is enabled). In both
         // cases, only data in Signing Block is verified instead of the whole file.
         final boolean skipVerify = scanSystemPartition
-                || (forceCollect && canSkipFullPackageVerification(pkg));
+                || (forceCollect && canSkipForcedPackageVerification(pkg));
         collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
 
         // Reset profile if the application version is changed
@@ -16552,44 +16555,11 @@
             throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
         }
 
-        if (PackageManagerServiceUtils.isApkVerityEnabled()) {
-            String apkPath = null;
-            synchronized (mPackages) {
-                // Note that if the attacker managed to skip verify setup, for example by tampering
-                // with the package settings, upon reboot we will do full apk verification when
-                // verity is not detected.
-                final PackageSetting ps = mSettings.mPackages.get(pkgName);
-                if (ps != null && ps.isPrivileged()) {
-                    apkPath = pkg.baseCodePath;
-                }
-            }
-            if (apkPath != null) {
-                final VerityUtils.SetupResult result =
-                        VerityUtils.generateApkVeritySetupData(apkPath, null /* signaturePath */,
-                                true /* skipSigningBlock */);
-                if (result.isOk()) {
-                    if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath);
-                    FileDescriptor fd = result.getUnownedFileDescriptor();
-                    try {
-                        final byte[] signedRootHash =
-                                VerityUtils.generateApkVerityRootHash(apkPath);
-                        mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
-                        mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
-                    } catch (InstallerException | IOException | DigestException |
-                            NoSuchAlgorithmException e) {
-                        throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
-                                "Failed to set up verity: " + e);
-                    } finally {
-                        IoUtils.closeQuietly(fd);
-                    }
-                } else if (result.isFailed()) {
-                    throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
-                            "Failed to generate verity");
-                } else {
-                    // Do nothing if verity is skipped. Will fall back to full apk verification on
-                    // reboot.
-                }
-            }
+        try {
+            setUpFsVerityIfPossible(pkg);
+        } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) {
+            throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+                    "Failed to set up verity: " + e);
         }
 
         if (!instantApp) {
@@ -16887,6 +16857,86 @@
         }
     }
 
+    /**
+     * Set up fs-verity for the given package if possible.  This requires a feature flag of system
+     * property to be enabled only if the kernel supports fs-verity.
+     *
+     * <p>When the feature flag is set to legacy mode, only APK is supported (with some experimental
+     * kernel patches). In normal mode, all file format can be supported.
+     */
+    private void setUpFsVerityIfPossible(PackageParser.Package pkg) throws InstallerException,
+            PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
+        final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
+        final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
+        if (!standardMode && !legacyMode) {
+            return;
+        }
+
+        // Collect files we care for fs-verity setup.
+        ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
+        if (legacyMode) {
+            synchronized (mPackages) {
+                final PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
+                if (ps != null && ps.isPrivileged()) {
+                    fsverityCandidates.put(pkg.baseCodePath, null);
+                    if (pkg.splitCodePaths != null) {
+                        for (String splitPath : pkg.splitCodePaths) {
+                            fsverityCandidates.put(splitPath, null);
+                        }
+                    }
+                }
+            }
+        } else {
+            // NB: These files will become only accessible if the signing key is loaded in kernel's
+            // .fs-verity keyring.
+            fsverityCandidates.put(pkg.baseCodePath,
+                    VerityUtils.getFsveritySignatureFilePath(pkg.baseCodePath));
+
+            final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(pkg.baseCodePath);
+            if (new File(dmPath).exists()) {
+                fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
+            }
+
+            if (pkg.splitCodePaths != null) {
+                for (String path : pkg.splitCodePaths) {
+                    fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
+
+                    final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
+                    if (new File(splitDmPath).exists()) {
+                        fsverityCandidates.put(splitDmPath,
+                                VerityUtils.getFsveritySignatureFilePath(splitDmPath));
+                    }
+                }
+            }
+        }
+
+        for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {
+            final String filePath = entry.getKey();
+            final String signaturePath = entry.getValue();
+
+            final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(
+                    filePath, signaturePath, legacyMode);
+            if (result.isOk()) {
+                if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath);
+                final FileDescriptor fd = result.getUnownedFileDescriptor();
+                try {
+                    mInstaller.installApkVerity(filePath, fd, result.getContentSize());
+
+                    // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN.
+                    if (legacyMode) {
+                        final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
+                        mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
+                    }
+                } finally {
+                    IoUtils.closeQuietly(fd);
+                }
+            } else if (result.isFailed()) {
+                throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
+                        "Failed to generate verity");
+            }
+        }
+    }
+
     private void startIntentFilterVerifications(int userId, boolean replacing,
             PackageParser.Package pkg) {
         if (mIntentFilterVerifierComponent == null) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 36948fc..6134d30 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -543,14 +543,31 @@
         }
     }
 
-    /** Returns true if APK Verity is enabled. */
+    /** Default is to not use fs-verity since it depends on kernel support. */
+    private static final int FSVERITY_DISABLED = 0;
+
+    /**
+     * Experimental implementation targeting priv apps, with Android specific kernel patches to
+     * extend fs-verity.
+     */
+    private static final int FSVERITY_LEGACY = 1;
+
+    /** Standard fs-verity. */
+    private static final int FSVERITY_ENABLED = 2;
+
+    /** Returns true if standard APK Verity is enabled. */
     static boolean isApkVerityEnabled() {
-        return SystemProperties.getInt("ro.apk_verity.mode", 0) != 0;
+        return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_ENABLED;
+    }
+
+    static boolean isLegacyApkVerityEnabled() {
+        return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY;
     }
 
     /** Returns true to force apk verification if the updated package (in /data) is a priv app. */
     static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
-        return disabledPs != null && disabledPs.isPrivileged() && isApkVerityEnabled();
+        return disabledPs != null && disabledPs.isPrivileged() && (
+                isApkVerityEnabled() || isLegacyApkVerityEnabled());
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index ac9c6ef..2e9d26a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2293,6 +2293,9 @@
                     break;
                 case "--apex":
                     sessionParams.installFlags |= PackageManager.INSTALL_APEX;
+                    // TODO(b/118865310): APEX packages should always imply
+                    //                    sessionParams.isStaged(). Enforce this when the staged
+                    //                    install workflow is complete.
                     break;
                 case "--multi-package":
                     sessionParams.setMultiPackage();
@@ -2588,7 +2591,7 @@
         try {
             session = new PackageInstaller.Session(
                     mInterface.getPackageInstaller().openSession(sessionId));
-            if (!session.isMultiPackage()) {
+            if (!session.isMultiPackage() && !session.isStaged()) {
                 // Sanity check that all .dm files match an apk.
                 // (The installer does not support standalone .dm files and will not process them.)
                 try {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 223f99e..3beda6a 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -19,9 +19,11 @@
 import android.annotation.NonNull;
 import android.content.pm.PackageInstaller;
 import android.content.pm.ParceledListSlice;
+import android.os.Handler;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -35,6 +37,7 @@
     private static final String TAG = "StagingManager";
 
     private final PackageManagerService mPm;
+    private final Handler mBgHandler;
 
     // STOPSHIP: This is a temporary mock implementation of staged sessions. This variable
     //           shouldn't be needed at all.
@@ -44,16 +47,17 @@
 
     StagingManager(PackageManagerService pm) {
         mPm = pm;
+        mBgHandler = BackgroundThread.getHandler();
     }
 
     private void updateStoredSession(@NonNull PackageInstallerSession sessionInfo) {
         synchronized (mStagedSessions) {
             PackageInstallerSession storedSession = mStagedSessions.get(sessionInfo.sessionId);
-            if (storedSession == null) {
-                throw new IllegalStateException("Attempting to change state of a session not "
-                        + "known to StagingManager");
+            // storedSession might be null if a call to abortSession was made before the session
+            // is updated.
+            if (storedSession != null) {
+                mStagedSessions.put(sessionInfo.sessionId, sessionInfo);
             }
-            mStagedSessions.put(sessionInfo.sessionId, sessionInfo);
         }
     }
 
@@ -69,10 +73,13 @@
 
     void commitSession(@NonNull PackageInstallerSession sessionInfo) {
         updateStoredSession(sessionInfo);
-        // TODO(b/118865310): Dispatch the session to apexd/PackageManager for verification. For
-        //                    now we directly mark it as ready.
-        sessionInfo.setStagedSessionReady();
-        mPm.sendSessionUpdatedBroadcast(sessionInfo.generateInfo(), sessionInfo.userId);
+
+        mBgHandler.post(() -> {
+            // TODO(b/118865310): Dispatch the session to apexd/PackageManager for verification. For
+            //                    now we directly mark it as ready.
+            sessionInfo.setStagedSessionReady();
+            mPm.sendSessionUpdatedBroadcast(sessionInfo.generateInfo(), sessionInfo.userId);
+        });
     }
 
     void createSession(@NonNull PackageInstallerSession sessionInfo) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c792863..2060aef 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -24,6 +24,7 @@
 import static android.content.Context.CONTEXT_RESTRICTED;
 import static android.content.Context.DISPLAY_SERVICE;
 import static android.content.Context.WINDOW_SERVICE;
+import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -126,6 +127,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
+import android.hardware.hdmi.HdmiAudioSystemClient;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
@@ -195,6 +197,7 @@
 import com.android.internal.accessibility.AccessibilityShortcutController;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.os.RoSystemProperties;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.policy.PhoneWindow;
@@ -373,6 +376,7 @@
     private ScreenshotHelper mScreenshotHelper;
     private boolean mHasFeatureWatch;
     private boolean mHasFeatureLeanback;
+    private boolean mHasFeatureHdmiCec;
 
     // Assigned on main thread, accessed on UI thread
     volatile VrManagerInternal mVrManagerInternal;
@@ -1453,7 +1457,7 @@
      */
     private HdmiControl getHdmiControl() {
         if (null == mHdmiControl) {
-            if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
+            if (!mHasFeatureHdmiCec) {
                 return null;
             }
             HdmiControlManager manager = (HdmiControlManager) mContext.getSystemService(
@@ -1689,6 +1693,7 @@
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
         mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK);
+        mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC);
         mAccessibilityShortcutController =
                 new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
         mLogger = new MetricsLogger();
@@ -4116,6 +4121,19 @@
     }
 
     private void dispatchDirectAudioEvent(KeyEvent event) {
+        // When System Audio Mode is off, volume keys received by AVR can be either consumed by AVR
+        // or forwarded to the TV. It's up to Amplifier manufacturer’s implementation.
+        HdmiControlManager hdmiControlManager = getHdmiControlManager();
+        if (null != hdmiControlManager
+                && !hdmiControlManager.getSystemAudioMode()
+                && shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff()) {
+            HdmiAudioSystemClient audioSystemClient = hdmiControlManager.getAudioSystemClient();
+            if (audioSystemClient != null) {
+                audioSystemClient.sendKeyEvent(
+                        event.getKeyCode(), event.getAction() == KeyEvent.ACTION_DOWN);
+                return;
+            }
+        }
         if (event.getAction() != KeyEvent.ACTION_DOWN) {
             return;
         }
@@ -4123,6 +4141,7 @@
         int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
                 | AudioManager.FLAG_FROM_KEY;
         String pkgName = mContext.getOpPackageName();
+
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
                 try {
@@ -4154,6 +4173,18 @@
         }
     }
 
+    @Nullable
+    private HdmiControlManager getHdmiControlManager() {
+        if (!mHasFeatureHdmiCec) {
+            return null;
+        }
+        return (HdmiControlManager) mContext.getSystemService(HdmiControlManager.class);
+    }
+
+    private boolean shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff() {
+        return RoSystemProperties.CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF;
+    }
+
     void dispatchMediaKeyWithWakeLock(KeyEvent event) {
         if (DEBUG_INPUT) {
             Slog.d(TAG, "dispatchMediaKeyWithWakeLock: " + event);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index f262f6d..a60f16d 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -15,8 +15,14 @@
  */
 package com.android.server.power.batterysaver;
 
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -26,6 +32,7 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
@@ -46,6 +53,8 @@
  */
 public class BatterySaverStateMachine {
     private static final String TAG = "BatterySaverStateMachine";
+    private static final String DYNAMIC_MODE_NOTIF_CHANNEL_ID = "dynamic_mode_notification";
+    private static final int DYNAMIC_MODE_NOTIFICATION_ID = 1992;
     private final Object mLock;
 
     private static final boolean DEBUG = BatterySaverPolicy.DEBUG;
@@ -484,6 +493,13 @@
         }
         mBatterySaverController.enableBatterySaver(enable, intReason);
 
+        // Handle triggering the notification to show/hide when appropriate
+        if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) {
+            runOnBgThread(this::triggerDynamicModeNotification);
+        } else if (!enable) {
+            runOnBgThread(this::hideDynamicModeNotification);
+        }
+
         if (DEBUG) {
             Slog.d(TAG, "Battery saver: Enabled=" + enable
                     + " manual=" + manual
@@ -491,6 +507,44 @@
         }
     }
 
+    private void triggerDynamicModeNotification() {
+        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+        ensureNotificationChannelExists(manager);
+
+        manager.notify(DYNAMIC_MODE_NOTIFICATION_ID, buildNotification());
+    }
+
+    private void ensureNotificationChannelExists(NotificationManager manager) {
+        NotificationChannel channel = new NotificationChannel(
+                DYNAMIC_MODE_NOTIF_CHANNEL_ID,
+                mContext.getText(
+                        R.string.dynamic_mode_notification_channel_name),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setSound(null, null);
+        manager.createNotificationChannel(channel);
+    }
+
+    private Notification buildNotification() {
+        Resources res = mContext.getResources();
+        Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        PendingIntent batterySaverIntent = PendingIntent.getActivity(
+                mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        return new Notification.Builder(mContext, DYNAMIC_MODE_NOTIF_CHANNEL_ID)
+                .setSmallIcon(R.drawable.ic_battery)
+                .setContentTitle(res.getString(R.string.dynamic_mode_notification_title))
+                .setContentText(res.getString(R.string.dynamic_mode_notification_summary))
+                .setContentIntent(batterySaverIntent)
+                .setOnlyAlertOnce(true)
+                .build();
+    }
+
+    private void hideDynamicModeNotification() {
+        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+        manager.cancel(DYNAMIC_MODE_NOTIFICATION_ID);
+    }
+
     @GuardedBy("mLock")
     private void updateSnoozingLocked(boolean snoozing, String reason) {
         if (mBatterySaverSnoozing == snoozing) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerService.java b/services/core/java/com/android/server/rollback/RollbackManagerService.java
index 5bf2040..4b5e764 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerService.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerService.java
@@ -16,831 +16,27 @@
 
 package com.android.server.rollback;
 
-import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.StringParceledListSlice;
-import android.content.rollback.IRollbackManager;
-import android.content.rollback.PackageRollbackInfo;
-import android.content.rollback.RollbackInfo;
-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.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.SELinux;
-import android.util.Log;
 
-import com.android.internal.annotations.GuardedBy;
-import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.pm.PackageManagerServiceUtils;
-
-import libcore.io.IoUtils;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.time.Instant;
-import java.time.format.DateTimeParseException;
-import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
 
 /**
- * Service that manages APK level rollbacks.
- *
- * TODO: Make RollbackManagerService extend SystemService and move the
- * IRollbackManager.Stub implementation to a new private
- * RollbackManagerServiceImpl class.
+ * Service that manages APK level rollbacks. Publishes
+ * Context.ROLLBACK_SERVICE.
  *
  * @hide
  */
-public class RollbackManagerService extends IRollbackManager.Stub {
+public final class RollbackManagerService extends SystemService {
 
-    private static final String TAG = "RollbackManager";
+    private RollbackManagerServiceImpl mService;
 
-    // Rollbacks expire after 48 hours.
-    // TODO: How to test rollback expiration works properly?
-    private static final long ROLLBACK_LIFETIME_DURATION_MILLIS = 48 * 60 * 60 * 1000;
-
-    // Lock used to synchronize accesses to in-memory rollback data
-    // structures. By convention, methods with the suffix "Locked" require
-    // mLock is held when they are called.
-    private final Object mLock = new Object();
-
-    // Package rollback data available to be used for rolling back a package.
-    // This list is null until the rollback data has been loaded.
-    @GuardedBy("mLock")
-    private List<PackageRollbackData> mAvailableRollbacks;
-
-    // The list of recently executed rollbacks.
-    // This list is null until the rollback data has been loaded.
-    @GuardedBy("mLock")
-    private List<RollbackInfo> mRecentlyExecutedRollbacks;
-
-    // Data for available rollbacks and recently executed rollbacks is
-    // persisted in storage. Assuming the rollback data directory is
-    // /data/rollback, we use the following directory structure
-    // to store this data:
-    //   /data/rollback/
-    //      available/
-    //          com.package.A-XXX/
-    //              base.apk
-    //              rollback.json
-    //          com.package.B-YYY/
-    //              base.apk
-    //              rollback.json
-    //      recently_executed.json
-    // TODO: Use AtomicFile for rollback.json and recently_executed.json.
-    private final File mRollbackDataDir;
-    private final File mAvailableRollbacksDir;
-    private final File mRecentlyExecutedRollbacksFile;
-
-    private final Context mContext;
-    private final HandlerThread mHandlerThread;
-
-    RollbackManagerService(Context context) {
-        mContext = context;
-        mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
-        mHandlerThread.start();
-
-        mRollbackDataDir = new File(Environment.getDataDirectory(), "rollback");
-        mAvailableRollbacksDir = new File(mRollbackDataDir, "available");
-        mRecentlyExecutedRollbacksFile = new File(mRollbackDataDir, "recently_executed.json");
-
-        // Kick off loading of the rollback data from strorage in a background
-        // thread.
-        // TODO: Consider loading the rollback data directly here instead, to
-        // avoid the need to call ensureRollbackDataLoaded every time before
-        // accessing the rollback data?
-        // TODO: Test that this kicks off initial scheduling of rollback
-        // expiration.
-        getHandler().post(() -> ensureRollbackDataLoaded());
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
-        filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
-        filter.addDataScheme("package");
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
-                    String packageName = intent.getData().getSchemeSpecificPart();
-                    onPackageReplaced(packageName);
-                }
-                if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
-                    String packageName = intent.getData().getSchemeSpecificPart();
-                    onPackageFullyRemoved(packageName);
-                }
-            }
-        }, filter, null, getHandler());
-
-        IntentFilter enableRollbackFilter = new IntentFilter();
-        enableRollbackFilter.addAction(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
-        try {
-            enableRollbackFilter.addDataType("application/vnd.android.package-archive");
-        } catch (IntentFilter.MalformedMimeTypeException e) {
-            Log.e(TAG, "addDataType", e);
-        }
-
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (Intent.ACTION_PACKAGE_ENABLE_ROLLBACK.equals(intent.getAction())) {
-                    int token = intent.getIntExtra(
-                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
-                    int installFlags = intent.getIntExtra(
-                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
-                    File newPackageCodePath = new File(intent.getData().getPath());
-
-                    getHandler().post(() -> {
-                        boolean success = enableRollback(installFlags, newPackageCodePath);
-                        int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
-                        if (!success) {
-                            ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
-                        }
-
-                        PackageManagerInternal pm = LocalServices.getService(
-                                PackageManagerInternal.class);
-                        pm.setEnableRollbackCode(token, ret);
-                    });
-
-                    // We're handling the ordered broadcast. Abort the
-                    // broadcast because there is no need for it to go to
-                    // anyone else.
-                    abortBroadcast();
-                }
-            }
-        }, enableRollbackFilter, null, getHandler());
+    public RollbackManagerService(Context context) {
+        super(context);
     }
 
     @Override
-    public RollbackInfo getAvailableRollback(String packageName) {
-        PackageRollbackInfo.PackageVersion installedVersion =
-                getInstalledPackageVersion(packageName);
-        if (installedVersion == null) {
-            return null;
-        }
-
-        synchronized (mLock) {
-            // TODO: Have ensureRollbackDataLoadedLocked return the list of
-            // available rollbacks, to hopefully avoid forgetting to call it?
-            ensureRollbackDataLoadedLocked();
-            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
-                PackageRollbackData data = mAvailableRollbacks.get(i);
-                if (data.info.higherVersion.equals(installedVersion)) {
-                    // TODO: For atomic installs, check all dependent packages
-                    // for available rollbacks and include that info here.
-                    return new RollbackInfo(data.info);
-                }
-            }
-        }
-
-        return null;
-    }
-
-    @Override
-    public StringParceledListSlice getPackagesWithAvailableRollbacks() {
-        // TODO: This may return packages whose rollback is out of date or
-        // expired.  Presumably that's okay because the package rollback could
-        // be expired anyway between when the caller calls this method and
-        // when the caller calls getAvailableRollback for more details.
-        final Set<String> packageNames = new HashSet<>();
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
-                PackageRollbackData data = mAvailableRollbacks.get(i);
-                packageNames.add(data.info.packageName);
-            }
-        }
-        return new StringParceledListSlice(new ArrayList<>(packageNames));
-    }
-
-    @Override
-    public ParceledListSlice<RollbackInfo> getRecentlyExecutedRollbacks() {
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            List<RollbackInfo> rollbacks = new ArrayList<>(mRecentlyExecutedRollbacks);
-            return new ParceledListSlice<>(rollbacks);
-        }
-    }
-
-    @Override
-    public void executeRollback(RollbackInfo rollback, String callerPackageName,
-            IntentSender statusReceiver) {
-        final int callingUid = Binder.getCallingUid();
-        if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
-            AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
-            appOps.checkPackage(callingUid, callerPackageName);
-        }
-
-        getHandler().post(() ->
-                executeRollbackInternal(rollback, callerPackageName, statusReceiver));
-    }
-
-    /**
-     * Performs the actual work to execute a rollback.
-     * The work is done on the current thread. This may be a long running
-     * operation.
-     */
-    private void executeRollbackInternal(RollbackInfo rollback,
-            String callerPackageName, IntentSender statusReceiver) {
-        String packageName = rollback.targetPackage.packageName;
-        Log.i(TAG, "Initiating rollback of " + packageName);
-
-        PackageRollbackInfo.PackageVersion installedVersion =
-                getInstalledPackageVersion(packageName);
-        if (installedVersion == null) {
-            // TODO: Test this case
-            sendFailure(statusReceiver, "Target package to roll back is not installed");
-            return;
-        }
-
-        if (!rollback.targetPackage.higherVersion.equals(installedVersion)) {
-            // TODO: Test this case
-            sendFailure(statusReceiver, "Target package version to roll back not installed.");
-            return;
-        }
-
-        // TODO: We assume that between now and the time we commit the
-        // downgrade install, the currently installed package version does not
-        // change. This is not safe to assume, particularly in the case of a
-        // rollback racing with a roll-forward fix of a buggy package.
-        // Figure out how to ensure we don't commit the rollback if
-        // roll forward happens at the same time.
-        PackageRollbackData data = null;
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
-                PackageRollbackData available = mAvailableRollbacks.get(i);
-                // TODO: Check if available.info.lowerVersion matches
-                // rollback.targetPackage.lowerVersion?
-                if (available.info.higherVersion.equals(installedVersion)) {
-                    data = available;
-                    break;
-                }
-            }
-        }
-
-        if (data == null) {
-            sendFailure(statusReceiver, "Rollback not available");
-            return;
-        }
-
-        // Get a context for the caller to use to install the downgraded
-        // version of the package.
-        Context context = null;
-        try {
-            context = mContext.createPackageContext(callerPackageName, 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            sendFailure(statusReceiver, "Invalid callerPackageName");
-            return;
-        }
-
-        PackageManager pm = context.getPackageManager();
-        try {
-            PackageInstaller.Session session = null;
-
-            PackageInstaller packageInstaller = pm.getPackageInstaller();
-            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
-                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-            params.setAllowDowngrade(true);
-            int sessionId = packageInstaller.createSession(params);
-            session = packageInstaller.openSession(sessionId);
-
-            // TODO: Will it always be called "base.apk"? What about splits?
-            File baseApk = new File(data.backupDir, "base.apk");
-            try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
-                    ParcelFileDescriptor.MODE_READ_ONLY)) {
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    session.write("base.apk", 0, baseApk.length(), fd);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
-            }
-
-            final LocalIntentReceiver receiver = new LocalIntentReceiver();
-            session.commit(receiver.getIntentSender());
-
-            Intent result = receiver.getResult();
-            int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                    PackageInstaller.STATUS_FAILURE);
-            if (status != PackageInstaller.STATUS_SUCCESS) {
-                sendFailure(statusReceiver, "Rollback downgrade install failed: "
-                        + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
-                return;
-            }
-
-            addRecentlyExecutedRollback(rollback);
-            sendSuccess(statusReceiver);
-
-            // TODO: Restrict permissions for who can listen for this
-            // broadcast?
-            Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
-                    Uri.fromParts("package", packageName, null));
-
-            // TODO: This call emits the warning "Calling a method in the
-            // system process without a qualified user". Fix that.
-            mContext.sendBroadcast(broadcast);
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to roll back " + packageName, e);
-            sendFailure(statusReceiver, "IOException: " + e.toString());
-            return;
-        }
-    }
-
-    @Override
-    public void reloadPersistedData() {
-        synchronized (mLock) {
-            mAvailableRollbacks = null;
-            mRecentlyExecutedRollbacks = null;
-        }
-        getHandler().post(() -> ensureRollbackDataLoaded());
-    }
-
-    @Override
-    public void expireRollbackForPackage(String packageName) {
-        // TODO: Should this take a package version number in addition to
-        // package name? For now, just remove all rollbacks matching the
-        // package name. This method is only currently used to facilitate
-        // testing anyway.
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
-            while (iter.hasNext()) {
-                PackageRollbackData data = iter.next();
-                if (data.info.packageName.equals(packageName)) {
-                    iter.remove();
-                    removeFile(data.backupDir);
-                }
-            }
-        }
-    }
-
-    /**
-     * Load rollback data from storage if it has not already been loaded.
-     * After calling this funciton, mAvailableRollbacks and
-     * mRecentlyExecutedRollbacks will be non-null.
-     */
-    private void ensureRollbackDataLoaded() {
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-        }
-    }
-
-    /**
-     * Load rollback data from storage if it has not already been loaded.
-     * After calling this function, mAvailableRollbacks and
-     * mRecentlyExecutedRollbacks will be non-null.
-     */
-    @GuardedBy("mLock")
-    private void ensureRollbackDataLoadedLocked() {
-        if (mAvailableRollbacks == null) {
-            loadRollbackDataLocked();
-        }
-    }
-
-    /**
-     * Load rollback data from storage.
-     * Note: We do potentially heavy IO here while holding mLock, because we
-     * have to have the rollback data loaded before we can do anything else
-     * meaningful.
-     */
-    @GuardedBy("mLock")
-    private void loadRollbackDataLocked() {
-        mAvailableRollbacksDir.mkdirs();
-        mAvailableRollbacks = new ArrayList<>();
-        for (File rollbackDir : mAvailableRollbacksDir.listFiles()) {
-            if (rollbackDir.isDirectory()) {
-                // TODO: How to detect and clean up an invalid rollback
-                // directory? We don't know if it's invalid because something
-                // went wrong, or if it's only temporarily invalid because
-                // it's in the process of being created.
-                try {
-                    File jsonFile = new File(rollbackDir, "rollback.json");
-                    String jsonString = IoUtils.readFileAsString(jsonFile.getAbsolutePath());
-                    JSONObject jsonObject = new JSONObject(jsonString);
-                    String packageName = jsonObject.getString("packageName");
-                    long higherVersionCode = jsonObject.getLong("higherVersionCode");
-                    long lowerVersionCode = jsonObject.getLong("lowerVersionCode");
-                    Instant timestamp = Instant.parse(jsonObject.getString("timestamp"));
-                    PackageRollbackData data = new PackageRollbackData(
-                            new PackageRollbackInfo(packageName,
-                                new PackageRollbackInfo.PackageVersion(higherVersionCode),
-                                new PackageRollbackInfo.PackageVersion(lowerVersionCode)),
-                            rollbackDir, timestamp);
-                    mAvailableRollbacks.add(data);
-                } catch (IOException | JSONException | DateTimeParseException e) {
-                    Log.e(TAG, "Unable to read rollback data at " + rollbackDir, e);
-                }
-            }
-        }
-
-        mRecentlyExecutedRollbacks = new ArrayList<>();
-        if (mRecentlyExecutedRollbacksFile.exists()) {
-            try {
-                // TODO: How to cope with changes to the format of this file from
-                // when RollbackStore is updated in the future?
-                String jsonString = IoUtils.readFileAsString(
-                        mRecentlyExecutedRollbacksFile.getAbsolutePath());
-                JSONObject object = new JSONObject(jsonString);
-                JSONArray array = object.getJSONArray("recentlyExecuted");
-                for (int i = 0; i < array.length(); ++i) {
-                    JSONObject element = array.getJSONObject(i);
-                    String packageName = element.getString("packageName");
-                    long higherVersionCode = element.getLong("higherVersionCode");
-                    long lowerVersionCode = element.getLong("lowerVersionCode");
-                    PackageRollbackInfo target = new PackageRollbackInfo(packageName,
-                            new PackageRollbackInfo.PackageVersion(higherVersionCode),
-                            new PackageRollbackInfo.PackageVersion(lowerVersionCode));
-                    RollbackInfo rollback = new RollbackInfo(target);
-                    mRecentlyExecutedRollbacks.add(rollback);
-                }
-            } catch (IOException | JSONException e) {
-                // TODO: What to do here? Surely we shouldn't just forget about
-                // everything after the point of exception?
-                Log.e(TAG, "Failed to read recently executed rollbacks", e);
-            }
-        }
-
-        scheduleExpiration(0);
-    }
-
-    /**
-     * Called when a package has been replaced with a different version.
-     * Removes all backups for the package not matching the currently
-     * installed package version.
-     */
-    private void onPackageReplaced(String packageName) {
-        // TODO: Could this end up incorrectly deleting a rollback for a
-        // package that is about to be installed?
-        PackageRollbackInfo.PackageVersion installedVersion =
-                getInstalledPackageVersion(packageName);
-
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
-            while (iter.hasNext()) {
-                PackageRollbackData data = iter.next();
-                if (data.info.packageName.equals(packageName)
-                        && !data.info.higherVersion.equals(installedVersion)) {
-                    iter.remove();
-                    removeFile(data.backupDir);
-                }
-            }
-        }
-    }
-
-    /**
-     * Called when a package has been completely removed from the device.
-     * Removes all backups and rollback history for the given package.
-     */
-    private void onPackageFullyRemoved(String packageName) {
-        expireRollbackForPackage(packageName);
-
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            Iterator<RollbackInfo> iter = mRecentlyExecutedRollbacks.iterator();
-            boolean changed = false;
-            while (iter.hasNext()) {
-                RollbackInfo rollback = iter.next();
-                if (packageName.equals(rollback.targetPackage.packageName)) {
-                    iter.remove();
-                    changed = true;
-                }
-            }
-
-            if (changed) {
-                saveRecentlyExecutedRollbacksLocked();
-            }
-        }
-    }
-
-    /**
-     * Write the list of recently executed rollbacks to storage.
-     * Note: This happens while mLock is held, which should be okay because we
-     * expect executed rollbacks to be modified only in exceptional cases.
-     */
-    @GuardedBy("mLock")
-    private void saveRecentlyExecutedRollbacksLocked() {
-        try {
-            JSONObject json = new JSONObject();
-            JSONArray array = new JSONArray();
-            json.put("recentlyExecuted", array);
-
-            for (int i = 0; i < mRecentlyExecutedRollbacks.size(); ++i) {
-                RollbackInfo rollback = mRecentlyExecutedRollbacks.get(i);
-                JSONObject element = new JSONObject();
-                element.put("packageName", rollback.targetPackage.packageName);
-                element.put("higherVersionCode", rollback.targetPackage.higherVersion.versionCode);
-                element.put("lowerVersionCode", rollback.targetPackage.lowerVersion.versionCode);
-                array.put(element);
-            }
-
-            PrintWriter pw = new PrintWriter(mRecentlyExecutedRollbacksFile);
-            pw.println(json.toString());
-            pw.close();
-        } catch (IOException | JSONException e) {
-            // TODO: What to do here?
-            Log.e(TAG, "Failed to save recently executed rollbacks", e);
-        }
-    }
-
-    /**
-     * Records that the given package has been recently rolled back.
-     */
-    private void addRecentlyExecutedRollback(RollbackInfo rollback) {
-        // TODO: if the list of rollbacks gets too big, trim it to only those
-        // that are necessary to keep track of.
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            mRecentlyExecutedRollbacks.add(rollback);
-            saveRecentlyExecutedRollbacksLocked();
-        }
-    }
-
-    /**
-     * Notifies an IntentSender of failure.
-     *
-     * @param statusReceiver where to send the failure
-     * @param message the failure message.
-     */
-    private void sendFailure(IntentSender statusReceiver, String message) {
-        Log.e(TAG, message);
-        try {
-            // TODO: More context on which rollback failed?
-            // TODO: More refined failure code?
-            final Intent fillIn = new Intent();
-            fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
-            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, message);
-            statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
-        } catch (IntentSender.SendIntentException e) {
-            // Nowhere to send the result back to, so don't bother.
-        }
-    }
-
-    /**
-     * Notifies an IntentSender of success.
-     */
-    private void sendSuccess(IntentSender statusReceiver) {
-        try {
-            final Intent fillIn = new Intent();
-            fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS);
-            statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
-        } catch (IntentSender.SendIntentException e) {
-            // Nowhere to send the result back to, so don't bother.
-        }
-    }
-
-    // Check to see if anything needs expiration, and if so, expire it.
-    // Schedules future expiration as appropriate.
-    // TODO: Handle cases where the user changes time on the device.
-    private void runExpiration() {
-        Instant now = Instant.now();
-        Instant oldest = null;
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-
-            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
-            while (iter.hasNext()) {
-                PackageRollbackData data = iter.next();
-                if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
-                    iter.remove();
-                    removeFile(data.backupDir);
-                } else if (oldest == null || oldest.isAfter(data.timestamp)) {
-                    oldest = data.timestamp;
-                }
-            }
-        }
-
-        if (oldest != null) {
-            scheduleExpiration(now.until(oldest.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS),
-                        ChronoUnit.MILLIS));
-        }
-    }
-
-    /**
-     * Schedules an expiration check to be run after the given duration in
-     * milliseconds has gone by.
-     */
-    private void scheduleExpiration(long duration) {
-        getHandler().postDelayed(() -> runExpiration(), duration);
-    }
-
-    /**
-     * Gets the RollbackManagerService's handler.
-     * To allow PackageManagerService to call into RollbackManagerService
-     * without fear of blocking the PackageManagerService thread.
-     */
-    public Handler getHandler() {
-        return mHandlerThread.getThreadHandler();
-    }
-
-    /**
-     * Called via broadcast by the package manager when a package is being
-     * staged for install with rollback enabled. Called before the package has
-     * been installed.
-     *
-     * @param id the id of the enable rollback request
-     * @param installFlags information about what is being installed.
-     * @param newPackageCodePath path to the package about to be installed.
-     * @return true if enabling the rollback succeeds, false otherwise.
-     */
-    private boolean enableRollback(int installFlags, File newPackageCodePath) {
-        if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
-            Log.e(TAG, "Rollbacks not supported for instant app install");
-            return false;
-        }
-        if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
-            Log.e(TAG, "Rollbacks not supported for apex install");
-            return false;
-        }
-
-        // Get information about the package to be installed.
-        PackageParser.PackageLite newPackage = null;
-        try {
-            newPackage = PackageParser.parsePackageLite(newPackageCodePath, 0);
-        } catch (PackageParser.PackageParserException e) {
-            Log.e(TAG, "Unable to parse new package", e);
-            return false;
-        }
-
-        String packageName = newPackage.packageName;
-        Log.i(TAG, "Enabling rollback for install of " + packageName);
-
-        PackageRollbackInfo.PackageVersion newVersion =
-                new PackageRollbackInfo.PackageVersion(newPackage.versionCode);
-
-        // Get information about the currently installed package.
-        PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
-        PackageParser.Package installedPackage = pm.getPackage(packageName);
-        if (installedPackage == null) {
-            // TODO: Support rolling back fresh package installs rather than
-            // fail here. Test this case.
-            Log.e(TAG, packageName + " is not installed");
-            return false;
-        }
-        PackageRollbackInfo.PackageVersion installedVersion =
-                new PackageRollbackInfo.PackageVersion(installedPackage.getLongVersionCode());
-
-        File backupDir;
-        try {
-            backupDir = Files.createTempDirectory(
-                mAvailableRollbacksDir.toPath(), packageName + "-").toFile();
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to create rollback for " + packageName, e);
-            return false;
-        }
-
-        // TODO: Should the timestamp be for when we commit the install, not
-        // when we create the pending one?
-        Instant timestamp = Instant.now();
-        try {
-            JSONObject json = new JSONObject();
-            json.put("packageName", packageName);
-            json.put("higherVersionCode", newVersion.versionCode);
-            json.put("lowerVersionCode", installedVersion.versionCode);
-            json.put("timestamp", timestamp.toString());
-
-            File jsonFile = new File(backupDir, "rollback.json");
-            PrintWriter pw = new PrintWriter(jsonFile);
-            pw.println(json.toString());
-            pw.close();
-        } catch (IOException | JSONException e) {
-            Log.e(TAG, "Unable to create rollback for " + packageName, e);
-            removeFile(backupDir);
-            return false;
-        }
-
-        // TODO: Copy by hard link instead to save on cpu and storage space?
-        int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, backupDir);
-        if (status != PackageManager.INSTALL_SUCCEEDED) {
-            Log.e(TAG, "Unable to copy package for rollback for " + packageName);
-            removeFile(backupDir);
-            return false;
-        }
-
-        PackageRollbackData data = new PackageRollbackData(
-                new PackageRollbackInfo(packageName, newVersion, installedVersion),
-                backupDir, timestamp);
-
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-            mAvailableRollbacks.add(data);
-        }
-
-        return true;
-    }
-
-    // TODO: Don't copy this from PackageManagerShellCommand like this?
-    private static class LocalIntentReceiver {
-        private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
-
-        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
-            @Override
-            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
-                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
-                try {
-                    mResult.offer(intent, 5, TimeUnit.SECONDS);
-                } catch (InterruptedException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        };
-
-        public IntentSender getIntentSender() {
-            return new IntentSender((IIntentSender) mLocalSender);
-        }
-
-        public Intent getResult() {
-            try {
-                return mResult.take();
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    /**
-     * Deletes a file completely.
-     * If the file is a directory, its contents are deleted as well.
-     * Has no effect if the directory does not exist.
-     */
-    private void removeFile(File file) {
-        if (file.isDirectory()) {
-            for (File child : file.listFiles()) {
-                removeFile(child);
-            }
-        }
-        if (file.exists()) {
-            file.delete();
-        }
-    }
-
-    /**
-     * Gets the version of the package currently installed.
-     * Returns null if the package is not currently installed.
-     */
-    private PackageRollbackInfo.PackageVersion getInstalledPackageVersion(String packageName) {
-        PackageManager pm = mContext.getPackageManager();
-        PackageInfo pkgInfo = null;
-        try {
-            pkgInfo = pm.getPackageInfo(packageName, 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            return null;
-        }
-
-        return new PackageRollbackInfo.PackageVersion(pkgInfo.getLongVersionCode());
-    }
-
-    /**
-     * Manages the lifecycle of RollbackManagerService within System Server.
-     */
-    public static class Lifecycle extends SystemService {
-        private RollbackManagerService mService;
-
-        public Lifecycle(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void onStart() {
-            mService = new RollbackManagerService(getContext());
-
-            // TODO: Set up sepolicy to allow publishing the service.
-            if (SELinux.isSELinuxEnforced()) {
-                Log.w(TAG, "RollbackManager disabled pending selinux policy updates");
-            } else {
-                publishBinderService(Context.ROLLBACK_SERVICE, mService);
-            }
-        }
+    public void onStart() {
+        mService = new RollbackManagerServiceImpl(getContext());
+        publishBinderService(Context.ROLLBACK_SERVICE, mService);
     }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
new file mode 100644
index 0000000..0c21312
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -0,0 +1,832 @@
+/*
+ * 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.rollback;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.StringParceledListSlice;
+import android.content.rollback.IRollbackManager;
+import android.content.rollback.PackageRollbackInfo;
+import android.content.rollback.RollbackInfo;
+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.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.pm.PackageManagerServiceUtils;
+
+import libcore.io.IoUtils;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.time.Instant;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Implementation of service that manages APK level rollbacks.
+ */
+class RollbackManagerServiceImpl extends IRollbackManager.Stub {
+
+    private static final String TAG = "RollbackManager";
+
+    // Rollbacks expire after 48 hours.
+    // TODO: How to test rollback expiration works properly?
+    private static final long ROLLBACK_LIFETIME_DURATION_MILLIS = 48 * 60 * 60 * 1000;
+
+    // Lock used to synchronize accesses to in-memory rollback data
+    // structures. By convention, methods with the suffix "Locked" require
+    // mLock is held when they are called.
+    private final Object mLock = new Object();
+
+    // Package rollback data available to be used for rolling back a package.
+    // This list is null until the rollback data has been loaded.
+    @GuardedBy("mLock")
+    private List<PackageRollbackData> mAvailableRollbacks;
+
+    // The list of recently executed rollbacks.
+    // This list is null until the rollback data has been loaded.
+    @GuardedBy("mLock")
+    private List<RollbackInfo> mRecentlyExecutedRollbacks;
+
+    // Data for available rollbacks and recently executed rollbacks is
+    // persisted in storage. Assuming the rollback data directory is
+    // /data/rollback, we use the following directory structure
+    // to store this data:
+    //   /data/rollback/
+    //      available/
+    //          com.package.A-XXX/
+    //              base.apk
+    //              rollback.json
+    //          com.package.B-YYY/
+    //              base.apk
+    //              rollback.json
+    //      recently_executed.json
+    // TODO: Use AtomicFile for rollback.json and recently_executed.json.
+    private final File mRollbackDataDir;
+    private final File mAvailableRollbacksDir;
+    private final File mRecentlyExecutedRollbacksFile;
+
+    private final Context mContext;
+    private final HandlerThread mHandlerThread;
+
+    RollbackManagerServiceImpl(Context context) {
+        mContext = context;
+        mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
+        mHandlerThread.start();
+
+        mRollbackDataDir = new File(Environment.getDataDirectory(), "rollback");
+        mAvailableRollbacksDir = new File(mRollbackDataDir, "available");
+        mRecentlyExecutedRollbacksFile = new File(mRollbackDataDir, "recently_executed.json");
+
+        // Kick off loading of the rollback data from strorage in a background
+        // thread.
+        // TODO: Consider loading the rollback data directly here instead, to
+        // avoid the need to call ensureRollbackDataLoaded every time before
+        // accessing the rollback data?
+        // TODO: Test that this kicks off initial scheduling of rollback
+        // expiration.
+        getHandler().post(() -> ensureRollbackDataLoaded());
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+                    String packageName = intent.getData().getSchemeSpecificPart();
+                    onPackageReplaced(packageName);
+                }
+                if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
+                    String packageName = intent.getData().getSchemeSpecificPart();
+                    onPackageFullyRemoved(packageName);
+                }
+            }
+        }, filter, null, getHandler());
+
+        IntentFilter enableRollbackFilter = new IntentFilter();
+        enableRollbackFilter.addAction(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
+        try {
+            enableRollbackFilter.addDataType("application/vnd.android.package-archive");
+        } catch (IntentFilter.MalformedMimeTypeException e) {
+            Log.e(TAG, "addDataType", e);
+        }
+
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (Intent.ACTION_PACKAGE_ENABLE_ROLLBACK.equals(intent.getAction())) {
+                    int token = intent.getIntExtra(
+                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
+                    int installFlags = intent.getIntExtra(
+                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
+                    File newPackageCodePath = new File(intent.getData().getPath());
+
+                    getHandler().post(() -> {
+                        boolean success = enableRollback(installFlags, newPackageCodePath);
+                        int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
+                        if (!success) {
+                            ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
+                        }
+
+                        PackageManagerInternal pm = LocalServices.getService(
+                                PackageManagerInternal.class);
+                        pm.setEnableRollbackCode(token, ret);
+                    });
+
+                    // We're handling the ordered broadcast. Abort the
+                    // broadcast because there is no need for it to go to
+                    // anyone else.
+                    abortBroadcast();
+                }
+            }
+        }, enableRollbackFilter, null, getHandler());
+    }
+
+    @Override
+    public RollbackInfo getAvailableRollback(String packageName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "getAvailableRollback");
+
+        PackageRollbackInfo.PackageVersion installedVersion =
+                getInstalledPackageVersion(packageName);
+        if (installedVersion == null) {
+            return null;
+        }
+
+        synchronized (mLock) {
+            // TODO: Have ensureRollbackDataLoadedLocked return the list of
+            // available rollbacks, to hopefully avoid forgetting to call it?
+            ensureRollbackDataLoadedLocked();
+            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
+                PackageRollbackData data = mAvailableRollbacks.get(i);
+                if (data.info.packageName.equals(packageName)
+                        && data.info.higherVersion.equals(installedVersion)) {
+                    // TODO: For atomic installs, check all dependent packages
+                    // for available rollbacks and include that info here.
+                    return new RollbackInfo(data.info);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public StringParceledListSlice getPackagesWithAvailableRollbacks() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "getPackagesWithAvailableRollbacks");
+
+        // TODO: This may return packages whose rollback is out of date or
+        // expired.  Presumably that's okay because the package rollback could
+        // be expired anyway between when the caller calls this method and
+        // when the caller calls getAvailableRollback for more details.
+        final Set<String> packageNames = new HashSet<>();
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
+                PackageRollbackData data = mAvailableRollbacks.get(i);
+                packageNames.add(data.info.packageName);
+            }
+        }
+        return new StringParceledListSlice(new ArrayList<>(packageNames));
+    }
+
+    @Override
+    public ParceledListSlice<RollbackInfo> getRecentlyExecutedRollbacks() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "getRecentlyExecutedRollbacks");
+
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            List<RollbackInfo> rollbacks = new ArrayList<>(mRecentlyExecutedRollbacks);
+            return new ParceledListSlice<>(rollbacks);
+        }
+    }
+
+    @Override
+    public void executeRollback(RollbackInfo rollback, String callerPackageName,
+            IntentSender statusReceiver) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "executeRollback");
+
+        final int callingUid = Binder.getCallingUid();
+        AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+        appOps.checkPackage(callingUid, callerPackageName);
+
+        getHandler().post(() ->
+                executeRollbackInternal(rollback, callerPackageName, statusReceiver));
+    }
+
+    /**
+     * Performs the actual work to execute a rollback.
+     * The work is done on the current thread. This may be a long running
+     * operation.
+     */
+    private void executeRollbackInternal(RollbackInfo rollback,
+            String callerPackageName, IntentSender statusReceiver) {
+        String packageName = rollback.targetPackage.packageName;
+        Log.i(TAG, "Initiating rollback of " + packageName);
+
+        PackageRollbackInfo.PackageVersion installedVersion =
+                getInstalledPackageVersion(packageName);
+        if (installedVersion == null) {
+            // TODO: Test this case
+            sendFailure(statusReceiver, "Target package to roll back is not installed");
+            return;
+        }
+
+        if (!rollback.targetPackage.higherVersion.equals(installedVersion)) {
+            // TODO: Test this case
+            sendFailure(statusReceiver, "Target package version to roll back not installed.");
+            return;
+        }
+
+        // TODO: We assume that between now and the time we commit the
+        // downgrade install, the currently installed package version does not
+        // change. This is not safe to assume, particularly in the case of a
+        // rollback racing with a roll-forward fix of a buggy package.
+        // Figure out how to ensure we don't commit the rollback if
+        // roll forward happens at the same time.
+        PackageRollbackData data = null;
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            for (int i = 0; i < mAvailableRollbacks.size(); ++i) {
+                PackageRollbackData available = mAvailableRollbacks.get(i);
+                // TODO: Check if available.info.lowerVersion matches
+                // rollback.targetPackage.lowerVersion?
+                if (available.info.packageName.equals(packageName)
+                        && available.info.higherVersion.equals(installedVersion)) {
+                    data = available;
+                    break;
+                }
+            }
+        }
+
+        if (data == null) {
+            sendFailure(statusReceiver, "Rollback not available");
+            return;
+        }
+
+        // Get a context for the caller to use to install the downgraded
+        // version of the package.
+        Context context = null;
+        try {
+            context = mContext.createPackageContext(callerPackageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            sendFailure(statusReceiver, "Invalid callerPackageName");
+            return;
+        }
+
+        PackageManager pm = context.getPackageManager();
+        try {
+            PackageInstaller.Session session = null;
+
+            PackageInstaller packageInstaller = pm.getPackageInstaller();
+            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+            params.setAllowDowngrade(true);
+            int sessionId = packageInstaller.createSession(params);
+            session = packageInstaller.openSession(sessionId);
+
+            // TODO: Will it always be called "base.apk"? What about splits?
+            File baseApk = new File(data.backupDir, "base.apk");
+            try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(baseApk,
+                    ParcelFileDescriptor.MODE_READ_ONLY)) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    session.write("base.apk", 0, baseApk.length(), fd);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+
+            final LocalIntentReceiver receiver = new LocalIntentReceiver();
+            session.commit(receiver.getIntentSender());
+
+            Intent result = receiver.getResult();
+            int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                    PackageInstaller.STATUS_FAILURE);
+            if (status != PackageInstaller.STATUS_SUCCESS) {
+                sendFailure(statusReceiver, "Rollback downgrade install failed: "
+                        + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
+                return;
+            }
+
+            addRecentlyExecutedRollback(rollback);
+            sendSuccess(statusReceiver);
+
+            Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
+                    Uri.fromParts("package", packageName, Manifest.permission.MANAGE_ROLLBACKS));
+
+            // TODO: This call emits the warning "Calling a method in the
+            // system process without a qualified user". Fix that.
+            mContext.sendBroadcast(broadcast);
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to roll back " + packageName, e);
+            sendFailure(statusReceiver, "IOException: " + e.toString());
+            return;
+        }
+    }
+
+    @Override
+    public void reloadPersistedData() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "reloadPersistedData");
+
+        synchronized (mLock) {
+            mAvailableRollbacks = null;
+            mRecentlyExecutedRollbacks = null;
+        }
+        getHandler().post(() -> ensureRollbackDataLoaded());
+    }
+
+    @Override
+    public void expireRollbackForPackage(String packageName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_ROLLBACKS,
+                "expireRollbackForPackage");
+
+        // TODO: Should this take a package version number in addition to
+        // package name? For now, just remove all rollbacks matching the
+        // package name. This method is only currently used to facilitate
+        // testing anyway.
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            while (iter.hasNext()) {
+                PackageRollbackData data = iter.next();
+                if (data.info.packageName.equals(packageName)) {
+                    iter.remove();
+                    removeFile(data.backupDir);
+                }
+            }
+        }
+    }
+
+    /**
+     * Load rollback data from storage if it has not already been loaded.
+     * After calling this funciton, mAvailableRollbacks and
+     * mRecentlyExecutedRollbacks will be non-null.
+     */
+    private void ensureRollbackDataLoaded() {
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+        }
+    }
+
+    /**
+     * Load rollback data from storage if it has not already been loaded.
+     * After calling this function, mAvailableRollbacks and
+     * mRecentlyExecutedRollbacks will be non-null.
+     */
+    @GuardedBy("mLock")
+    private void ensureRollbackDataLoadedLocked() {
+        if (mAvailableRollbacks == null) {
+            loadRollbackDataLocked();
+        }
+    }
+
+    /**
+     * Load rollback data from storage.
+     * Note: We do potentially heavy IO here while holding mLock, because we
+     * have to have the rollback data loaded before we can do anything else
+     * meaningful.
+     */
+    @GuardedBy("mLock")
+    private void loadRollbackDataLocked() {
+        mAvailableRollbacksDir.mkdirs();
+        mAvailableRollbacks = new ArrayList<>();
+        for (File rollbackDir : mAvailableRollbacksDir.listFiles()) {
+            if (rollbackDir.isDirectory()) {
+                // TODO: How to detect and clean up an invalid rollback
+                // directory? We don't know if it's invalid because something
+                // went wrong, or if it's only temporarily invalid because
+                // it's in the process of being created.
+                try {
+                    File jsonFile = new File(rollbackDir, "rollback.json");
+                    String jsonString = IoUtils.readFileAsString(jsonFile.getAbsolutePath());
+                    JSONObject jsonObject = new JSONObject(jsonString);
+                    String packageName = jsonObject.getString("packageName");
+                    long higherVersionCode = jsonObject.getLong("higherVersionCode");
+                    long lowerVersionCode = jsonObject.getLong("lowerVersionCode");
+                    Instant timestamp = Instant.parse(jsonObject.getString("timestamp"));
+                    PackageRollbackData data = new PackageRollbackData(
+                            new PackageRollbackInfo(packageName,
+                                new PackageRollbackInfo.PackageVersion(higherVersionCode),
+                                new PackageRollbackInfo.PackageVersion(lowerVersionCode)),
+                            rollbackDir, timestamp);
+                    mAvailableRollbacks.add(data);
+                } catch (IOException | JSONException | DateTimeParseException e) {
+                    Log.e(TAG, "Unable to read rollback data at " + rollbackDir, e);
+                }
+            }
+        }
+
+        mRecentlyExecutedRollbacks = new ArrayList<>();
+        if (mRecentlyExecutedRollbacksFile.exists()) {
+            try {
+                // TODO: How to cope with changes to the format of this file from
+                // when RollbackStore is updated in the future?
+                String jsonString = IoUtils.readFileAsString(
+                        mRecentlyExecutedRollbacksFile.getAbsolutePath());
+                JSONObject object = new JSONObject(jsonString);
+                JSONArray array = object.getJSONArray("recentlyExecuted");
+                for (int i = 0; i < array.length(); ++i) {
+                    JSONObject element = array.getJSONObject(i);
+                    String packageName = element.getString("packageName");
+                    long higherVersionCode = element.getLong("higherVersionCode");
+                    long lowerVersionCode = element.getLong("lowerVersionCode");
+                    PackageRollbackInfo target = new PackageRollbackInfo(packageName,
+                            new PackageRollbackInfo.PackageVersion(higherVersionCode),
+                            new PackageRollbackInfo.PackageVersion(lowerVersionCode));
+                    RollbackInfo rollback = new RollbackInfo(target);
+                    mRecentlyExecutedRollbacks.add(rollback);
+                }
+            } catch (IOException | JSONException e) {
+                // TODO: What to do here? Surely we shouldn't just forget about
+                // everything after the point of exception?
+                Log.e(TAG, "Failed to read recently executed rollbacks", e);
+            }
+        }
+
+        scheduleExpiration(0);
+    }
+
+    /**
+     * Called when a package has been replaced with a different version.
+     * Removes all backups for the package not matching the currently
+     * installed package version.
+     */
+    private void onPackageReplaced(String packageName) {
+        // TODO: Could this end up incorrectly deleting a rollback for a
+        // package that is about to be installed?
+        PackageRollbackInfo.PackageVersion installedVersion =
+                getInstalledPackageVersion(packageName);
+
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            while (iter.hasNext()) {
+                PackageRollbackData data = iter.next();
+                if (data.info.packageName.equals(packageName)
+                        && !data.info.higherVersion.equals(installedVersion)) {
+                    iter.remove();
+                    removeFile(data.backupDir);
+                }
+            }
+        }
+    }
+
+    /**
+     * Called when a package has been completely removed from the device.
+     * Removes all backups and rollback history for the given package.
+     */
+    private void onPackageFullyRemoved(String packageName) {
+        expireRollbackForPackage(packageName);
+
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            Iterator<RollbackInfo> iter = mRecentlyExecutedRollbacks.iterator();
+            boolean changed = false;
+            while (iter.hasNext()) {
+                RollbackInfo rollback = iter.next();
+                if (packageName.equals(rollback.targetPackage.packageName)) {
+                    iter.remove();
+                    changed = true;
+                }
+            }
+
+            if (changed) {
+                saveRecentlyExecutedRollbacksLocked();
+            }
+        }
+    }
+
+    /**
+     * Write the list of recently executed rollbacks to storage.
+     * Note: This happens while mLock is held, which should be okay because we
+     * expect executed rollbacks to be modified only in exceptional cases.
+     */
+    @GuardedBy("mLock")
+    private void saveRecentlyExecutedRollbacksLocked() {
+        try {
+            JSONObject json = new JSONObject();
+            JSONArray array = new JSONArray();
+            json.put("recentlyExecuted", array);
+
+            for (int i = 0; i < mRecentlyExecutedRollbacks.size(); ++i) {
+                RollbackInfo rollback = mRecentlyExecutedRollbacks.get(i);
+                JSONObject element = new JSONObject();
+                element.put("packageName", rollback.targetPackage.packageName);
+                element.put("higherVersionCode", rollback.targetPackage.higherVersion.versionCode);
+                element.put("lowerVersionCode", rollback.targetPackage.lowerVersion.versionCode);
+                array.put(element);
+            }
+
+            PrintWriter pw = new PrintWriter(mRecentlyExecutedRollbacksFile);
+            pw.println(json.toString());
+            pw.close();
+        } catch (IOException | JSONException e) {
+            // TODO: What to do here?
+            Log.e(TAG, "Failed to save recently executed rollbacks", e);
+        }
+    }
+
+    /**
+     * Records that the given package has been recently rolled back.
+     */
+    private void addRecentlyExecutedRollback(RollbackInfo rollback) {
+        // TODO: if the list of rollbacks gets too big, trim it to only those
+        // that are necessary to keep track of.
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            mRecentlyExecutedRollbacks.add(rollback);
+            saveRecentlyExecutedRollbacksLocked();
+        }
+    }
+
+    /**
+     * Notifies an IntentSender of failure.
+     *
+     * @param statusReceiver where to send the failure
+     * @param message the failure message.
+     */
+    private void sendFailure(IntentSender statusReceiver, String message) {
+        Log.e(TAG, message);
+        try {
+            // TODO: More context on which rollback failed?
+            // TODO: More refined failure code?
+            final Intent fillIn = new Intent();
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, message);
+            statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
+        } catch (IntentSender.SendIntentException e) {
+            // Nowhere to send the result back to, so don't bother.
+        }
+    }
+
+    /**
+     * Notifies an IntentSender of success.
+     */
+    private void sendSuccess(IntentSender statusReceiver) {
+        try {
+            final Intent fillIn = new Intent();
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS);
+            statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
+        } catch (IntentSender.SendIntentException e) {
+            // Nowhere to send the result back to, so don't bother.
+        }
+    }
+
+    // Check to see if anything needs expiration, and if so, expire it.
+    // Schedules future expiration as appropriate.
+    // TODO: Handle cases where the user changes time on the device.
+    private void runExpiration() {
+        Instant now = Instant.now();
+        Instant oldest = null;
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+
+            Iterator<PackageRollbackData> iter = mAvailableRollbacks.iterator();
+            while (iter.hasNext()) {
+                PackageRollbackData data = iter.next();
+                if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
+                    iter.remove();
+                    removeFile(data.backupDir);
+                } else if (oldest == null || oldest.isAfter(data.timestamp)) {
+                    oldest = data.timestamp;
+                }
+            }
+        }
+
+        if (oldest != null) {
+            scheduleExpiration(now.until(oldest.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS),
+                        ChronoUnit.MILLIS));
+        }
+    }
+
+    /**
+     * Schedules an expiration check to be run after the given duration in
+     * milliseconds has gone by.
+     */
+    private void scheduleExpiration(long duration) {
+        getHandler().postDelayed(() -> runExpiration(), duration);
+    }
+
+    private Handler getHandler() {
+        return mHandlerThread.getThreadHandler();
+    }
+
+    /**
+     * Called via broadcast by the package manager when a package is being
+     * staged for install with rollback enabled. Called before the package has
+     * been installed.
+     *
+     * @param id the id of the enable rollback request
+     * @param installFlags information about what is being installed.
+     * @param newPackageCodePath path to the package about to be installed.
+     * @return true if enabling the rollback succeeds, false otherwise.
+     */
+    private boolean enableRollback(int installFlags, File newPackageCodePath) {
+        if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
+            Log.e(TAG, "Rollbacks not supported for instant app install");
+            return false;
+        }
+        if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+            Log.e(TAG, "Rollbacks not supported for apex install");
+            return false;
+        }
+
+        // Get information about the package to be installed.
+        PackageParser.PackageLite newPackage = null;
+        try {
+            newPackage = PackageParser.parsePackageLite(newPackageCodePath, 0);
+        } catch (PackageParser.PackageParserException e) {
+            Log.e(TAG, "Unable to parse new package", e);
+            return false;
+        }
+
+        String packageName = newPackage.packageName;
+        Log.i(TAG, "Enabling rollback for install of " + packageName);
+
+        PackageRollbackInfo.PackageVersion newVersion =
+                new PackageRollbackInfo.PackageVersion(newPackage.versionCode);
+
+        // Get information about the currently installed package.
+        PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+        PackageParser.Package installedPackage = pm.getPackage(packageName);
+        if (installedPackage == null) {
+            // TODO: Support rolling back fresh package installs rather than
+            // fail here. Test this case.
+            Log.e(TAG, packageName + " is not installed");
+            return false;
+        }
+        PackageRollbackInfo.PackageVersion installedVersion =
+                new PackageRollbackInfo.PackageVersion(installedPackage.getLongVersionCode());
+
+        File backupDir;
+        try {
+            backupDir = Files.createTempDirectory(
+                mAvailableRollbacksDir.toPath(), packageName + "-").toFile();
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to create rollback for " + packageName, e);
+            return false;
+        }
+
+        // TODO: Should the timestamp be for when we commit the install, not
+        // when we create the pending one?
+        Instant timestamp = Instant.now();
+        try {
+            JSONObject json = new JSONObject();
+            json.put("packageName", packageName);
+            json.put("higherVersionCode", newVersion.versionCode);
+            json.put("lowerVersionCode", installedVersion.versionCode);
+            json.put("timestamp", timestamp.toString());
+
+            File jsonFile = new File(backupDir, "rollback.json");
+            PrintWriter pw = new PrintWriter(jsonFile);
+            pw.println(json.toString());
+            pw.close();
+        } catch (IOException | JSONException e) {
+            Log.e(TAG, "Unable to create rollback for " + packageName, e);
+            removeFile(backupDir);
+            return false;
+        }
+
+        // TODO: Copy by hard link instead to save on cpu and storage space?
+        int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, backupDir);
+        if (status != PackageManager.INSTALL_SUCCEEDED) {
+            Log.e(TAG, "Unable to copy package for rollback for " + packageName);
+            removeFile(backupDir);
+            return false;
+        }
+
+        PackageRollbackData data = new PackageRollbackData(
+                new PackageRollbackInfo(packageName, newVersion, installedVersion),
+                backupDir, timestamp);
+
+        synchronized (mLock) {
+            ensureRollbackDataLoadedLocked();
+            mAvailableRollbacks.add(data);
+        }
+
+        return true;
+    }
+
+    // TODO: Don't copy this from PackageManagerShellCommand like this?
+    private static class LocalIntentReceiver {
+        private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
+
+        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+            @Override
+            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+                try {
+                    mResult.offer(intent, 5, TimeUnit.SECONDS);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+
+        public IntentSender getIntentSender() {
+            return new IntentSender((IIntentSender) mLocalSender);
+        }
+
+        public Intent getResult() {
+            try {
+                return mResult.take();
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Deletes a file completely.
+     * If the file is a directory, its contents are deleted as well.
+     * Has no effect if the directory does not exist.
+     */
+    private void removeFile(File file) {
+        if (file.isDirectory()) {
+            for (File child : file.listFiles()) {
+                removeFile(child);
+            }
+        }
+        if (file.exists()) {
+            file.delete();
+        }
+    }
+
+    /**
+     * Gets the version of the package currently installed.
+     * Returns null if the package is not currently installed.
+     */
+    private PackageRollbackInfo.PackageVersion getInstalledPackageVersion(String packageName) {
+        PackageManager pm = mContext.getPackageManager();
+        PackageInfo pkgInfo = null;
+        try {
+            pkgInfo = pm.getPackageInfo(packageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+
+        return new PackageRollbackInfo.PackageVersion(pkgInfo.getLongVersionCode());
+    }
+}
diff --git a/services/core/java/com/android/server/rollback/TEST_MAPPING b/services/core/java/com/android/server/rollback/TEST_MAPPING
new file mode 100644
index 0000000..c1d95ac
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "RollbackTest"
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 667d21d..839ed30 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -30,6 +30,7 @@
 
 import libcore.util.HexEncoding;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.RandomAccessFile;
@@ -49,11 +50,27 @@
 abstract public class VerityUtils {
     private static final String TAG = "VerityUtils";
 
+    /**
+     * File extension of the signature file. For example, foo.apk.fsv_sig is the signature file of
+     * foo.apk.
+     */
+    public static final String FSVERITY_SIGNATURE_FILE_EXTENSION = ".fsv_sig";
+
     /** The maximum size of signature file.  This is just to avoid potential abuse. */
     private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
 
     private static final boolean DEBUG = false;
 
+    /** Returns true if the given file looks like containing an fs-verity signature. */
+    public static boolean isFsveritySignatureFile(File file) {
+        return file.getName().endsWith(FSVERITY_SIGNATURE_FILE_EXTENSION);
+    }
+
+    /** Returns the fs-verity signature file path of the given file. */
+    public static String getFsveritySignatureFilePath(String filePath) {
+        return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION;
+    }
+
     /** Returns whether the file has fs-verity enabled. */
     public static boolean hasFsverity(@NonNull String filePath) {
         // NB: only measure but not check the actual measurement here. As long as this succeeds,
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
similarity index 86%
rename from services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
rename to services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
index 4908964..438c303 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
+++ b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
@@ -30,7 +30,7 @@
 import java.util.Map;
 import java.util.Set;
 
-class SignedConfigApplicator {
+class GlobalSettingsConfigApplicator {
 
     private static final String TAG = "SignedConfig";
 
@@ -68,7 +68,7 @@
     private final String mSourcePackage;
     private final SignatureVerifier mVerifier;
 
-    SignedConfigApplicator(Context context, String sourcePackage) {
+    GlobalSettingsConfigApplicator(Context context, String sourcePackage) {
         mContext = context;
         mSourcePackage = sourcePackage;
         mVerifier = new SignatureVerifier();
@@ -102,7 +102,7 @@
 
     void applyConfig(String configStr, String signature) {
         if (!checkSignature(configStr, signature)) {
-            Slog.e(TAG, "Signature check on signed configuration in package " + mSourcePackage
+            Slog.e(TAG, "Signature check on global settings in package " + mSourcePackage
                     + " failed; ignoring");
             return;
         }
@@ -110,26 +110,26 @@
         try {
             config = SignedConfig.parse(configStr, ALLOWED_KEYS, KEY_VALUE_MAPPERS);
         } catch (InvalidConfigException e) {
-            Slog.e(TAG, "Failed to parse config from package " + mSourcePackage, e);
+            Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e);
             return;
         }
         int currentVersion = getCurrentConfigVersion();
         if (currentVersion >= config.version) {
-            Slog.i(TAG, "Config from package " + mSourcePackage + " is older than existing: "
-                    + config.version + "<=" + currentVersion);
+            Slog.i(TAG, "Global settings from package " + mSourcePackage
+                    + " is older than existing: " + config.version + "<=" + currentVersion);
             return;
         }
         // We have new config!
-        Slog.i(TAG, "Got new signed config from package " + mSourcePackage + ": version "
+        Slog.i(TAG, "Got new global settings from package " + mSourcePackage + ": version "
                 + config.version + " replacing existing version " + currentVersion);
         SignedConfig.PerSdkConfig matchedConfig =
                 config.getMatchingConfig(Build.VERSION.SDK_INT);
         if (matchedConfig == null) {
-            Slog.i(TAG, "Config is not applicable to current SDK version; ignoring");
+            Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring");
             return;
         }
 
-        Slog.i(TAG, "Updating signed config to version " + config.version);
+        Slog.i(TAG, "Updating global settings to version " + config.version);
         updateCurrentConfig(config.version, matchedConfig.values);
     }
 }
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
index 84ce93f..6bcee14 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -42,8 +42,8 @@
     private static final String TAG = "SignedConfig";
 
     // TODO should these be elsewhere? In a public API?
-    private static final String KEY_CONFIG = "android.signedconfig";
-    private static final String KEY_CONFIG_SIGNATURE = "android.signedconfig.signature";
+    private static final String KEY_GLOBAL_SETTINGS = "android.settings.global";
+    private static final String KEY_GLOBAL_SETTINGS_SIGNATURE = "android.settings.global.signature";
 
     private static class UpdateReceiver extends BroadcastReceiver {
         @Override
@@ -80,25 +80,25 @@
             if (DBG) Slog.d(TAG, "handlePackageBroadcast: no metadata");
             return;
         }
-        if (metaData.containsKey(KEY_CONFIG)
-                && metaData.containsKey(KEY_CONFIG_SIGNATURE)) {
-            String config = metaData.getString(KEY_CONFIG);
-            String signature = metaData.getString(KEY_CONFIG_SIGNATURE);
+        if (metaData.containsKey(KEY_GLOBAL_SETTINGS)
+                && metaData.containsKey(KEY_GLOBAL_SETTINGS_SIGNATURE)) {
+            String config = metaData.getString(KEY_GLOBAL_SETTINGS);
+            String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE);
             try {
                 // Base64 encoding is standard (not URL safe) encoding: RFC4648
                 config = new String(Base64.getDecoder().decode(config), StandardCharsets.UTF_8);
             } catch (IllegalArgumentException iae) {
-                Slog.e(TAG, "Failed to base64 decode config from " + packageName);
+                Slog.e(TAG, "Failed to base64 decode global settings config from " + packageName);
                 return;
             }
             if (DBG) {
-                Slog.d(TAG, "Got signed config: " + config);
-                Slog.d(TAG, "Got config signature: " + signature);
+                Slog.d(TAG, "Got global settings config: " + config);
+                Slog.d(TAG, "Got global settings signature: " + signature);
             }
-            new SignedConfigApplicator(mContext, packageName).applyConfig(
+            new GlobalSettingsConfigApplicator(mContext, packageName).applyConfig(
                     config, signature);
         } else {
-            if (DBG) Slog.d(TAG, "Package has no config/signature.");
+            if (DBG) Slog.d(TAG, "Package has no global settings config/signature.");
         }
     }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index f008770..01bff07 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -350,12 +350,34 @@
     }
 
     private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
+        if (wallpaper.connection != null) {
+            wallpaper.connection.forEachDisplayConnector(connector -> {
+                notifyWallpaperColorsChangedOnDisplay(wallpaper, which, connector.mDisplayId);
+            });
+        } else { // Lock wallpaper does not have WallpaperConnection.
+            notifyWallpaperColorsChangedOnDisplay(wallpaper, which, DEFAULT_DISPLAY);
+        }
+    }
+
+    private RemoteCallbackList<IWallpaperManagerCallback> getWallpaperCallbacks(int userId,
+            int displayId) {
+        RemoteCallbackList<IWallpaperManagerCallback> listeners = null;
+        final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> displayListeners =
+                mColorsChangedListeners.get(userId);
+        if (displayListeners != null) {
+            listeners = displayListeners.get(displayId);
+        }
+        return listeners;
+    }
+
+    private void notifyWallpaperColorsChangedOnDisplay(@NonNull WallpaperData wallpaper, int which,
+            int displayId) {
         boolean needsExtraction;
         synchronized (mLock) {
             final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
-                    mColorsChangedListeners.get(wallpaper.userId);
+                    getWallpaperCallbacks(wallpaper.userId, displayId);
             final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
-                    mColorsChangedListeners.get(UserHandle.USER_ALL);
+                    getWallpaperCallbacks(UserHandle.USER_ALL, displayId);
             // No-op until someone is listening to it.
             if (emptyCallbackList(currentUserColorListeners)  &&
                     emptyCallbackList(userAllColorListeners)) {
@@ -363,7 +385,7 @@
             }
 
             if (DEBUG) {
-                Slog.v(TAG, "notifyWallpaperColorsChanged " + which);
+                Slog.v(TAG, "notifyWallpaperColorsChangedOnDisplay " + which);
             }
 
             needsExtraction = wallpaper.primaryColors == null;
@@ -371,7 +393,7 @@
 
         // Let's notify the current values, it's fine if it's null, it just means
         // that we don't know yet.
-        notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
+        notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
 
         if (needsExtraction) {
             extractColors(wallpaper);
@@ -381,7 +403,7 @@
                     return;
                 }
             }
-            notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
+            notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
         }
     }
 
@@ -390,14 +412,14 @@
     }
 
     private void notifyColorListeners(@NonNull WallpaperColors wallpaperColors, int which,
-            int userId) {
+            int userId, int displayId) {
         final IWallpaperManagerCallback keyguardListener;
         final ArrayList<IWallpaperManagerCallback> colorListeners = new ArrayList<>();
         synchronized (mLock) {
             final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
-                    mColorsChangedListeners.get(userId);
+                    getWallpaperCallbacks(userId, displayId);
             final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
-                    mColorsChangedListeners.get(UserHandle.USER_ALL);
+                    getWallpaperCallbacks(UserHandle.USER_ALL, displayId);
             keyguardListener = mKeyguardListener;
 
             if (currentUserColorListeners != null) {
@@ -427,7 +449,8 @@
             }
         }
 
-        if (keyguardListener != null) {
+        // Only shows Keyguard on default display
+        if (keyguardListener != null && displayId == DEFAULT_DISPLAY) {
             try {
                 keyguardListener.onWallpaperColorsChanged(wallpaperColors, which, userId);
             } catch (RemoteException e) {
@@ -446,6 +469,11 @@
         String cropFile = null;
         int wallpaperId;
 
+        if (wallpaper.equals(mFallbackWallpaper)) {
+            extractDefaultImageWallpaperColors();
+            return;
+        }
+
         synchronized (mLock) {
             // Not having a wallpaperComponent means it's a lock screen wallpaper.
             final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent)
@@ -482,6 +510,39 @@
         }
     }
 
+    private void extractDefaultImageWallpaperColors() {
+        synchronized (mLock) {
+            if (mFallbackWallpaper.primaryColors != null) return;
+        }
+
+        if (DEBUG) Slog.d(TAG, "Extract default image wallpaper colors");
+        WallpaperColors colors = null;
+        final InputStream is = WallpaperManager.openDefaultWallpaper(mContext, FLAG_SYSTEM);
+        if (is != null) {
+            try {
+                final BitmapFactory.Options options = new BitmapFactory.Options();
+                final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
+                if (bitmap != null) {
+                    colors = WallpaperColors.fromBitmap(bitmap);
+                    bitmap.recycle();
+                }
+            } catch (OutOfMemoryError e) {
+                Slog.w(TAG, "Can't decode default wallpaper stream", e);
+            } finally {
+                IoUtils.closeQuietly(is);
+            }
+        }
+
+        if (colors == null) {
+            Slog.e(TAG, "Extract default image wallpaper colors failed");
+            return;
+        }
+
+        synchronized (mLock) {
+            mFallbackWallpaper.primaryColors = colors;
+        }
+    }
+
     /**
      * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
      * for display.
@@ -696,6 +757,11 @@
                     targetWallpaper.connection.removeDisplayConnector(displayId);
                     removeDisplayData(displayId);
                 }
+                for (int i = mColorsChangedListeners.size() - 1; i >= 0; i--) {
+                    final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> callbacks =
+                            mColorsChangedListeners.valueAt(i);
+                    callbacks.delete(displayId);
+                }
             }
         }
 
@@ -706,9 +772,11 @@
 
     /**
      * Map of color listeners per user id.
-     * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
+     * The first key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
+     * The secondary key will be the display id, which means which display the listener is
+     * interested in.
      */
-    private final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+    private final SparseArray<SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>>
             mColorsChangedListeners;
     private WallpaperData mLastWallpaper;
     private IWallpaperManagerCallback mKeyguardListener;
@@ -1228,9 +1296,10 @@
         /**
          * Called by a live wallpaper if its colors have changed.
          * @param primaryColors representation of wallpaper primary colors
+         * @param displayId for which display
          */
         @Override
-        public void onWallpaperColorsChanged(WallpaperColors primaryColors) {
+        public void onWallpaperColorsChanged(WallpaperColors primaryColors, int displayId) {
             int which;
             synchronized (mLock) {
                 // Do not broadcast changes on ImageWallpaper since it's handled
@@ -1243,14 +1312,16 @@
 
                 // Live wallpapers always are system wallpapers.
                 which = FLAG_SYSTEM;
-                // It's also the lock screen wallpaper when we don't have a bitmap in there
-                WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
-                if (lockedWallpaper == null) {
-                    which |= FLAG_LOCK;
+                // It's also the lock screen wallpaper when we don't have a bitmap in there.
+                if (displayId == DEFAULT_DISPLAY) {
+                    final WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
+                    if (lockedWallpaper == null) {
+                        which |= FLAG_LOCK;
+                    }
                 }
             }
             if (which != 0) {
-                notifyWallpaperColorsChanged(mWallpaper, which);
+                notifyWallpaperColorsChangedOnDisplay(mWallpaper, which, displayId);
             }
         }
 
@@ -1277,16 +1348,12 @@
                         Slog.w(TAG, "Failed to set ambient mode state", e);
                     }
                 }
-                // 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.
-                        // It's fine to be locked in here since the binder is oneway.
-                        connector.mEngine.requestWallpaperColors();
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to request wallpaper colors", e);
-                    }
+                try {
+                    // This will trigger onComputeColors in the wallpaper engine.
+                    // It's fine to be locked in here since the binder is oneway.
+                    connector.mEngine.requestWallpaperColors();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to request wallpaper colors", e);
                 }
             }
         }
@@ -1681,6 +1748,7 @@
         FgThread.getHandler().post(() -> {
             notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
             notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
+            notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
         });
     }
 
@@ -1743,6 +1811,7 @@
         // When clearing a wallpaper, broadcast new valid colors
         if (data != null) {
             notifyWallpaperColorsChanged(data, which);
+            notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
         }
     }
 
@@ -2084,35 +2153,47 @@
     }
 
     @Override
-    public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
+    public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
+            int displayId) {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, true, true, "registerWallpaperColorsCallback", null);
         synchronized (mLock) {
-            RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
-                    mColorsChangedListeners.get(userId);
-            if (userColorsChangedListeners == null) {
-                userColorsChangedListeners = new RemoteCallbackList<>();
-                mColorsChangedListeners.put(userId, userColorsChangedListeners);
+            SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+                    userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
+            if (userDisplayColorsChangedListeners == null) {
+                userDisplayColorsChangedListeners = new SparseArray<>();
+                mColorsChangedListeners.put(userId, userDisplayColorsChangedListeners);
             }
-            userColorsChangedListeners.register(cb);
+            RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
+                    userDisplayColorsChangedListeners.get(displayId);
+            if (displayChangedListeners == null) {
+                displayChangedListeners = new RemoteCallbackList<>();
+                userDisplayColorsChangedListeners.put(displayId, displayChangedListeners);
+            }
+            displayChangedListeners.register(cb);
         }
     }
 
     @Override
-    public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
+    public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
+            int displayId) {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, true, true, "unregisterWallpaperColorsCallback", null);
         synchronized (mLock) {
-            final RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
-                    mColorsChangedListeners.get(userId);
-            if (userColorsChangedListeners != null) {
-                userColorsChangedListeners.unregister(cb);
+            SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+                    userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
+            if (userDisplayColorsChangedListeners != null) {
+                RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
+                        userDisplayColorsChangedListeners.get(displayId);
+                if (displayChangedListeners != null) {
+                    displayChangedListeners.unregister(cb);
+                }
             }
         }
     }
 
     /**
-     * TODO(b/115486823) Extends this method with specific display.
+     * TODO(multi-display) Extends this method with specific display.
      * Propagate ambient state to wallpaper engine.
      *
      * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise.
@@ -2125,7 +2206,7 @@
             final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
             if (data != null && data.connection != null && data.connection.mInfo != null
                     && data.connection.mInfo.supportsAmbientMode()) {
-                // TODO(b/115486823) Extends this method with specific display.
+                // TODO(multi-display) Extends this method with specific display.
                 engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
             } else {
                 engine = null;
@@ -2151,7 +2232,8 @@
     }
 
     @Override
-    public WallpaperColors getWallpaperColors(int which, int userId) throws RemoteException {
+    public WallpaperColors getWallpaperColors(int which, int userId, int displayId)
+            throws RemoteException {
         if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
             throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM");
         }
@@ -2169,7 +2251,7 @@
             // Try to get the system wallpaper anyway since it might
             // also be the lock screen wallpaper
             if (wallpaperData == null) {
-                wallpaperData = mWallpaperMap.get(userId);
+                wallpaperData = findWallpaperAtDisplay(userId, displayId);
             }
 
             if (wallpaperData == null) {
@@ -2187,6 +2269,15 @@
         }
     }
 
+    private WallpaperData findWallpaperAtDisplay(int userId, int displayId) {
+        if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null
+                && mFallbackWallpaper.connection.containsDisplay(displayId)) {
+            return mFallbackWallpaper;
+        } else {
+            return mWallpaperMap.get(userId);
+        }
+    }
+
     @Override
     public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
             Rect cropHint, boolean allowBackup, Bundle extras, int which,
@@ -2382,6 +2473,7 @@
 
         if (shouldNotifyColors) {
             notifyWallpaperColorsChanged(wallpaper, which);
+            notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index fe0b5c2..caebf15 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1066,93 +1066,19 @@
 
                 final int visibleWindowCount = visibleWindows.size();
                 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
-                for (int i = visibleWindowCount - 1; i >= 0; i--) {
+
+                // Iterate until we figure out what is touchable for the entire screen.
+                for (int i = visibleWindowCount - 1; i >= 0 && !unaccountedSpace.isEmpty(); i--) {
                     final WindowState windowState = visibleWindows.valueAt(i);
-                    final int flags = windowState.mAttrs.flags;
-                    final Task task = windowState.getTask();
 
-                    // If the window is part of a task that we're finished with - ignore.
-                    if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
-                        continue;
-                    }
-
-                    // Ignore non-touchable windows, except the split-screen divider, which is
-                    // occasionally non-touchable but still useful for identifying split-screen
-                    // mode.
-                    if (((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
-                            && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
-                        continue;
-                    }
-
-                    // Compute the bounds in the screen.
                     final Rect boundsInScreen = mTempRect;
                     computeWindowBoundsInScreen(windowState, boundsInScreen);
 
-                    // If the window is completely covered by other windows - ignore.
-                    if (unaccountedSpace.quickReject(boundsInScreen)) {
-                        continue;
-                    }
-
-                    // Add windows of certain types not covered by modal windows.
-                    if (isReportedWindowType(windowState.mAttrs.type)) {
-                        // Add the window to the ones to be reported.
+                    if (windowMattersToAccessibility(windowState, boundsInScreen, unaccountedSpace,
+                            skipRemainingWindowsForTasks)) {
                         addPopulatedWindowInfo(windowState, boundsInScreen, windows, addedWindows);
-                        if (windowState.isFocused()) {
-                            focusedWindowAdded = true;
-                        }
-                    }
-
-                    if (windowState.mAttrs.type !=
-                            WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
-
-                        // Account for the space this window takes if the window
-                        // is not an accessibility overlay which does not change
-                        // the reported windows.
-                        unaccountedSpace.op(boundsInScreen, unaccountedSpace,
-                                Region.Op.REVERSE_DIFFERENCE);
-
-                        // If a window is modal it prevents other windows from being touched
-                        if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
-                            // Account for all space in the task, whether the windows in it are
-                            // touchable or not. The modal window blocks all touches from the task's
-                            // area.
-                            unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
-                                    Region.Op.REVERSE_DIFFERENCE);
-
-                            if (task != null) {
-                                // If the window is associated with a particular task, we can skip the
-                                // rest of the windows for that task.
-                                skipRemainingWindowsForTasks.add(task.mTaskId);
-                                continue;
-                            } else {
-                                // If the window is not associated with a particular task, then it is
-                                // globally modal. In this case we can skip all remaining windows.
-                                break;
-                            }
-                        }
-                    }
-
-                    // We figured out what is touchable for the entire screen - done.
-                    if (unaccountedSpace.isEmpty()) {
-                        break;
-                    }
-                }
-
-                // Always report the focused window.
-                if (!focusedWindowAdded) {
-                    for (int i = visibleWindowCount - 1; i >= 0; i--) {
-                        WindowState windowState = visibleWindows.valueAt(i);
-                        if (windowState.isFocused()) {
-                            // Compute the bounds in the screen.
-                            Rect boundsInScreen = mTempRect;
-                            computeWindowBoundsInScreen(windowState, boundsInScreen);
-
-                            // Add the window to the ones to be reported.
-                            addPopulatedWindowInfo(
-                                    windowState, boundsInScreen, windows, addedWindows);
-                            break;
-                        }
+                        updateUnaccountedSpace(windowState, boundsInScreen, unaccountedSpace,
+                                skipRemainingWindowsForTasks);
                     }
                 }
 
@@ -1221,6 +1147,73 @@
             clearAndRecycleWindows(windows);
         }
 
+        private boolean windowMattersToAccessibility(WindowState windowState, Rect boundsInScreen,
+                Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
+            if (windowState.isFocused()) {
+                return true;
+            }
+
+            // If the window is part of a task that we're finished with - ignore.
+            final Task task = windowState.getTask();
+            if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
+                return false;
+            }
+
+            // Ignore non-touchable windows, except the split-screen divider, which is
+            // occasionally non-touchable but still useful for identifying split-screen
+            // mode.
+            if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+                    && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
+                return false;
+            }
+
+            // If the window is completely covered by other windows - ignore.
+            if (unaccountedSpace.quickReject(boundsInScreen)) {
+                return false;
+            }
+
+            // Add windows of certain types not covered by modal windows.
+            if (isReportedWindowType(windowState.mAttrs.type)) {
+                return true;
+            }
+
+            return false;
+        }
+
+        private void updateUnaccountedSpace(WindowState windowState, Rect boundsInScreen,
+                Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
+            if (windowState.mAttrs.type
+                    != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+
+                // Account for the space this window takes if the window
+                // is not an accessibility overlay which does not change
+                // the reported windows.
+                unaccountedSpace.op(boundsInScreen, unaccountedSpace,
+                        Region.Op.REVERSE_DIFFERENCE);
+
+                // If a window is modal it prevents other windows from being touched
+                if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+                    // Account for all space in the task, whether the windows in it are
+                    // touchable or not. The modal window blocks all touches from the task's
+                    // area.
+                    unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
+                            Region.Op.REVERSE_DIFFERENCE);
+
+                    final Task task = windowState.getTask();
+                    if (task != null) {
+                        // If the window is associated with a particular task, we can skip the
+                        // rest of the windows for that task.
+                        skipRemainingWindowsForTasks.add(task.mTaskId);
+                    } else {
+                        // If the window is not associated with a particular task, then it is
+                        // globally modal. In this case we can skip all remaining windows.
+                        unaccountedSpace.setEmpty();
+                    }
+                }
+            }
+        }
+
         private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
             // Get the touchable frame.
             Region touchableRegion = mTempRegion1;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2cd0168..6f8f85f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -271,7 +271,10 @@
     boolean fullscreen; // The activity is opaque and fills the entire space of this task.
     // TODO: See if it possible to combine this with the fullscreen field.
     final boolean hasWallpaper; // Has a wallpaper window as a background.
-    final boolean noDisplay;  // activity is not displayed?
+    @VisibleForTesting
+    boolean noDisplay;  // activity is not displayed?
+    @VisibleForTesting
+    int mHandoverLaunchDisplayId = INVALID_DISPLAY; // Handover launch display id to next activity.
     private final boolean componentSpecified;  // did caller specify an explicit component?
     final boolean rootVoiceInteraction;  // was this the root activity of a voice interaction?
 
@@ -725,9 +728,10 @@
             mLastReportedMultiWindowMode = inPictureInPictureMode;
             final Configuration newConfig = new Configuration();
             if (targetStackBounds != null && !targetStackBounds.isEmpty()) {
-                task.computeResolvedOverrideConfiguration(newConfig,
-                        task.getParent().getConfiguration(),
-                        task.getRequestedOverrideConfiguration());
+                newConfig.setTo(task.getRequestedOverrideConfiguration());
+                Rect outBounds = newConfig.windowConfiguration.getBounds();
+                task.adjustForMinimalTaskDimensions(outBounds, outBounds);
+                task.computeConfigResourceOverrides(newConfig, task.getParent().getConfiguration());
             }
             schedulePictureInPictureModeChanged(newConfig);
             scheduleMultiWindowModeChanged(newConfig);
@@ -1019,6 +1023,8 @@
             if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
                 lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
             }
+            // Gets launch display id from options. It returns INVALID_DISPLAY if not set.
+            mHandoverLaunchDisplayId = options.getLaunchDisplayId();
         }
     }
 
@@ -2498,7 +2504,8 @@
             return;
         }
 
-        final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
+        final IBinder binder =
+                (freezeScreenIfNeeded && appToken != null) ? appToken.asBinder() : null;
         mAppWindowToken.setOrientation(requestedOrientation, binder, this);
     }
 
@@ -2542,7 +2549,6 @@
 
     // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
     private void updateOverrideConfiguration() {
-        mTmpConfig.unset();
         computeBounds(mTmpBounds);
 
         if (mTmpBounds.equals(getRequestedOverrideBounds())) {
@@ -2553,8 +2559,10 @@
 
         // Bounds changed...update configuration to match.
         if (!matchParentBounds()) {
-            task.computeResolvedOverrideConfiguration(mTmpConfig,
-                    task.getParent().getConfiguration(), getRequestedOverrideConfiguration());
+            mTmpConfig.setTo(getRequestedOverrideConfiguration());
+            task.computeConfigResourceOverrides(mTmpConfig, task.getParent().getConfiguration());
+        } else {
+            mTmpConfig.unset();
         }
 
         onRequestedOverrideConfigurationChanged(mTmpConfig);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index f58b83d..a50ae84 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -947,6 +947,7 @@
         final WindowProcessController wpc =
                 mService.getProcessController(r.processName, r.info.applicationInfo.uid);
 
+        boolean knownToBeDead = false;
         if (wpc != null && wpc.hasThread()) {
             try {
                 if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
@@ -965,6 +966,7 @@
 
             // If a dead object exception was thrown -- fall through to
             // restart the application.
+            knownToBeDead = true;
         }
 
         // Suppress transition until the new activity becomes ready, otherwise the keyguard can
@@ -978,7 +980,7 @@
         // ATMS lock held.
         final Message msg = PooledLambda.obtainMessage(
                 ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
-                r.info.applicationInfo, true, "activity", r.intent.getComponent());
+                r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
         mService.mH.sendMessage(msg);
     }
 
@@ -2657,7 +2659,8 @@
             return mService.getActivityStartController().startActivityInPackage(
                     task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
                     null, 0, 0, options, userId, task, "startActivityFromRecents",
-                    false /* validateIncomingUser */, null /* originatingPendingIntent */);
+                    false /* validateIncomingUser */, null /* originatingPendingIntent */,
+                    false /* allowBackgroundActivityStart */);
         } finally {
             if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && task != null) {
                 // If we are launching the task in the docked stack, put it into resizing mode so
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index b4d5d9f..0859683 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -258,7 +258,7 @@
             String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
             int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent) {
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
 
         userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
                 reason);
@@ -278,6 +278,7 @@
                 .setMayWait(userId)
                 .setInTask(inTask)
                 .setOriginatingPendingIntent(originatingPendingIntent)
+                .setAllowBackgroundActivityStart(allowBackgroundActivityStart)
                 .execute();
     }
 
@@ -294,7 +295,8 @@
      */
     final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
             String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) {
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart) {
 
         final String reason = "startActivityInPackage";
 
@@ -303,12 +305,13 @@
 
         // TODO: Switch to user app stacks here.
         return startActivities(null, uid, callingPackage, intents, resolvedTypes, resultTo, options,
-                userId, reason, originatingPendingIntent);
+                userId, reason, originatingPendingIntent, allowBackgroundActivityStart);
     }
 
     int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
-            int userId, String reason, PendingIntentRecord originatingPendingIntent) {
+            int userId, String reason, PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart) {
         if (intents == null) {
             throw new NullPointerException("intents is null");
         }
@@ -387,6 +390,7 @@
                             // top one as otherwise an activity below might consume it.
                             .setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
                             .setOriginatingPendingIntent(originatingPendingIntent)
+                            .setAllowBackgroundActivityStart(allowBackgroundActivityStart)
                             .execute();
 
                     if (res < 0) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 36701ea..b100ecd 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -323,6 +323,7 @@
         WaitResult waitResult;
         int filterCallingUid;
         PendingIntentRecord originatingPendingIntent;
+        boolean allowBackgroundActivityStart;
 
         /**
          * If set to {@code true}, allows this activity start to look into
@@ -380,6 +381,7 @@
             allowPendingRemoteAnimationRegistryLookup = true;
             filterCallingUid = UserHandle.USER_NULL;
             originatingPendingIntent = null;
+            allowBackgroundActivityStart = false;
         }
 
         /**
@@ -419,6 +421,7 @@
                     = request.allowPendingRemoteAnimationRegistryLookup;
             filterCallingUid = request.filterCallingUid;
             originatingPendingIntent = request.originatingPendingIntent;
+            allowBackgroundActivityStart = request.allowBackgroundActivityStart;
         }
     }
 
@@ -504,7 +507,7 @@
                         mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
                         mRequest.inTask, mRequest.reason,
                         mRequest.allowPendingRemoteAnimationRegistryLookup,
-                        mRequest.originatingPendingIntent);
+                        mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);
             } else {
                 return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
                         mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
@@ -515,7 +518,7 @@
                         mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
                         mRequest.outActivity, mRequest.inTask, mRequest.reason,
                         mRequest.allowPendingRemoteAnimationRegistryLookup,
-                        mRequest.originatingPendingIntent);
+                        mRequest.originatingPendingIntent, mRequest.allowBackgroundActivityStart);
             }
         } finally {
             onExecutionComplete();
@@ -548,7 +551,7 @@
             SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
             ActivityRecord[] outActivity, TaskRecord inTask, String reason,
             boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent) {
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
 
         if (TextUtils.isEmpty(reason)) {
             throw new IllegalArgumentException("Need to specify a reason.");
@@ -561,7 +564,8 @@
                 aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
                 callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                 options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
-                inTask, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);
+                inTask, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
+                allowBackgroundActivityStart);
 
         if (outActivity != null) {
             // mLastStartActivityRecord[0] is set in the call to startActivity above.
@@ -592,7 +596,7 @@
             SafeActivityOptions options,
             boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
             TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent) {
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
         int err = ActivityManager.START_SUCCESS;
         // Pull the optional Ephemeral Installer-only bundle out of the options early.
         final Bundle verificationBundle
@@ -742,7 +746,7 @@
         // on START_ABORTED
         if (!abort) {
             abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, realCallingUid,
-                    callerApp);
+                    callerApp, originatingPendingIntent, allowBackgroundActivityStart);
         }
 
         // Merge the two options bundles, while realCallerOptions takes precedence.
@@ -890,7 +894,8 @@
     }
 
     private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage,
-            int realCallingUid, WindowProcessController callerApp) {
+            int realCallingUid, WindowProcessController callerApp,
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
         if (mService.isBackgroundActivityStartsEnabled()) {
             return false;
         }
@@ -902,12 +907,25 @@
         if (callerApp != null && callerApp.hasForegroundActivities()) {
             return false;
         }
-        // don't abort if the callingUid is in the foreground
-        if (isUidForeground(callingUid)) {
+        // don't abort if the callingUid is in the foreground or is a persistent system process
+        if (isUidForeground(callingUid) || isUidPersistentSystemProcess(callingUid)) {
             return false;
         }
-        // don't abort if the realCallingUid is in the foreground and callingUid isn't
-        if ((realCallingUid != callingUid) && isUidForeground(realCallingUid)) {
+        // take realCallingUid into consideration
+        if (realCallingUid != callingUid) {
+            // don't abort if the realCallingUid is in the foreground and callingUid isn't
+            if (isUidForeground(realCallingUid)) {
+                return false;
+            }
+            // if the realCallingUid is a persistent system process, abort if the IntentSender
+            // wasn't whitelisted to start an activity
+            if (isUidPersistentSystemProcess(realCallingUid) && (originatingPendingIntent != null)
+                    && allowBackgroundActivityStart) {
+                return false;
+            }
+        }
+        // don't abort if the caller is currently temporarily whitelisted
+        if (callerApp != null && callerApp.areBackgroundActivityStartsAllowed()) {
             return false;
         }
         // don't abort if the caller has the same uid as the recents component
@@ -924,12 +942,17 @@
         return true;
     }
 
-    /** Returns true if uid has a visible window or its process is in top or persistent state. */
+    /** Returns true if uid has a visible window or its process is in a top state. */
     private boolean isUidForeground(int uid) {
-        return (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_TOP)
+        return (mService.getUidStateLocked(uid) == ActivityManager.PROCESS_STATE_TOP)
             || mService.mWindowManager.isAnyWindowVisibleForUid(uid);
     }
 
+    /** Returns true if uid is in a persistent state. */
+    private boolean isUidPersistentSystemProcess(int uid) {
+        return (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+    }
+
     private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
             Intent intent, WindowProcessController callerApp, ActivityRecord r,
             PendingIntentRecord originatingPendingIntent, boolean abortedStart) {
@@ -1049,7 +1072,7 @@
             Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
             int userId, TaskRecord inTask, String reason,
             boolean allowPendingRemoteAnimationRegistryLookup,
-            PendingIntentRecord originatingPendingIntent) {
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -1195,7 +1218,8 @@
                     voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
                     callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
                     ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
-                    allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);
+                    allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
+                    allowBackgroundActivityStart);
 
             Binder.restoreCallingIdentity(origId);
 
@@ -2731,6 +2755,11 @@
         return this;
     }
 
+    ActivityStarter setAllowBackgroundActivityStart(boolean allowBackgroundActivityStart) {
+        mRequest.allowBackgroundActivityStart = allowBackgroundActivityStart;
+        return this;
+    }
+
     void dump(PrintWriter pw, String prefix) {
         prefix = prefix + "  ";
         pw.print(prefix);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 0fc890a..0f286ce 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -197,16 +197,19 @@
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
+     * @param allowBackgroundActivityStart Whether the background activity start should be allowed
+     *        from originatingPendingIntent
      */
     public abstract int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
             String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent);
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart);
 
     public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
             String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
             int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent);
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart);
 
     /**
      * Start activity {@code intent} without calling user-id check.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 86c5d4d..42121ca 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -639,7 +639,8 @@
         }
     }
 
-    ActivityTaskManagerService(Context context) {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public ActivityTaskManagerService(Context context) {
         mContext = context;
         mFactoryTest = FactoryTest.getMode();
         mSystemThread = ActivityThread.currentActivityThread();
@@ -960,7 +961,7 @@
         // TODO: Switch to user app stacks here.
         return getActivityStartController().startActivities(caller, -1, callingPackage, intents,
                 resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId, reason,
-                null /* originatingPendingIntent */);
+                null /* originatingPendingIntent */, false /* allowBackgroundActivityStart */);
     }
 
     @Override
@@ -5807,18 +5808,20 @@
                         packageUid, packageName,
                         intents, resolvedTypes, null /* resultTo */,
                         SafeActivityOptions.fromBundle(bOptions), userId,
-                        false /* validateIncomingUser */, null /* originatingPendingIntent */);
+                        false /* validateIncomingUser */, null /* originatingPendingIntent */,
+                        false /* allowBackgroundActivityStart */);
             }
         }
 
         @Override
         public int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
                 String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) {
+                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+                boolean allowBackgroundActivityStart) {
             synchronized (mGlobalLock) {
                 return getActivityStartController().startActivitiesInPackage(uid, callingPackage,
                         intents, resolvedTypes, resultTo, options, userId, validateIncomingUser,
-                        originatingPendingIntent);
+                        originatingPendingIntent, allowBackgroundActivityStart);
             }
         }
 
@@ -5827,12 +5830,14 @@
                 String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
                 String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
                 int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
-                PendingIntentRecord originatingPendingIntent) {
+                PendingIntentRecord originatingPendingIntent,
+                boolean allowBackgroundActivityStart) {
             synchronized (mGlobalLock) {
                 return getActivityStartController().startActivityInPackage(uid, realCallingPid,
                         realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho,
                         requestCode, startFlags, options, userId, inTask, reason,
-                        validateIncomingUser, originatingPendingIntent);
+                        validateIncomingUser, originatingPendingIntent,
+                        allowBackgroundActivityStart);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 5b20af3..9bc8462 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -62,7 +62,8 @@
     private StatusBarManagerInternal mStatusBarInternal;
 
     protected WindowState mWin;
-    private int mState = StatusBarManager.WINDOW_STATE_SHOWING;
+    private @StatusBarManager.WindowVisibleState int mState =
+            StatusBarManager.WINDOW_STATE_SHOWING;
     private int mTransientBarState;
     private boolean mPendingShow;
     private long mLastTranslucent;
@@ -199,7 +200,8 @@
         return !mWin.isDrawnLw();
     }
 
-    private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
+    private @StatusBarManager.WindowVisibleState int computeStateLw(
+            boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
         if (win.isDrawnLw()) {
             final boolean vis = win.isVisibleLw();
             final boolean anim = win.isAnimatingLw();
@@ -218,7 +220,7 @@
         return mState;
     }
 
-    private boolean updateStateLw(final int state) {
+    private boolean updateStateLw(@StatusBarManager.WindowVisibleState final int state) {
         if (mWin != null && state != mState) {
             mState = state;
             if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fecc8da..740d472 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3656,6 +3656,12 @@
         }
     }
 
+    /** @returns the orientation of the display when it's rotation is ROTATION_0. */
+    int getNaturalOrientation() {
+        return mBaseDisplayWidth < mBaseDisplayHeight
+                ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+    }
+
     void performLayout(boolean initial, boolean updateInputWindows) {
         if (!isLayoutNeeded()) {
             return;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 49a3186..d6f1616 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsState;
@@ -26,8 +25,10 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
+import android.view.ViewRootImpl;
 
 import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
 import java.io.PrintWriter;
@@ -106,8 +107,8 @@
             mTmpRect.inset(mWin.mGivenContentInsets);
         }
         mSource.setFrame(mTmpRect);
-        setServerVisible(mWin.isVisible() && !mWin.mGivenInsetsPending);
-
+        setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.mPolicyVisibility
+                && !mWin.mGivenInsetsPending);
     }
 
     void updateControlForTarget(@Nullable WindowState target) {
@@ -115,15 +116,15 @@
             return;
         }
         if (target == null) {
-            revokeControl();
+            // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
+            mWin.cancelAnimation();
             return;
         }
         mAdapter = new ControlAdapter();
         mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
-                false /* TODO hidden */);
+                !mClientVisible /* hidden */);
         mControllingWin = target;
         mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash);
-        setClientVisible(InsetsState.getDefaultVisibly(mSource.getType()));
     }
 
     boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) {
@@ -135,7 +136,12 @@
     }
 
     private void setClientVisible(boolean clientVisible) {
+        if (mClientVisible == clientVisible) {
+            return;
+        }
         mClientVisible = clientVisible;
+        mDisplayContent.mWmService.mH.sendMessage(PooledLambda.obtainMessage(
+                DisplayContent::layoutAndAssignWindowLayersIfNeeded, mDisplayContent));
         updateVisibility();
     }
 
@@ -152,13 +158,8 @@
         return mControl;
     }
 
-    void revokeControl() {
-        if (mControllingWin != null) {
-
-            // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
-            mWin.cancelAnimation();
-        }
-        setClientVisible(InsetsState.getDefaultVisibly(mSource.getType()));
+    boolean isClientVisible() {
+        return !ViewRootImpl.USE_NEW_INSETS || mClientVisible;
     }
 
     private class ControlAdapter implements AnimationAdapter {
@@ -186,6 +187,7 @@
         public void onAnimationCancelled(SurfaceControl animationLeash) {
             if (mAdapter == this) {
                 mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this);
+                setClientVisible(InsetsState.getDefaultVisibly(mSource.getType()));
                 mControl = null;
                 mControllingWin = null;
                 mAdapter = null;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 8e119bb..cc57b93 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -218,5 +218,11 @@
     void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "WindowInsetsStateController");
         mState.dump(prefix + "  ", pw);
+        pw.println(prefix + "  " + "Control map:");
+        for (int i = mTypeWinControlMap.size() - 1; i >= 0; i--) {
+            pw.print(prefix + "  ");
+            pw.println(InsetsState.typeToString(mTypeWinControlMap.keyAt(i)) + " -> "
+                    + mTypeWinControlMap.valueAt(i));
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index d85fdb0..937c9d9 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -187,7 +187,7 @@
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
             Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface, InsetsState outInsetsState) {
+            SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
@@ -195,7 +195,7 @@
                 requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                 outStableInsets, outsets, outBackdropFrame, cutout,
-                mergedConfiguration, outSurface, outInsetsState);
+                mergedConfiguration, outSurfaceControl, 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/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 5107b52..6acd864 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -219,10 +219,16 @@
                 }
             }
 
-            if (launchMode == WINDOWING_MODE_FREEFORM && !currentParams.mBounds.isEmpty()) {
+            if (!currentParams.mBounds.isEmpty()) {
+                // Carry over bounds from callers regardless of launch mode because bounds is still
+                // used to restore last non-fullscreen bounds when launch mode is not freeform.
+                // Therefore it's not a resolution step for non-freeform launch mode and only
+                // consider it fully resolved only when launch mode is freeform.
                 outParams.mBounds.set(currentParams.mBounds);
-                fullyResolvedCurrentParam = true;
-                if (DEBUG) appendLog("inherit-bounds=" + outParams.mBounds);
+                if (launchMode == WINDOWING_MODE_FREEFORM) {
+                    fullyResolvedCurrentParam = true;
+                    if (DEBUG) appendLog("inherit-bounds=" + outParams.mBounds);
+                }
             }
         }
 
@@ -305,6 +311,13 @@
             displayId = optionLaunchId;
         }
 
+        // If the source activity is a no-display activity, pass on the launch display id from
+        // source activity as currently preferred.
+        if (displayId == INVALID_DISPLAY && source != null && source.noDisplay) {
+            displayId = source.mHandoverLaunchDisplayId;
+            if (DEBUG) appendLog("display-from-no-display-source=" + displayId);
+        }
+
         ActivityStack stack =
                 (displayId == INVALID_DISPLAY && task != null) ? task.getStack() : null;
         if (stack != null) {
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index e343322..1d56f04 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -44,6 +44,9 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -1259,10 +1262,6 @@
         setFrontOfTask();
     }
 
-    void addActivityAtBottom(ActivityRecord r) {
-        addActivityAtIndex(0, r);
-    }
-
     void addActivityToTop(ActivityRecord r) {
         addActivityAtIndex(mActivities.size(), r);
     }
@@ -1278,6 +1277,34 @@
     }
 
     /**
+     * Checks if the root activity requires a particular orientation (either by override or
+     * activityInfo) and returns that. Otherwise, this returns ORIENTATION_UNDEFINED.
+     */
+    private int getRootActivityRequestedOrientation() {
+        ActivityRecord root = getRootActivity();
+        if (getRequestedOverrideConfiguration().orientation != ORIENTATION_UNDEFINED
+                || root == null) {
+            return getRequestedOverrideConfiguration().orientation;
+        }
+        int rootScreenOrientation = root.getOrientation();
+        if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+            // NOSENSOR means the display's "natural" orientation, so return that.
+            ActivityDisplay display = mStack != null ? mStack.getDisplay() : null;
+            if (display != null && display.mDisplayContent != null) {
+                return mStack.getDisplay().mDisplayContent.getNaturalOrientation();
+            }
+        } else if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+            // LOCKED means the activity's orientation remains unchanged, so return existing value.
+            return root.getConfiguration().orientation;
+        } else if (ActivityInfo.isFixedOrientationLandscape(rootScreenOrientation)) {
+            return ORIENTATION_LANDSCAPE;
+        } else if (ActivityInfo.isFixedOrientationPortrait(rootScreenOrientation)) {
+            return ORIENTATION_PORTRAIT;
+        }
+        return ORIENTATION_UNDEFINED;
+    }
+
+    /**
      * Adds an activity {@param r} at the given {@param index}. The activity {@param r} must either
      * be in the current task or unparented to any task.
      */
@@ -1741,7 +1768,7 @@
         updateTaskDescription();
     }
 
-    private void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
+    void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
         if (bounds == null) {
             return;
         }
@@ -1853,11 +1880,27 @@
 
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
+        // Check if the new configuration supports persistent bounds (eg. is Freeform) and if so
+        // restore the last recorded non-fullscreen bounds.
+        final boolean prevPersistTaskBounds = getWindowConfiguration().persistTaskBounds();
+        final boolean nextPersistTaskBounds =
+                getRequestedOverrideConfiguration().windowConfiguration.persistTaskBounds()
+                || newParentConfig.windowConfiguration.persistTaskBounds();
+        if (!prevPersistTaskBounds && nextPersistTaskBounds
+                && mLastNonFullscreenBounds != null && !mLastNonFullscreenBounds.isEmpty()) {
+            // Bypass onRequestedOverrideConfigurationChanged here to avoid infinite loop.
+            getRequestedOverrideConfiguration().windowConfiguration
+                    .setBounds(mLastNonFullscreenBounds);
+        }
+
         final boolean wasInMultiWindowMode = inMultiWindowMode();
         super.onConfigurationChanged(newParentConfig);
         if (wasInMultiWindowMode != inMultiWindowMode()) {
             mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
         }
+
+        // If the configuration supports persistent bounds (eg. Freeform), keep track of the
+        // current (non-fullscreen) bounds for persistence.
         if (getWindowConfiguration().persistTaskBounds()) {
             final Rect currentBounds = getRequestedOverrideBounds();
             if (!currentBounds.isEmpty()) {
@@ -2047,7 +2090,7 @@
      * configuring an "inherit-bounds" window which means that all configuration settings would
      * just be inherited from the parent configuration.
      **/
-    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Rect bounds,
+    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
             @NonNull Configuration parentConfig) {
         int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_UNDEFINED) {
@@ -2060,6 +2103,7 @@
         }
         density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
 
+        final Rect bounds = inOutConfig.windowConfiguration.getBounds();
         Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
         if (outAppBounds == null || outAppBounds.isEmpty()) {
             inOutConfig.windowConfiguration.setAppBounds(bounds);
@@ -2107,13 +2151,14 @@
                     // Iterating across all screen orientations, and return the minimum of the task
                     // width taking into account that the bounds might change because the snap
                     // algorithm snaps to a different value
-                    getSmallestScreenWidthDpForDockedBounds(bounds);
+                    inOutConfig.smallestScreenWidthDp =
+                            getSmallestScreenWidthDpForDockedBounds(bounds);
                 }
                 // otherwise, it will just inherit
             }
         }
 
-        if (inOutConfig.orientation == Configuration.ORIENTATION_UNDEFINED) {
+        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
             inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
                     ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
         }
@@ -2134,36 +2179,56 @@
         }
     }
 
-    // TODO(b/113900640): remove this once ActivityRecord is changed to not need it anymore.
-    void computeResolvedOverrideConfiguration(Configuration inOutConfig, Configuration parentConfig,
-            Configuration overrideConfig) {
-        // Save previous bounds because adjustForMinimalTaskDimensions uses that to determine if it
-        // changes left bound vs. right bound, or top bound vs. bottom bound.
-        mTmpBounds.set(inOutConfig.windowConfiguration.getBounds());
-
-        inOutConfig.setTo(overrideConfig);
-
-        Rect outOverrideBounds = inOutConfig.windowConfiguration.getBounds();
-        if (outOverrideBounds != null && !outOverrideBounds.isEmpty()) {
-            adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
-
-            int windowingMode = overrideConfig.windowConfiguration.getWindowingMode();
-            if (windowingMode == WINDOWING_MODE_UNDEFINED) {
-                windowingMode = parentConfig.windowConfiguration.getWindowingMode();
-            }
-            if (windowingMode == WINDOWING_MODE_FREEFORM) {
-                // by policy, make sure the window remains within parent
-                fitWithinBounds(outOverrideBounds, parentConfig.windowConfiguration.getBounds());
-            }
-
-            computeConfigResourceOverrides(inOutConfig, outOverrideBounds, parentConfig);
-        }
-    }
-
     @Override
     void resolveOverrideConfiguration(Configuration newParentConfig) {
-        computeResolvedOverrideConfiguration(getResolvedOverrideConfiguration(), newParentConfig,
-                getRequestedOverrideConfiguration());
+        mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
+        super.resolveOverrideConfiguration(newParentConfig);
+        int windowingMode =
+                getRequestedOverrideConfiguration().windowConfiguration.getWindowingMode();
+        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+            windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
+        }
+        Rect outOverrideBounds =
+                getResolvedOverrideConfiguration().windowConfiguration.getBounds();
+
+        if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
+            // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent"
+            outOverrideBounds.setEmpty();
+
+            // If the task or its root activity require a different orientation, make it fit the
+            // available bounds by scaling down its bounds.
+            int forcedOrientation = getRootActivityRequestedOrientation();
+            if (forcedOrientation != ORIENTATION_UNDEFINED
+                    && forcedOrientation != newParentConfig.orientation) {
+                final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+                final int parentWidth = parentBounds.width();
+                final int parentHeight = parentBounds.height();
+                final float aspect = ((float) parentHeight) / parentWidth;
+                if (forcedOrientation == ORIENTATION_LANDSCAPE) {
+                    final int height = (int) (parentWidth / aspect);
+                    final int top = parentBounds.centerY() - height / 2;
+                    outOverrideBounds.set(
+                            parentBounds.left, top, parentBounds.right, top + height);
+                } else {
+                    final int width = (int) (parentHeight * aspect);
+                    final int left = parentBounds.centerX() - width / 2;
+                    outOverrideBounds.set(
+                            left, parentBounds.top, left + width, parentBounds.bottom);
+                }
+            }
+        }
+
+        if (outOverrideBounds.isEmpty()) {
+            // If the task fills the parent, just inherit all the other configs from parent.
+            return;
+        }
+
+        adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
+        if (windowingMode == WINDOWING_MODE_FREEFORM) {
+            // by policy, make sure the window remains within parent somewhere
+            fitWithinBounds(outOverrideBounds, newParentConfig.windowConfiguration.getBounds());
+        }
+        computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
     }
 
     Rect updateOverrideConfigurationFromLaunchBounds() {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 9a56606..2d3e3ae 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -111,6 +111,7 @@
     private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
     private final Window mWindow;
     private final Surface mSurface;
+    private SurfaceControl mSurfaceControl;
     private SurfaceControl mChildSurfaceControl;
     private final IWindowSession mSession;
     private final WindowManagerService mService;
@@ -136,7 +137,7 @@
         final Window window = new Window();
         final IWindowSession session = WindowManagerGlobal.getWindowSession();
         window.setSession(session);
-        final Surface surface = new Surface();
+        final SurfaceControl surfaceControl = new SurfaceControl();
         final Rect tmpRect = new Rect();
         final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper();
         final Rect tmpFrame = new Rect();
@@ -213,14 +214,14 @@
             // Local call.
         }
         final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
-                surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
+                surfaceControl, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
                 navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds,
                 currentOrientation);
         window.setOuter(snapshotSurface);
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                     tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
-                    tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState);
+                    tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState);
         } catch (RemoteException e) {
             // Local call.
         }
@@ -230,15 +231,16 @@
     }
 
     @VisibleForTesting
-    TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface,
+    TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
             TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor,
             int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags,
             Rect taskBounds, int currentOrientation) {
         mService = service;
+        mSurface = new Surface();
         mHandler = new Handler(mService.mH.getLooper());
         mSession = WindowManagerGlobal.getWindowSession();
         mWindow = window;
-        mSurface = surface;
+        mSurfaceControl = surfaceControl;
         mSnapshot = snapshot;
         mTitle = title;
         mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
@@ -281,6 +283,8 @@
 
     private void drawSnapshot() {
         final GraphicBuffer buffer = mSnapshot.getSnapshot();
+        mSurface.copyFrom(mSurfaceControl);
+
         if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch="
                 + mSizeMismatch);
         if (mSizeMismatch) {
@@ -310,13 +314,14 @@
         if (!mSurface.isValid()) {
             throw new IllegalStateException("mSurface does not hold a valid surface.");
         }
-        final SurfaceSession session = new SurfaceSession(mSurface);
+        final SurfaceSession session = new SurfaceSession();
 
         // Keep a reference to it such that it doesn't get destroyed when finalized.
         mChildSurfaceControl = new SurfaceControl.Builder(session)
                 .setName(mTitle + " - task-snapshot-surface")
                 .setBufferSize(buffer.getWidth(), buffer.getHeight())
                 .setFormat(buffer.getFormat())
+                .setParent(mSurfaceControl)
                 .build();
         Surface surface = new Surface();
         surface.copyFrom(mChildSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f5f55e2..91aac7e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1874,7 +1874,7 @@
             long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface, InsetsState outInsetsState) {
+            SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
         int result = 0;
         boolean configChanged;
         final boolean hasStatusBarPermission =
@@ -2047,7 +2047,7 @@
                 result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility);
 
                 try {
-                    result = createSurfaceControl(outSurface, result, win, winAnimator);
+                    result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                 } catch (Exception e) {
                     displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
 
@@ -2078,7 +2078,7 @@
                     // handled yet, or it might want to draw a last frame. If we already have a
                     // surface, let the client use that, but don't create new surface at this point.
                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
-                    winAnimator.mSurfaceController.getSurface(outSurface);
+                    winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 } else {
                     if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
@@ -2086,7 +2086,7 @@
                     try {
                         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
                                 + win.mAttrs.getTitle());
-                        outSurface.release();
+                        outSurfaceControl.release();
                     } finally {
                         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                     }
@@ -2182,7 +2182,7 @@
                 + ", requestedHeight=" + requestedHeight
                 + ", viewVisibility=" + viewVisibility
                 + "\nRelayout returning frame=" + outFrame
-                + ", surface=" + outSurface);
+                + ", surface=" + outSurfaceControl);
 
             if (localLOGV || DEBUG_FOCUS) Slog.v(
                 TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
@@ -2252,7 +2252,7 @@
         return focusMayChange;
     }
 
-    private int createSurfaceControl(Surface outSurface, int result, WindowState win,
+    private int createSurfaceControl(SurfaceControl outSurfaceControl, int result, WindowState win,
             WindowStateAnimator winAnimator) {
         if (!win.mHasSurface) {
             result |= RELAYOUT_RES_SURFACE_CHANGED;
@@ -2266,13 +2266,13 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
         if (surfaceController != null) {
-            surfaceController.getSurface(outSurface);
-            if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  OUT SURFACE " + outSurface + ": copied");
+            surfaceController.getSurfaceControl(outSurfaceControl);
+            if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  OUT SURFACE " + outSurfaceControl + ": copied");
         } else {
             // For some reason there isn't a surface.  Clear the
             // caller's object so they see the same state.
             Slog.w(TAG_WM, "Failed to create surface control for " + win);
-            outSurface.release();
+            outSurfaceControl.release();
         }
 
         return result;
@@ -6166,7 +6166,7 @@
                     dumpWindowsLocked(pw, true, null);
                 }
                 return;
-            } else if ("all".equals(cmd) || "a".equals(cmd)) {
+            } else if ("all".equals(cmd)) {
                 synchronized (mGlobalLock) {
                     dumpWindowsLocked(pw, true, null);
                 }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index f7f7528..c38a974 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -141,6 +141,9 @@
     private volatile boolean mPerceptible;
     // Set to true when process was launched with a wrapper attached
     private volatile boolean mUsingWrapper;
+    // Set to true if this process is currently temporarily whitelisted to start activities even if
+    // it's not in the foreground
+    private volatile boolean mAllowBackgroundActivityStarts;
 
     // Thread currently set for VR scheduling
     int mVrThreadTid;
@@ -343,6 +346,14 @@
         return mUsingWrapper;
     }
 
+    public void setAllowBackgroundActivityStarts(boolean allowBackgroundActivityStarts) {
+        mAllowBackgroundActivityStarts = allowBackgroundActivityStarts;
+    }
+
+    public boolean areBackgroundActivityStartsAllowed() {
+        return mAllowBackgroundActivityStarts;
+    }
+
     public void setInstrumenting(boolean instrumenting) {
         mInstrumenting = instrumenting;
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e78c12c..cd29b3c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1373,12 +1373,15 @@
 
     @Override
     boolean isVisible() {
-        return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility;
+        return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility
+                // If we don't have a provider, this window isn't used as a window generating
+                // insets, so nobody can hide it over the inset APIs.
+                && (mInsetProvider == null || mInsetProvider.isClientVisible());
     }
 
     /**
-     * @return True if the window would be visible if we'd ignore policy visibility, false
-     *         otherwise.
+     * @return {@code true} if the window would be visible if we'd ignore policy visibility,
+     *         {@code false} otherwise.
      */
     boolean wouldBeVisibleIfPolicyIgnored() {
         return mHasSurface && !isParentWindowHidden()
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index ce627e2..c2a8e7e 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -478,8 +478,8 @@
         return mSurfaceControl.getHandle();
     }
 
-    void getSurface(Surface outSurface) {
-        outSurface.copyFrom(mSurfaceControl);
+    void getSurfaceControl(SurfaceControl outSurfaceControl) {
+        outSurfaceControl.copyFrom(mSurfaceControl);
     }
 
     int getLayer() {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 855fb50..4326c39 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -145,6 +145,7 @@
 import com.android.server.wm.ActivityTaskManagerService;
 import com.android.server.wm.WindowManagerGlobalLock;
 import com.android.server.wm.WindowManagerService;
+import com.google.android.startop.iorap.IorapForwardingService;
 
 import dalvik.system.VMRuntime;
 
@@ -783,7 +784,7 @@
 
         // Manages apk rollbacks.
         traceBeginAndSlog("StartRollbackManagerService");
-        mSystemServiceManager.startService(RollbackManagerService.Lifecycle.class);
+        mSystemServiceManager.startService(RollbackManagerService.class);
         traceEnd();
     }
 
@@ -1007,10 +1008,13 @@
             mSystemServiceManager.startService(PinnerService.class);
             traceEnd();
 
+            traceBeginAndSlog("IorapForwardingService");
+            mSystemServiceManager.startService(IorapForwardingService.class);
+            traceEnd();
+
             traceBeginAndSlog("SignedConfigService");
             SignedConfigService.registerUpdateReceiver(mSystemContext);
             traceEnd();
-
         } catch (RuntimeException e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service", e);
diff --git a/services/net/java/android/net/dhcp/DhcpLease.java b/services/net/java/android/net/dhcp/DhcpLease.java
index 6cdd2aa..6849cfa 100644
--- a/services/net/java/android/net/dhcp/DhcpLease.java
+++ b/services/net/java/android/net/dhcp/DhcpLease.java
@@ -58,6 +58,11 @@
         mHostname = hostname;
     }
 
+    /**
+     * Get the clientId associated with this lease, if any.
+     *
+     * <p>If the lease is not associated to a clientId, this returns null.
+     */
     @Nullable
     public byte[] getClientId() {
         if (mClientId == null) {
@@ -97,6 +102,11 @@
                 (hostname == null ? mHostname : hostname));
     }
 
+    /**
+     * Determine whether this lease matches a client with the specified parameters.
+     * @param clientId clientId of the client if any, or null otherwise.
+     * @param hwAddr Hardware address of the client.
+     */
     public boolean matchesClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) {
         if (mClientId != null) {
             return Arrays.equals(mClientId, clientId);
@@ -110,7 +120,7 @@
         if (!(obj instanceof DhcpLease)) {
             return false;
         }
-        final DhcpLease other = (DhcpLease)obj;
+        final DhcpLease other = (DhcpLease) obj;
         return Arrays.equals(mClientId, other.mClientId)
                 && mHwAddr.equals(other.mHwAddr)
                 && mNetAddr.equals(other.mNetAddr)
diff --git a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java b/services/net/java/android/net/dhcp/DhcpLeaseRepository.java
index 2dda421..b3d0512 100644
--- a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java
+++ b/services/net/java/android/net/dhcp/DhcpLeaseRepository.java
@@ -29,8 +29,8 @@
 import android.annotation.Nullable;
 import android.net.IpPrefix;
 import android.net.MacAddress;
-import android.net.util.SharedLog;
 import android.net.dhcp.DhcpServer.Clock;
+import android.net.util.SharedLog;
 import android.util.ArrayMap;
 
 import java.net.Inet4Address;
@@ -117,7 +117,7 @@
      */
     private final LinkedHashMap<Inet4Address, Long> mDeclinedAddrs = new LinkedHashMap<>();
 
-    public DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
+    DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
             long leaseTimeMs, @NonNull SharedLog log, @NonNull Clock clock) {
         updateParams(prefix, reservedAddrs, leaseTimeMs);
         mLog = log;
@@ -250,8 +250,8 @@
                 // reqAddr null (RENEWING/REBINDING): client renewing its own lease for clientAddr.
                 // reqAddr set with sid not set (INIT-REBOOT): client verifying configuration.
                 // In both cases, throw if clientAddr or reqAddr does not match the known lease.
-                throw new InvalidAddressException("Incorrect address for client in " +
-                        (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING"));
+                throw new InvalidAddressException("Incorrect address for client in "
+                        + (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING"));
             }
         }
 
diff --git a/services/net/java/android/net/dhcp/DhcpPacketListener.java b/services/net/java/android/net/dhcp/DhcpPacketListener.java
index 6f620c5..dce8b61 100644
--- a/services/net/java/android/net/dhcp/DhcpPacketListener.java
+++ b/services/net/java/android/net/dhcp/DhcpPacketListener.java
@@ -32,32 +32,32 @@
  */
 abstract class DhcpPacketListener extends FdEventsReader<DhcpPacketListener.Payload> {
     static final class Payload {
-        final byte[] bytes = new byte[DhcpPacket.MAX_LENGTH];
-        Inet4Address srcAddr;
-        int srcPort;
+        protected final byte[] mBytes = new byte[DhcpPacket.MAX_LENGTH];
+        protected Inet4Address mSrcAddr;
+        protected int mSrcPort;
     }
 
-    public DhcpPacketListener(@NonNull Handler handler) {
+    DhcpPacketListener(@NonNull Handler handler) {
         super(handler, new Payload());
     }
 
     @Override
     protected int recvBufSize(@NonNull Payload buffer) {
-        return buffer.bytes.length;
+        return buffer.mBytes.length;
     }
 
     @Override
     protected final void handlePacket(@NonNull Payload recvbuf, int length) {
-        if (recvbuf.srcAddr == null) {
+        if (recvbuf.mSrcAddr == null) {
             return;
         }
 
         try {
-            final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.bytes, length,
+            final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.mBytes, length,
                     DhcpPacket.ENCAP_BOOTP);
-            onReceive(packet, recvbuf.srcAddr, recvbuf.srcPort);
+            onReceive(packet, recvbuf.mSrcAddr, recvbuf.mSrcPort);
         } catch (DhcpPacket.ParseException e) {
-            logParseError(recvbuf.bytes, length, e);
+            logParseError(recvbuf.mBytes, length, e);
         }
     }
 
@@ -66,11 +66,11 @@
             throws Exception {
         final InetSocketAddress addr = new InetSocketAddress();
         final int read = Os.recvfrom(
-                fd, packetBuffer.bytes, 0, packetBuffer.bytes.length, 0 /* flags */, addr);
+                fd, packetBuffer.mBytes, 0, packetBuffer.mBytes.length, 0 /* flags */, addr);
 
         // Buffers with null srcAddr will be dropped in handlePacket()
-        packetBuffer.srcAddr = inet4AddrOrNull(addr);
-        packetBuffer.srcPort = addr.getPort();
+        packetBuffer.mSrcAddr = inet4AddrOrNull(addr);
+        packetBuffer.mSrcPort = addr.getPort();
         return read;
     }
 
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java
index 35d29e7..641bba2 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/services/net/java/android/net/dhcp/DhcpServer.java
@@ -101,6 +101,13 @@
     @NonNull
     private DhcpServingParams mServingParams;
 
+    /**
+     * Clock to be used by DhcpServer to track time for lease expiration.
+     *
+     * <p>The clock should track time as may be measured by clients obtaining a lease. It does not
+     * need to be monotonous across restarts of the server as long as leases are cleared when the
+     * server is stopped.
+     */
     public static class Clock {
         /**
          * @see SystemClock#elapsedRealtime()
@@ -110,13 +117,43 @@
         }
     }
 
+    /**
+     * Dependencies for the DhcpServer. Useful to be mocked in tests.
+     */
     public interface Dependencies {
+        /**
+         * Send a packet to the specified datagram socket.
+         *
+         * @param fd File descriptor of the socket.
+         * @param buffer Data to be sent.
+         * @param dst Destination address of the packet.
+         */
         void sendPacket(@NonNull FileDescriptor fd, @NonNull ByteBuffer buffer,
                 @NonNull InetAddress dst) throws ErrnoException, IOException;
+
+        /**
+         * Create a DhcpLeaseRepository for the server.
+         * @param servingParams Parameters used to serve DHCP requests.
+         * @param log Log to be used by the repository.
+         * @param clock Clock that the repository must use to track time.
+         */
         DhcpLeaseRepository makeLeaseRepository(@NonNull DhcpServingParams servingParams,
                 @NonNull SharedLog log, @NonNull Clock clock);
+
+        /**
+         * Create a packet listener that will send packets to be processed.
+         */
         DhcpPacketListener makePacketListener();
+
+        /**
+         * Create a clock that the server will use to track time.
+         */
         Clock makeClock();
+
+        /**
+         * Add an entry to the ARP cache table.
+         * @param fd Datagram socket file descriptor that must use the new entry.
+         */
         void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
                 @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException;
     }
@@ -134,7 +171,7 @@
             return new DhcpLeaseRepository(
                     DhcpServingParams.makeIpPrefix(servingParams.serverAddr),
                     servingParams.excludedAddrs,
-                    servingParams.dhcpLeaseTimeSecs*1000, log.forSubComponent(REPO_TAG), clock);
+                    servingParams.dhcpLeaseTimeSecs * 1000, log.forSubComponent(REPO_TAG), clock);
         }
 
         @Override
@@ -212,7 +249,7 @@
     }
 
     private class ServerHandler extends Handler {
-        public ServerHandler(@NonNull Looper looper) {
+        ServerHandler(@NonNull Looper looper) {
             super(looper);
         }
 
@@ -496,22 +533,24 @@
     }
 
     private class PacketListener extends DhcpPacketListener {
-        public PacketListener() {
+        PacketListener() {
             super(mHandler);
         }
 
         @Override
-        protected void onReceive(DhcpPacket packet, Inet4Address srcAddr, int srcPort) {
+        protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr,
+                int srcPort) {
             processPacket(packet, srcPort);
         }
 
         @Override
-        protected void logError(String msg, Exception e) {
+        protected void logError(@NonNull String msg, Exception e) {
             mLog.e("Error receiving packet: " + msg, e);
         }
 
         @Override
-        protected void logParseError(byte[] packet, int length, DhcpPacket.ParseException e) {
+        protected void logParseError(@NonNull byte[] packet, int length,
+                @NonNull DhcpPacket.ParseException e) {
             mLog.e("Error parsing packet", e);
         }
 
diff --git a/services/net/java/android/net/dhcp/DhcpServingParams.java b/services/net/java/android/net/dhcp/DhcpServingParams.java
index df15ba1..2780814a 100644
--- a/services/net/java/android/net/dhcp/DhcpServingParams.java
+++ b/services/net/java/android/net/dhcp/DhcpServingParams.java
@@ -17,6 +17,7 @@
 package android.net.dhcp;
 
 import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
+import static android.net.NetworkUtils.intToInet4AddressHTH;
 import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
 import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
 import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
@@ -24,6 +25,7 @@
 import static java.lang.Integer.toUnsignedLong;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
@@ -103,6 +105,37 @@
         this.metered = metered;
     }
 
+    /**
+     * Create parameters from a stable AIDL-compatible parcel.
+     */
+    public static DhcpServingParams fromParcelableObject(@NonNull DhcpServingParamsParcel parcel)
+            throws InvalidParameterException {
+        final LinkAddress serverAddr = new LinkAddress(
+                intToInet4AddressHTH(parcel.serverAddr),
+                parcel.serverAddrPrefixLength);
+        return new Builder()
+                .setServerAddr(serverAddr)
+                .setDefaultRouters(toInet4AddressSet(parcel.defaultRouters))
+                .setDnsServers(toInet4AddressSet(parcel.dnsServers))
+                .setExcludedAddrs(toInet4AddressSet(parcel.excludedAddrs))
+                .setDhcpLeaseTimeSecs(parcel.dhcpLeaseTimeSecs)
+                .setLinkMtu(parcel.linkMtu)
+                .setMetered(parcel.metered)
+                .build();
+    }
+
+    private static Set<Inet4Address> toInet4AddressSet(@Nullable int[] addrs) {
+        if (addrs == null) {
+            return new HashSet<>(0);
+        }
+
+        final HashSet<Inet4Address> res = new HashSet<>();
+        for (int addr : addrs) {
+            res.add(intToInet4AddressHTH(addr));
+        }
+        return res;
+    }
+
     @NonNull
     public Inet4Address getServerInet4Addr() {
         return (Inet4Address) serverAddr.getAddress();
@@ -134,13 +167,13 @@
      * of the parameters.
      */
     public static class Builder {
-        private LinkAddress serverAddr;
-        private Set<Inet4Address> defaultRouters;
-        private Set<Inet4Address> dnsServers;
-        private Set<Inet4Address> excludedAddrs;
-        private long dhcpLeaseTimeSecs;
-        private int linkMtu = MTU_UNSET;
-        private boolean metered;
+        private LinkAddress mServerAddr;
+        private Set<Inet4Address> mDefaultRouters;
+        private Set<Inet4Address> mDnsServers;
+        private Set<Inet4Address> mExcludedAddrs;
+        private long mDhcpLeaseTimeSecs;
+        private int mLinkMtu = MTU_UNSET;
+        private boolean mMetered;
 
         /**
          * Set the server address and served prefix for the DHCP server.
@@ -148,7 +181,7 @@
          * <p>This parameter is required.
          */
         public Builder setServerAddr(@NonNull LinkAddress serverAddr) {
-            this.serverAddr = serverAddr;
+            this.mServerAddr = serverAddr;
             return this;
         }
 
@@ -159,7 +192,7 @@
          * always be set explicitly before building the {@link DhcpServingParams}.
          */
         public Builder setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
-            this.defaultRouters = defaultRouters;
+            this.mDefaultRouters = defaultRouters;
             return this;
         }
 
@@ -189,7 +222,7 @@
          * {@link DhcpServingParams}.
          */
         public Builder setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
-            this.dnsServers = dnsServers;
+            this.mDnsServers = dnsServers;
             return this;
         }
 
@@ -219,7 +252,7 @@
          * and do not need to be set here.
          */
         public Builder setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
-            this.excludedAddrs = excludedAddrs;
+            this.mExcludedAddrs = excludedAddrs;
             return this;
         }
 
@@ -239,7 +272,7 @@
          * <p>This parameter is required.
          */
         public Builder setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
-            this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
+            this.mDhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
             return this;
         }
 
@@ -250,7 +283,7 @@
          * is optional and defaults to {@link #MTU_UNSET}.
          */
         public Builder setLinkMtu(int linkMtu) {
-            this.linkMtu = linkMtu;
+            this.mLinkMtu = linkMtu;
             return this;
         }
 
@@ -260,7 +293,7 @@
          * <p>If not set, the default value is false.
          */
         public Builder setMetered(boolean metered) {
-            this.metered = metered;
+            this.mMetered = metered;
             return this;
         }
 
@@ -274,54 +307,57 @@
          */
         @NonNull
         public DhcpServingParams build() throws InvalidParameterException {
-            if (serverAddr == null) {
+            if (mServerAddr == null) {
                 throw new InvalidParameterException("Missing serverAddr");
             }
-            if (defaultRouters == null) {
+            if (mDefaultRouters == null) {
                 throw new InvalidParameterException("Missing defaultRouters");
             }
-            if (dnsServers == null) {
+            if (mDnsServers == null) {
                 // Empty set is OK, but enforce explicitly setting it
                 throw new InvalidParameterException("Missing dnsServers");
             }
-            if (dhcpLeaseTimeSecs <= 0 || dhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) {
-                throw new InvalidParameterException("Invalid lease time: " + dhcpLeaseTimeSecs);
+            if (mDhcpLeaseTimeSecs <= 0 || mDhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) {
+                throw new InvalidParameterException("Invalid lease time: " + mDhcpLeaseTimeSecs);
             }
-            if (linkMtu != MTU_UNSET && (linkMtu < IPV4_MIN_MTU || linkMtu > IPV4_MAX_MTU)) {
-                throw new InvalidParameterException("Invalid link MTU: " + linkMtu);
+            if (mLinkMtu != MTU_UNSET && (mLinkMtu < IPV4_MIN_MTU || mLinkMtu > IPV4_MAX_MTU)) {
+                throw new InvalidParameterException("Invalid link MTU: " + mLinkMtu);
             }
-            if (!serverAddr.isIPv4()) {
+            if (!mServerAddr.isIPv4()) {
                 throw new InvalidParameterException("serverAddr must be IPv4");
             }
-            if (serverAddr.getPrefixLength() < MIN_PREFIX_LENGTH
-                    || serverAddr.getPrefixLength() > MAX_PREFIX_LENGTH) {
+            if (mServerAddr.getPrefixLength() < MIN_PREFIX_LENGTH
+                    || mServerAddr.getPrefixLength() > MAX_PREFIX_LENGTH) {
                 throw new InvalidParameterException("Prefix length is not in supported range");
             }
 
-            final IpPrefix prefix = makeIpPrefix(serverAddr);
-            for (Inet4Address addr : defaultRouters) {
+            final IpPrefix prefix = makeIpPrefix(mServerAddr);
+            for (Inet4Address addr : mDefaultRouters) {
                 if (!prefix.contains(addr)) {
                     throw new InvalidParameterException(String.format(
-                            "Default router %s is not in server prefix %s", addr, serverAddr));
+                            "Default router %s is not in server prefix %s", addr, mServerAddr));
                 }
             }
 
             final Set<Inet4Address> excl = new HashSet<>();
-            if (excludedAddrs != null) {
-                excl.addAll(excludedAddrs);
+            if (mExcludedAddrs != null) {
+                excl.addAll(mExcludedAddrs);
             }
-            excl.add((Inet4Address) serverAddr.getAddress());
-            excl.addAll(defaultRouters);
-            excl.addAll(dnsServers);
+            excl.add((Inet4Address) mServerAddr.getAddress());
+            excl.addAll(mDefaultRouters);
+            excl.addAll(mDnsServers);
 
-            return new DhcpServingParams(serverAddr,
-                    Collections.unmodifiableSet(new HashSet<>(defaultRouters)),
-                    Collections.unmodifiableSet(new HashSet<>(dnsServers)),
+            return new DhcpServingParams(mServerAddr,
+                    Collections.unmodifiableSet(new HashSet<>(mDefaultRouters)),
+                    Collections.unmodifiableSet(new HashSet<>(mDnsServers)),
                     Collections.unmodifiableSet(excl),
-                    dhcpLeaseTimeSecs, linkMtu, metered);
+                    mDhcpLeaseTimeSecs, mLinkMtu, mMetered);
         }
     }
 
+    /**
+     * Utility method to create an IpPrefix with the address and prefix length of a LinkAddress.
+     */
     @NonNull
     static IpPrefix makeIpPrefix(@NonNull LinkAddress addr) {
         return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
diff --git a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
new file mode 100644
index 0000000..f068c3a
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import static android.net.NetworkUtils.inet4AddressToIntHTH;
+
+import android.annotation.NonNull;
+import android.net.LinkAddress;
+
+import com.google.android.collect.Sets;
+
+import java.net.Inet4Address;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Subclass of {@link DhcpServingParamsParcel} with additional utility methods for building.
+ *
+ * <p>This utility class does not check for validity of the parameters: invalid parameters are
+ * reported by the receiving module when unparceling the parcel.
+ *
+ * @see DhcpServingParams
+ * @hide
+ */
+public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel {
+    public static final int MTU_UNSET = 0;
+
+    /**
+     * Set the server address and served prefix for the DHCP server.
+     *
+     * <p>This parameter is required.
+     */
+    public DhcpServingParamsParcelExt setServerAddr(@NonNull LinkAddress serverAddr) {
+        this.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress());
+        this.serverAddrPrefixLength = serverAddr.getPrefixLength();
+        return this;
+    }
+
+    /**
+     * Set the default routers to be advertised to DHCP clients.
+     *
+     * <p>Each router must be inside the served prefix. This may be an empty set, but it must
+     * always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
+        this.defaultRouters = toIntArray(defaultRouters);
+        return this;
+    }
+
+    /**
+     * Set the default routers to be advertised to DHCP clients.
+     *
+     * <p>Each router must be inside the served prefix. This may be an empty list of routers,
+     * but it must always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
+        return setDefaultRouters(Sets.newArraySet(defaultRouters));
+    }
+
+    /**
+     * Convenience method to build the parameters with no default router.
+     *
+     * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address.
+     */
+    public DhcpServingParamsParcelExt setNoDefaultRouter() {
+        return setDefaultRouters();
+    }
+
+    /**
+     * Set the DNS servers to be advertised to DHCP clients.
+     *
+     * <p>This may be an empty set, but it must always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
+        this.dnsServers = toIntArray(dnsServers);
+        return this;
+    }
+
+    /**
+     * Set the DNS servers to be advertised to DHCP clients.
+     *
+     * <p>This may be an empty list of servers, but it must always be set explicitly.
+     */
+    public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) {
+        return setDnsServers(Sets.newArraySet(dnsServers));
+    }
+
+    /**
+     * Convenience method to build the parameters with no DNS server.
+     *
+     * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address.
+     */
+    public DhcpServingParamsParcelExt setNoDnsServer() {
+        return setDnsServers();
+    }
+
+    /**
+     * Set excluded addresses that the DHCP server is not allowed to assign to clients.
+     *
+     * <p>This parameter is optional. DNS servers and default routers are always excluded
+     * and do not need to be set here.
+     */
+    public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
+        this.excludedAddrs = toIntArray(excludedAddrs);
+        return this;
+    }
+
+    /**
+     * Set excluded addresses that the DHCP server is not allowed to assign to clients.
+     *
+     * <p>This parameter is optional. DNS servers and default routers are always excluded
+     * and do not need to be set here.
+     */
+    public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
+        return setExcludedAddrs(Sets.newArraySet(excludedAddrs));
+    }
+
+    /**
+     * Set the lease time for leases assigned by the DHCP server.
+     *
+     * <p>This parameter is required.
+     */
+    public DhcpServingParamsParcelExt setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
+        this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
+        return this;
+    }
+
+    /**
+     * Set the link MTU to be advertised to DHCP clients.
+     *
+     * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter
+     * is optional and defaults to {@link #MTU_UNSET}.
+     */
+    public DhcpServingParamsParcelExt setLinkMtu(int linkMtu) {
+        this.linkMtu = linkMtu;
+        return this;
+    }
+
+    /**
+     * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option.
+     *
+     * <p>If not set, the default value is false.
+     */
+    public DhcpServingParamsParcelExt setMetered(boolean metered) {
+        this.metered = metered;
+        return this;
+    }
+
+    private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
+        int[] res = new int[addrs.size()];
+        int i = 0;
+        for (Inet4Address addr : addrs) {
+            res[i] = inet4AddressToIntHTH(addr);
+            i++;
+        }
+        return res;
+    }
+}
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
index 5a73a4e..74bc147 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -35,8 +35,8 @@
  * @hide
  */
 public class SharedLog {
-    private final static int DEFAULT_MAX_RECORDS = 500;
-    private final static String COMPONENT_DELIMITER = ".";
+    private static final int DEFAULT_MAX_RECORDS = 500;
+    private static final String COMPONENT_DELIMITER = ".";
 
     private enum Category {
         NONE,
@@ -69,6 +69,9 @@
         mComponent = component;
     }
 
+    /**
+     * Create a SharedLog based on this log with an additional component prefix on each logged line.
+     */
     public SharedLog forSubComponent(String component) {
         if (!isRootLogInstance()) {
             component = mComponent + COMPONENT_DELIMITER + component;
@@ -76,6 +79,11 @@
         return new SharedLog(mLocalLog, mTag, component);
     }
 
+    /**
+     * Dump the contents of this log.
+     *
+     * <p>This method may be called on any thread.
+     */
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         mLocalLog.readOnlyLocalLog().dump(fd, writer, args);
     }
@@ -84,10 +92,21 @@
     // Methods that both log an entry and emit it to the system log.
     //////
 
+    /**
+     * Log an error due to an exception. This does not include the exception stacktrace.
+     *
+     * <p>The log entry will be also added to the system log.
+     * @see #e(String, Throwable)
+     */
     public void e(Exception e) {
         Log.e(mTag, record(Category.ERROR, e.toString()));
     }
 
+    /**
+     * Log an error message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
     public void e(String msg) {
         Log.e(mTag, record(Category.ERROR, msg));
     }
@@ -96,7 +115,7 @@
      * Log an error due to an exception, with the exception stacktrace if provided.
      *
      * <p>The error and exception message appear in the shared log, but the stacktrace is only
-     * logged in general log output (logcat).
+     * logged in general log output (logcat). The log entry will be also added to the system log.
      */
     public void e(@NonNull String msg, @Nullable Throwable exception) {
         if (exception == null) {
@@ -106,10 +125,20 @@
         Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception);
     }
 
+    /**
+     * Log an informational message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
     public void i(String msg) {
         Log.i(mTag, record(Category.NONE, msg));
     }
 
+    /**
+     * Log a warning message.
+     *
+     * <p>The log entry will be also added to the system log.
+     */
     public void w(String msg) {
         Log.w(mTag, record(Category.WARN, msg));
     }
@@ -118,14 +147,30 @@
     // Methods that only log an entry (and do NOT emit to the system log).
     //////
 
+    /**
+     * Log a general message to be only included in the in-memory log.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     */
     public void log(String msg) {
         record(Category.NONE, msg);
     }
 
+    /**
+     * Log a general, formatted message to be only included in the in-memory log.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     * @see String#format(String, Object...)
+     */
     public void logf(String fmt, Object... args) {
         log(String.format(fmt, args));
     }
 
+    /**
+     * Log a message with MARK level.
+     *
+     * <p>The log entry will *not* be added to the system log.
+     */
     public void mark(String msg) {
         record(Category.MARK, msg);
     }
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
index b8723c5..b682def 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.robolectric.Shadows.shadowOf;
 import static org.testng.Assert.expectThrows;
 
@@ -192,6 +193,19 @@
 
     /** Test that the service unregisters users when stopped. */
     @Test
+    public void testStopServiceForUser_forRegisteredUser_tearsDownCorrectUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+
+        backupManagerService.stopServiceForUser(mUserOneId);
+
+        verify(mUserOneService).tearDownService();
+        verifyNoMoreInteractions(mUserTwoService);
+    }
+
+    /** Test that the service unregisters users when stopped. */
+    @Test
     public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception {
         BackupManagerService backupManagerService = createService();
 
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index bb60c27..8d5c301 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -829,7 +829,7 @@
     public void testRequestBackup_whenNotProvisioned() throws Exception {
         mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
-        backupManagerService.setProvisioned(false);
+        backupManagerService.setSetupComplete(false);
 
         int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
 
@@ -848,7 +848,7 @@
         setUpCurrentTransport(mTransportManager, mTransport.unregistered());
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
+        backupManagerService.setSetupComplete(true);
 
         int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
 
@@ -868,7 +868,7 @@
         setUpCurrentTransport(mTransportManager, mTransport);
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
+        backupManagerService.setSetupComplete(true);
         // Haven't set PACKAGE_1 as eligible
 
         int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
@@ -965,14 +965,13 @@
     private UserBackupManagerService createBackupManagerServiceForRequestBackup() {
         UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
         backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
+        backupManagerService.setSetupComplete(true);
         return backupManagerService;
     }
 
     /**
-     * Test verifying that {@link UserBackupManagerService#createAndInitializeService(Context,
-     * Trampoline, HandlerThread, File, File, TransportManager)} posts a transport registration task
-     * to the backup thread.
+     * Test verifying that creating a new instance posts a transport registration task to the backup
+     * thread.
      */
     @Test
     public void testCreateAndInitializeService_postRegisterTransports() {
@@ -992,9 +991,8 @@
     }
 
     /**
-     * Test verifying that {@link UserBackupManagerService#createAndInitializeService(Context,
-     * Trampoline, HandlerThread, File, File, TransportManager)} does not directly register
-     * transports on the main thread.
+     * Test verifying that creating a new instance does not directly register transports on the main
+     * thread.
      */
     @Test
     public void testCreateAndInitializeService_doesNotRegisterTransportsSynchronously() {
@@ -1013,11 +1011,7 @@
         verify(mTransportManager, never()).registerTransports();
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullContext_throws() {
         expectThrows(
@@ -1033,11 +1027,7 @@
                                 mTransportManager));
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullTrampoline_throws() {
         expectThrows(
@@ -1053,11 +1043,7 @@
                                 mTransportManager));
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullBackupThread_throws() {
         expectThrows(
@@ -1073,11 +1059,7 @@
                                 mTransportManager));
     }
 
-    /**
-     * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(Context, Trampoline, HandlerThread, File,
-     * File, TransportManager)}.
-     */
+    /** Test checking non-null argument on instance creation. */
     @Test
     public void testCreateAndInitializeService_withNullStateDir_throws() {
         expectThrows(
diff --git a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
new file mode 100644
index 0000000..b56b5e6
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+
+import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.testing.BackupManagerServiceTestUtils;
+import com.android.server.backup.testing.TestUtils;
+
+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 java.io.File;
+
+/**
+ * Tests verifying the interaction between {@link SetupObserver} and {@link
+ * UserBackupManagerService}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class SetupObserverTest {
+    private static final String TAG = "SetupObserverTest";
+    private static final int USER_ID = 10;
+
+    @Mock private TransportManager mTransportManager;
+
+    private Context mContext;
+    private UserBackupManagerService mUserBackupManagerService;
+    private HandlerThread mHandlerThread;
+
+    /** Setup state. */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mHandlerThread = BackupManagerServiceTestUtils.startSilentBackupThread(TAG);
+        mUserBackupManagerService =
+                BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
+                        USER_ID,
+                        mContext,
+                        mHandlerThread,
+                        new File(mContext.getDataDir(), "test1"),
+                        new File(mContext.getDataDir(), "test2"),
+                        mTransportManager);
+    }
+
+    /** Test observer handles changes from not setup -> setup correctly. */
+    @Test
+    public void testOnChange_whenNewlySetup_updatesState() throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(false);
+        changeSetupCompleteSettingForUser(true, USER_ID);
+
+        setupObserver.onChange(true);
+
+        assertThat(mUserBackupManagerService.isSetupComplete()).isTrue();
+    }
+
+    /** Test observer handles changes from setup -> not setup correctly. */
+    @Test
+    public void testOnChange_whenPreviouslySetup_doesNotUpdateState() throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(true);
+        changeSetupCompleteSettingForUser(false, USER_ID);
+
+        setupObserver.onChange(true);
+
+        assertThat(mUserBackupManagerService.isSetupComplete()).isTrue();
+    }
+
+    /** Test observer handles changes from not setup -> not setup correctly. */
+    @Test
+    public void testOnChange_whenNotPreviouslySetup_doesNotUpdateStateIfNoChange()
+            throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(false);
+        changeSetupCompleteSettingForUser(false, USER_ID);
+
+        setupObserver.onChange(true);
+
+        assertThat(mUserBackupManagerService.isSetupComplete()).isFalse();
+    }
+
+    /** Test observer handles changes from not setup -> setup correctly. */
+    @Test
+    public void testOnChange_whenNewlySetup_schedulesBackup() throws Exception {
+        SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
+        mUserBackupManagerService.setSetupComplete(false);
+        changeSetupCompleteSettingForUser(true, USER_ID);
+        // Setup conditions for a full backup job to be scheduled.
+        mUserBackupManagerService.setEnabled(true);
+        mUserBackupManagerService.enqueueFullBackup("testPackage", /* lastBackedUp */ 0);
+        // Clear the handler of all pending tasks. This is to prevent the below assertion on the
+        // handler from encountering false positives due to other tasks being scheduled as part of
+        // setup work.
+        TestUtils.runToEndOfTasks(mHandlerThread.getLooper());
+
+        setupObserver.onChange(true);
+
+        assertThat(KeyValueBackupJob.isScheduled()).isTrue();
+        // Verifies that the full backup job is scheduled. The job is scheduled via a posted message
+        // on the backup handler so we verify that a message exists.
+        assertThat(mUserBackupManagerService.getBackupHandler().hasMessagesOrCallbacks()).isTrue();
+    }
+
+    private void changeSetupCompleteSettingForUser(boolean value, int userId) {
+        Settings.Secure.putIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE,
+                value ? 1 : 0,
+                userId);
+    }
+}
diff --git a/core/java/android/hardware/display/BrightnessCorrection.aidl b/services/startop/Android.bp
similarity index 79%
copy from core/java/android/hardware/display/BrightnessCorrection.aidl
copy to services/startop/Android.bp
index 3abe29c..093b4ec 100644
--- a/core/java/android/hardware/display/BrightnessCorrection.aidl
+++ b/services/startop/Android.bp
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
-package android.hardware.display;
+java_library_static {
+    name: "services.startop",
 
-parcelable BrightnessCorrection;
+    static_libs: [
+        // frameworks/base/startop/iorap
+        "services.startop.iorap",
+    ],
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 767eb60..6a10ff4 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -29,6 +29,8 @@
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.util.DebugUtils.valueToString;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.android.server.am.ActivityManagerInternalTest.CustomThread;
 import static com.android.server.am.ActivityManagerService.DISPATCH_UIDS_CHANGED_UI_MSG;
 import static com.android.server.am.ActivityManagerService.Injector;
@@ -69,6 +71,9 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.AppOpsService;
+import com.android.server.am.ProcessList.IsolatedUidRange;
+import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
+import com.android.server.wm.ActivityTaskManagerService;
 
 import org.junit.After;
 import org.junit.Before;
@@ -109,7 +114,7 @@
         UidRecord.CHANGE_ACTIVE
     };
 
-    @Mock private Context mContext;
+    private Context mContext = getInstrumentation().getTargetContext();
     @Mock private AppOpsService mAppOpsService;
     @Mock private PackageManager mPackageManager;
 
@@ -128,8 +133,8 @@
         mInjector = new TestInjector();
         mAms = new ActivityManagerService(mInjector);
         mAms.mWaitForNetworkTimeoutMs = 2000;
-
-        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+        mAms.mActivityTaskManager.initialize(null, null, mHandler.getLooper());
     }
 
     @After
@@ -291,6 +296,113 @@
         }
     }
 
+    private void validateAppZygoteIsolatedUidRange(IsolatedUidRange uidRange) {
+        assertNotNull(uidRange);
+        assertTrue(uidRange.mFirstUid >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
+                && uidRange.mFirstUid <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
+        assertTrue(uidRange.mLastUid >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
+                && uidRange.mLastUid <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
+        assertTrue(uidRange.mLastUid > uidRange.mFirstUid
+                && ((uidRange.mLastUid - uidRange.mFirstUid + 1)
+                     == Process.NUM_UIDS_PER_APP_ZYGOTE));
+    }
+
+    private void verifyUidRangesNoOverlap(IsolatedUidRange uidRange1, IsolatedUidRange uidRange2) {
+        IsolatedUidRange lowRange = uidRange1.mFirstUid <= uidRange2.mFirstUid ? uidRange1 : uidRange2;
+        IsolatedUidRange highRange = lowRange == uidRange1  ? uidRange2 : uidRange1;
+
+        assertTrue(highRange.mFirstUid > lowRange.mLastUid);
+    }
+
+    @Test
+    public void testIsolatedUidRangeAllocator() {
+        final IsolatedUidRangeAllocator allocator = mAms.mProcessList.mAppIsolatedUidRangeAllocator;
+
+        // Create initial range
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.processName = "com.android.test.app";
+        appInfo.uid = 10000;
+        final IsolatedUidRange range = allocator.getOrCreateIsolatedUidRangeLocked(appInfo);
+        validateAppZygoteIsolatedUidRange(range);
+        verifyIsolatedUidAllocator(range);
+
+        // Create a second range
+        ApplicationInfo appInfo2 = new ApplicationInfo();
+        appInfo2.processName = "com.android.test.app2";
+        appInfo2.uid = 10001;
+        IsolatedUidRange range2 = allocator.getOrCreateIsolatedUidRangeLocked(appInfo2);
+        validateAppZygoteIsolatedUidRange(range2);
+        verifyIsolatedUidAllocator(range2);
+
+        // Verify ranges don't overlap
+        verifyUidRangesNoOverlap(range, range2);
+
+        // Free range, reallocate and verify
+        allocator.freeUidRangeLocked(appInfo2);
+        range2 = allocator.getOrCreateIsolatedUidRangeLocked(appInfo2);
+        validateAppZygoteIsolatedUidRange(range2);
+        verifyUidRangesNoOverlap(range, range2);
+        verifyIsolatedUidAllocator(range2);
+
+        // Free both, then try to allocate the maximum number of UID ranges
+        allocator.freeUidRangeLocked(appInfo);
+        allocator.freeUidRangeLocked(appInfo2);
+
+        int maxNumUidRanges = (Process.LAST_APP_ZYGOTE_ISOLATED_UID
+                - Process.FIRST_APP_ZYGOTE_ISOLATED_UID + 1) / Process.NUM_UIDS_PER_APP_ZYGOTE;
+        for (int i = 0; i < maxNumUidRanges; i++) {
+            appInfo = new ApplicationInfo();
+            appInfo.uid = 10000 + i;
+            appInfo.processName = "com.android.test.app" + Integer.toString(i);
+            IsolatedUidRange uidRange = allocator.getOrCreateIsolatedUidRangeLocked(appInfo);
+            validateAppZygoteIsolatedUidRange(uidRange);
+            verifyIsolatedUidAllocator(uidRange);
+        }
+
+        // Try to allocate another one and make sure it fails
+        appInfo = new ApplicationInfo();
+        appInfo.uid = 9000;
+        appInfo.processName = "com.android.test.app.failed";
+        IsolatedUidRange failedRange = allocator.getOrCreateIsolatedUidRangeLocked(appInfo);
+
+        assertNull(failedRange);
+    }
+
+    public void verifyIsolatedUid(ProcessList.IsolatedUidRange range, int uid) {
+        assertTrue(uid >= range.mFirstUid && uid <= range.mLastUid);
+    }
+
+    public void verifyIsolatedUidAllocator(ProcessList.IsolatedUidRange range) {
+        int uid = range.allocateIsolatedUidLocked(0);
+        verifyIsolatedUid(range, uid);
+
+        int uid2 = range.allocateIsolatedUidLocked(0);
+        verifyIsolatedUid(range, uid2);
+        assertTrue(uid2 != uid);
+
+        // Free both
+        range.freeIsolatedUidLocked(uid);
+        range.freeIsolatedUidLocked(uid2);
+
+        // Allocate the entire range
+        for (int i = 0; i < (range.mLastUid - range.mFirstUid + 1); ++i) {
+            uid = range.allocateIsolatedUidLocked(0);
+            verifyIsolatedUid(range, uid);
+        }
+
+        // Ensure the next one fails
+        uid = range.allocateIsolatedUidLocked(0);
+        assertEquals(uid, -1);
+    }
+
+    @Test
+    public void testGlobalIsolatedUidAllocator() {
+        final IsolatedUidRange globalUidRange = mAms.mProcessList.mGlobalIsolatedUids;
+        assertEquals(globalUidRange.mFirstUid, Process.FIRST_ISOLATED_UID);
+        assertEquals(globalUidRange.mLastUid, Process.LAST_ISOLATED_UID);
+        verifyIsolatedUidAllocator(globalUidRange);
+    }
+
     @Test
     public void testBlockStateForUid() {
         final UidRecord uidRec = new UidRecord(TEST_UID, null /* atmInternal */);
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index 0889265..d4bb636 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -193,6 +193,7 @@
                 false /* serialized */,
                 false /* sticky */,
                 false /* initialSticky */,
-                userId);
+                userId,
+                false /* allowBackgroundActivityStarts */);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 9e3a0ea..147a691 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -20,18 +20,17 @@
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
-import static com.google.common.truth.Truth.assertThat;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.media.AudioManager;
 import android.os.Looper;
 import android.os.SystemProperties;
 import android.os.test.TestLooper;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
+
 import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
 
 import org.junit.Before;
@@ -123,6 +122,11 @@
 
                     @Override
                     void wakeUp() {}
+
+                    @Override
+                    boolean isControlEnabled() {
+                        return true;
+                    }
                 };
 
         mMyLooper = mTestLooper.getLooper();
@@ -144,7 +148,7 @@
     }
 
     @Test
-    public void handleGiveAudioStatus_volume_10_mute_true() {
+    public void handleGiveAudioStatus_volume_10_mute_true() throws Exception {
         mMusicVolume = 10;
         mMusicMute = true;
         mMusicMaxVolume = 20;
@@ -161,7 +165,7 @@
     }
 
     @Test
-    public void handleGiveSystemAudioModeStatus_originalOff() {
+    public void handleGiveSystemAudioModeStatus_originalOff() throws Exception {
         HdmiCecMessage expectedMessage =
                 HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
         HdmiCecMessage messageGive =
@@ -228,7 +232,7 @@
     }
 
     @Test
-    public void handleSetSystemAudioMode_setOn_orignalOff() {
+    public void handleSetSystemAudioMode_setOn_orignalOff() throws Exception {
         mMusicMute = true;
         HdmiCecMessage messageSet =
                 HdmiCecMessageBuilder.buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
@@ -256,7 +260,7 @@
 
     @Ignore("b/80297700")
     @Test
-    public void handleSystemAudioModeRequest_turnOffByTv() {
+    public void handleSystemAudioModeRequest_turnOffByTv() throws Exception {
         assertThat(mMusicMute).isFalse();
         // Check if feature correctly turned off
         HdmiCecMessage messageGive =
@@ -282,7 +286,7 @@
 
     @Ignore("b/80297700")
     @Test
-    public void onStandbyAudioSystem_currentSystemAudioControlOn() {
+    public void onStandbyAudioSystem_currentSystemAudioControlOn() throws Exception {
         // Set system audio control on first
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
         // Check if standby correctly turns off the feature
@@ -296,7 +300,7 @@
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_alwaysOn() {
+    public void systemAudioControlOnPowerOn_alwaysOn() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
@@ -307,7 +311,7 @@
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_neverOn() {
+    public void systemAudioControlOnPowerOn_neverOn() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
@@ -318,7 +322,7 @@
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_useLastState_off() {
+    public void systemAudioControlOnPowerOn_useLastState_off() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
@@ -329,7 +333,7 @@
     }
 
     @Test
-    public void systemAudioControlOnPowerOn_useLastState_on() {
+    public void systemAudioControlOnPowerOn_useLastState_on() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
                 Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
@@ -340,7 +344,7 @@
     }
 
     @Test
-    public void handleActiveSource_updateActiveSource() {
+    public void handleActiveSource_updateActiveSource() throws Exception {
         HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
         ActiveSource expectedActiveSource = new ActiveSource(ADDR_TV, 0x0000);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message))
@@ -351,7 +355,7 @@
     }
 
     @Test
-    public void terminateSystemAudioMode_systemAudioModeOff() {
+    public void terminateSystemAudioMode_systemAudioModeOff() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
         mMusicMute = false;
@@ -366,7 +370,7 @@
 
     @Ignore("b/80297700")
     @Test
-    public void terminateSystemAudioMode_systemAudioModeOn() {
+    public void terminateSystemAudioMode_systemAudioModeOn() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
         mMusicMute = false;
@@ -381,7 +385,7 @@
     }
 
     @Test
-    public void isPhysicalAddressMeOrBelow_isMe() {
+    public void isPhysicalAddressMeOrBelow_isMe() throws Exception {
         int targetPhysicalAddress = 0x1000;
         mNativeWrapper.setPhysicalAddress(0x1000);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
@@ -389,7 +393,7 @@
     }
 
     @Test
-    public void isPhysicalAddressMeOrBelow_isBelow() {
+    public void isPhysicalAddressMeOrBelow_isBelow() throws Exception {
         int targetPhysicalAddress = 0x1100;
         mNativeWrapper.setPhysicalAddress(0x1000);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
@@ -397,7 +401,7 @@
     }
 
     @Test
-    public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() {
+    public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() throws Exception {
         int targetPhysicalAddress = 0x3000;
         mNativeWrapper.setPhysicalAddress(0x2000);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
@@ -420,7 +424,7 @@
     }
 
     @Test
-    public void handleRequestArcInitiate_isNotDirectConnectedToTv() {
+    public void handleRequestArcInitiate_isNotDirectConnectedToTv() throws Exception {
         HdmiCecMessage message = HdmiCecMessageBuilder
             .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
@@ -437,7 +441,7 @@
     }
 
     @Test
-    public void handleRequestArcInitiate_startArcInitiationActionFromAvr() {
+    public void handleRequestArcInitiate_startArcInitiationActionFromAvr() throws Exception {
         HdmiCecMessage message = HdmiCecMessageBuilder
             .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
         mNativeWrapper.setPhysicalAddress(0x1000);
@@ -452,7 +456,7 @@
     }
 
     @Test
-    public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() {
+    public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() throws Exception {
         mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
 
@@ -469,7 +473,7 @@
     }
 
     @Test
-    public void handleRequestArcTerminate_arcIsNotOn() {
+    public void handleRequestArcTerminate_arcIsNotOn() throws Exception {
         assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
         HdmiCecMessage message = HdmiCecMessageBuilder
             .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
@@ -486,7 +490,7 @@
     }
 
     @Test
-    public void handleRequestArcInit_arcIsNotSupported() {
+    public void handleRequestArcInit_arcIsNotSupported() throws Exception {
         HdmiCecMessage message = HdmiCecMessageBuilder
             .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
@@ -501,4 +505,15 @@
         mTestLooper.dispatchAll();
         assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
+
+    @Test
+    public void onStandby_setAutoDeviceOff_true() throws Exception {
+        HdmiCecMessage expectedMessage =
+                HdmiCecMessageBuilder.buildStandby(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST);
+        mHdmiCecLocalDeviceAudioSystem.setAutoDeviceOff(true);
+        mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF);
+
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 8496a96..b348aee 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -699,10 +699,52 @@
         assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
     }
 
+    /** Verify the timeout message is delivered at the right time after past usage was reported */
+    @Test
+    public void testAppUsageObserver_PastUsage() throws Exception {
+        setTime(10_000L);
+        addAppUsageObserver(OBS_ID1, GROUP1, 6_000L);
+        setTime(20_000L);
+        startPastUsage(PKG_SOC1, 5_000);
+        setTime(21_000L);
+        assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Verify that the observer was removed
+        assertFalse(hasAppUsageObserver(UID, OBS_ID1));
+    }
+
+    /**
+     * Verify the timeout message is delivered at the right time after past usage was reported
+     * that overlaps with already known usage
+     */
+    @Test
+    public void testAppUsageObserver_PastUsageOverlap() throws Exception {
+        setTime(0L);
+        addAppUsageObserver(OBS_ID1, GROUP1, 20_000L);
+        setTime(10_000L);
+        startUsage(PKG_SOC1);
+        setTime(20_000L);
+        stopUsage(PKG_SOC1);
+        setTime(25_000L);
+        startPastUsage(PKG_SOC1, 9_000);
+        setTime(26_000L);
+        // the 4 seconds of overlapped usage should not be counted
+        assertFalse(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+        setTime(30_000L);
+        assertTrue(mLimitReachedLatch.await(4_000L, TimeUnit.MILLISECONDS));
+        stopUsage(PKG_SOC1);
+        // Verify that the observer was removed
+        assertFalse(hasAppUsageObserver(UID, OBS_ID1));
+    }
+
     private void startUsage(String packageName) {
         mController.noteUsageStart(packageName, USER_ID);
     }
 
+    private void startPastUsage(String packageName, int timeAgo) {
+        mController.noteUsageStart(packageName, USER_ID, timeAgo);
+    }
+
     private void stopUsage(String packageName) {
         mController.noteUsageStop(packageName, USER_ID);
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 9d84751..20c5f5d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -60,6 +60,7 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -114,7 +115,7 @@
     private static final Uri CUSTOM_SOUND = Settings.System.DEFAULT_ALARM_ALERT_URI;
     private static final AudioAttributes CUSTOM_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
-            .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+            .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
             .build();
     private static final int CUSTOM_LIGHT_COLOR = Color.BLACK;
     private static final int CUSTOM_LIGHT_ON = 10000;
@@ -237,11 +238,11 @@
                 false /* noisy */, false /* buzzy*/, true /* lights */);
     }
 
-    private NotificationRecord getCustomLightsNotification() {
-        return getNotificationRecord(mId, false /* insistent */, true /* once */,
-                false /* noisy */, true /* buzzy*/, true /* lights */,
-                true /* defaultVibration */, true /* defaultSound */, false /* defaultLights */,
-                null, Notification.GROUP_ALERT_ALL, false);
+    private NotificationRecord getCallRecord(int id, boolean insistent) {
+        return getNotificationRecord(id, false, false /* once */, true /* noisy */,
+                false /* buzzy */, false /* lights */, false /* default vib */,
+                false /* default sound */, false /* default lights */, "",
+                Notification.GROUP_ALERT_ALL, false);
     }
 
     private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
@@ -351,11 +352,6 @@
                 eq(false), (AudioAttributes) anyObject());
     }
 
-    private void verifyCustomBeep() throws RemoteException {
-        verify(mRingtonePlayer, times(1)).playAsync(eq(CUSTOM_SOUND), (UserHandle) anyObject(),
-                eq(false), (AudioAttributes) anyObject());
-    }
-
     private void verifyNeverStopAudio() throws RemoteException {
         verify(mRingtonePlayer, never()).stopAsync();
     }
@@ -1015,22 +1011,6 @@
     }
 
     @Test
-    public void testCanceledNoisyNeverVibrate() throws Exception {
-        NotificationRecord r = getBuzzyBeepyNotification();
-
-        final int waitMs = mAudioManager.getFocusRampTimeMs(
-                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
-                r.getAudioAttributes());
-
-        mService.buzzBeepBlinkLocked(r);
-        mService.clearNotifications();
-
-        verifyNeverVibrate();
-        Thread.sleep(waitMs);
-        verifyNeverVibrate();
-    }
-    
-    @Test
     public void testEmptyUriSoundTreatedAsNoSound() throws Exception {
         NotificationChannel channel = new NotificationChannel("test", "test", IMPORTANCE_HIGH);
         channel.setSound(Uri.EMPTY, null);
@@ -1293,6 +1273,64 @@
         assertEquals(-1, group.getLastAudiblyAlertedMs());
     }
 
+    @Test
+    public void testListenerHintCall() throws Exception {
+        NotificationRecord r = getCallRecord(1, true);
+
+        mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverBeep();
+    }
+
+    @Test
+    public void testListenerHintCall_notificationSound() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+
+        mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyBeepLooped();
+    }
+
+    @Test
+    public void testListenerHintNotification() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+
+        mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverBeep();
+    }
+
+    @Test
+    public void testListenerHintBoth() throws Exception {
+        NotificationRecord r = getCallRecord(1, true);
+        NotificationRecord s = getBeepyNotification();
+
+        mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
+                | NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
+
+        mService.buzzBeepBlinkLocked(r);
+        mService.buzzBeepBlinkLocked(s);
+
+        verifyNeverBeep();
+    }
+
+    @Test
+    public void testListenerHintNotification_callSound() throws Exception {
+        NotificationRecord r = getCallRecord(1, true);
+
+        mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyBeepLooped();
+    }
+
     static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
         private final int mRepeatIndex;
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 8b65e76..20f72bf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -613,7 +613,7 @@
     }
 
     @Test
-    public void testGetAllowedPackages() throws Exception {
+    public void testGetAllowedPackages_byUser() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
                     mIpm, approvalLevel);
@@ -681,6 +681,30 @@
     }
 
     @Test
+    public void testGetAllowedPackages() throws Exception {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_COMPONENT);
+        loadXml(service);
+        service.mApprovalLevel = APPROVAL_BY_PACKAGE;
+        loadXml(service);
+
+        List<String> allowedPackages = new ArrayList<>();
+        allowedPackages.add("this.is.a.package.name");
+        allowedPackages.add("another.package");
+        allowedPackages.add("secondary");
+        allowedPackages.add("this.is.another.package");
+        allowedPackages.add("package");
+        allowedPackages.add("component");
+        allowedPackages.add("bananas!");
+
+        Set<String> actual = service.getAllowedPackages();
+        assertEquals(allowedPackages.size(), actual.size());
+        for (String pkg : allowedPackages) {
+            assertTrue(actual.contains(pkg));
+        }
+    }
+
+    @Test
     public void testOnUserRemoved() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 65e640f..1458266 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -825,4 +825,24 @@
 
         assertNotEquals(-1, record.getLastAudiblyAlertedMs());
     }
+
+    @Test
+    public void testIsNewEnoughForAlerting_new() {
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertTrue(record.isNewEnoughForAlerting(record.mUpdateTimeMs));
+    }
+
+    @Test
+    public void testIsNewEnoughForAlerting_old() {
+        StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertFalse(record.isNewEnoughForAlerting(record.mUpdateTimeMs + (1000 * 60 * 60)));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 49f134f..174c5fa 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -299,6 +299,59 @@
         assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
     }
 
+    @Test
+    public void testClearData() {
+        // snooze 2 from same package
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 1000);
+        mSnoozeHelper.snooze(r2, 1000);
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+
+        // clear data
+        mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
+
+        // nothing snoozed; alarms canceled
+        assertFalse(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertFalse(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+        // twice for initial snooze, twice for canceling the snooze
+        verify(mAm, times(4)).cancel(any(PendingIntent.class));
+    }
+
+    @Test
+    public void testClearData_otherRecordsUntouched() {
+        // 2 packages, 2 users
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.ALL);
+        NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 1000);
+        mSnoozeHelper.snooze(r2, 1000);
+        mSnoozeHelper.snooze(r3, 1000);
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+
+        // clear data
+        mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
+
+        assertFalse(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+        assertTrue(mSnoozeHelper.isSnoozed(
+                UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+        // once for each initial snooze, once for canceling one snooze
+        verify(mAm, times(4)).cancel(any(PendingIntent.class));
+    }
+
     private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
             UserHandle user, String groupKey, boolean groupSummary) {
         Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
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 61e968d..7a6b2b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -565,7 +565,7 @@
         runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
     }
 
     /**
@@ -580,7 +580,7 @@
                 "disallowed_unsupportedUsecase_aborted", true,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
     }
 
     /**
@@ -595,47 +595,53 @@
         runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", false,
                 Process.ROOT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", false,
                 Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_callingUidHasVisibleWindow_notAborted", false,
                 UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_callingUidProcessStateTop_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_realCallingUidHasVisibleWindow_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, true, PROCESS_STATE_TOP + 1,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_realCallingUidProcessStateTop_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP,
-                false, false);
+                false, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_hasForegroundActivities_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                true, false);
+                true, false, false);
         runAndVerifyBackgroundActivityStartsSubtest(
                 "disallowed_callerIsRecents_notAborted", false,
                 UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
                 UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
-                false, true);
+                false, true, false);
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_callerIsWhitelisted_notAborted", false,
+                UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, false, true);
     }
 
     private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
             int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
             int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
-            boolean hasForegroundActivities, boolean callerIsRecents) {
+            boolean hasForegroundActivities, boolean callerIsRecents,
+            boolean callerIsTempWhitelisted) {
         // window visibility
         doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
                 callingUid);
@@ -656,6 +662,8 @@
         RecentTasks recentTasks = mock(RecentTasks.class);
         mService.mStackSupervisor.setRecentTasks(recentTasks);
         doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
+        // caller is temp whitelisted
+        callerApp.setAllowBackgroundActivityStarts(callerIsTempWhitelisted);
 
         final ActivityOptions options = spy(ActivityOptions.makeBasic());
         ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
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 569c6d4..ee336a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -31,6 +31,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -38,6 +39,9 @@
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
@@ -115,6 +119,10 @@
     @After
     public void tearDownBase() {
         mTestInjector.tearDown();
+        if (mService != null) {
+            mService.setWindowManager(null);
+            mService = null;
+        }
     }
 
     ActivityTaskManagerService createActivityTaskManagerService() {
@@ -144,6 +152,13 @@
         return display;
     }
 
+    /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
+    TestActivityDisplay addNewActivityDisplayAt(DisplayInfo info, int position) {
+        final TestActivityDisplay display = createNewActivityDisplay(info);
+        mRootActivityContainer.addChild(display, position);
+        return display;
+    }
+
     /**
      * Builder for creating new activities.
      */
@@ -234,6 +249,10 @@
                     mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
             spyOn(activity);
             activity.mAppWindowToken = mock(AppWindowToken.class);
+            doCallRealMethod().when(activity.mAppWindowToken).getOrientationIgnoreVisibility();
+            doCallRealMethod().when(activity.mAppWindowToken)
+                    .setOrientation(anyInt(), any(), any());
+            doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt());
             doNothing().when(activity).removeWindowContainer();
 
             if (mTaskRecord != null) {
@@ -346,6 +365,7 @@
                 mStack.addTask(task, true, "creating test task");
                 task.setStack(mStack);
                 task.setTask();
+                mStack.getWindowContainerController().mContainer.addChild(task.mTask, 0);
             }
 
             task.touchActiveTime();
@@ -365,7 +385,10 @@
                 setTask();
             }
 
-            private void setTask() {
+            void setTask() {
+                Task mockTask = mock(Task.class);
+                mockTask.mTaskRecord = this;
+                doCallRealMethod().when(mockTask).onDescendantOrientationChanged(any(), any());
                 setTask(mock(Task.class));
             }
         }
@@ -619,7 +642,13 @@
         }
     }
 
+    private static WindowManagerService sMockWindowManagerService;
+
     private static WindowManagerService prepareMockWindowManager() {
+        if (sMockWindowManagerService != null) {
+            return sMockWindowManagerService;
+        }
+
         final WindowManagerService service = mock(WindowManagerService.class);
         service.mRoot = mock(RootWindowContainer.class);
 
@@ -631,6 +660,7 @@
             return null;
         }).when(service).inSurfaceTransaction(any());
 
+        sMockWindowManagerService = service;
         return service;
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 6b31e6f..198e7ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -63,6 +63,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
@@ -86,7 +87,7 @@
 
     private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
 
-    private WindowManagerService mMockWm;
+    private static WindowManagerService sMockWm;
     private DisplayContent mMockDisplayContent;
     private DisplayPolicy mMockDisplayPolicy;
     private Context mMockContext;
@@ -108,13 +109,16 @@
 
     private DisplayRotation mTarget;
 
+    @BeforeClass
+    public static void setUpOnce() {
+        sMockWm = mock(WindowManagerService.class);
+        sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
+    }
+
     @Before
     public void setUp() {
         FakeSettingsProvider.clearSettingsProvider();
 
-        mMockWm = mock(WindowManagerService.class);
-        mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
-
         mPreviousStatusBarManagerInternal = LocalServices.getService(
                 StatusBarManagerInternal.class);
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
@@ -452,7 +456,7 @@
         mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
         assertTrue(waitForUiHandler());
 
-        verify(mMockWm).updateRotation(false, false);
+        verify(sMockWm).updateRotation(false, false);
     }
 
     @Test
@@ -833,8 +837,9 @@
                     .thenReturn(mFakeSettingsProvider.getIContentProvider());
 
             mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
-            mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy,
+            mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayPolicy,
                     mMockDisplayWindowSettings, mMockContext, new Object());
+            reset(sMockWm);
 
             captureObservers();
         }
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 a9f150b..e24eb75 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -34,7 +34,12 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.isNull;
 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;
@@ -47,10 +52,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
 
 import android.app.StatusBarManager;
 import android.app.admin.DevicePolicyManager;
@@ -115,6 +116,7 @@
 
     private LockTaskController mLockTaskController;
     private Context mContext;
+    private String mPackageName;
     private String mLockToAppSetting;
 
     @Before
@@ -122,6 +124,7 @@
         MockitoAnnotations.initMocks(this);
 
         mContext = getInstrumentation().getTargetContext();
+        mPackageName = mContext.getPackageName();
         mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(),
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED);
 
@@ -146,6 +149,7 @@
 
     @After
     public void tearDown() throws Exception {
+        mLockTaskController.setWindowManager(null);
         Settings.Secure.putString(mContext.getContentResolver(),
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting);
     }
@@ -223,7 +227,7 @@
         // THEN lock task mode should be started
         verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
         // THEN screen pinning toast should be shown
-        verify(mStatusBarService).showPinningEnterExitToast(true /* entering */);
+        verify(mStatusBarService).showPinningEnterExitToast(eq(true /* entering */));
     }
 
     @Test
@@ -390,9 +394,9 @@
         // THEN lock task mode should have been finished
         verifyLockTaskStopped(times(1));
         // THEN the keyguard should be shown
-        verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
+        verify(mLockPatternUtils).requireCredentialEntry(eq(UserHandle.USER_ALL));
         // THEN screen pinning toast should be shown
-        verify(mStatusBarService).showPinningEnterExitToast(false /* entering */);
+        verify(mStatusBarService).showPinningEnterExitToast(eq(false /* entering */));
     }
 
     @Test
@@ -509,9 +513,9 @@
                 & ~DISABLE_HOME;
         int expectedFlags2 = DISABLE2_MASK;
         verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
 
         // reset invocation counter
         reset(mStatusBarService);
@@ -526,9 +530,9 @@
         expectedFlags2 = DISABLE2_MASK
                 & ~DISABLE2_NOTIFICATION_SHADE;
         verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
     }
 
     @Test
@@ -548,9 +552,9 @@
 
         // THEN status bar shouldn't change
         verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
     }
 
     @Test
@@ -657,14 +661,14 @@
         verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID));
         // THEN the status bar should have been disabled
         verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
-                eq(mContext.getPackageName()));
+                eq(mPackageName));
         // THEN recents should have been notified
         verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
         // THEN the DO/PO should be informed about the operation
-        verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
-                TEST_USER_ID);
+        verify(mDevicePolicyManager).notifyLockTaskModeChanged(eq(true), eq(TEST_PACKAGE_NAME),
+                eq(TEST_USER_ID));
     }
 
     private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
@@ -672,11 +676,12 @@
         verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID));
         // THEN the status bar should have been disabled
         verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
-                any(IBinder.class), eq(mContext.getPackageName()));
+                any(IBinder.class), eq(mPackageName));
         verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE),
-                any(IBinder.class), eq(mContext.getPackageName()));
+                any(IBinder.class), eq(mPackageName));
         // THEN the DO/PO should be informed about the operation
-        verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID);
+        verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(eq(false), isNull(),
+                eq(TEST_USER_ID));
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index ad2a708..413b6f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -143,8 +143,8 @@
 
     @Test
     public void testTimeout_scaled() throws Exception {
-        mWm.setAnimationScale(2, 5.0f);
         try {
+            mWm.setAnimationScale(2, 5.0f);
             final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
                     "testWin");
             final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 36eccd1..03aba39 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -33,6 +33,8 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -58,7 +60,6 @@
 import android.view.WindowManager;
 import android.widget.TextView;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
@@ -80,8 +81,8 @@
 @Presubmit
 public class ScreenDecorWindowTests {
 
-    private final Context mContext = InstrumentationRegistry.getTargetContext();
-    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    private final Context mContext = getInstrumentation().getTargetContext();
+    private final Instrumentation mInstrumentation = getInstrumentation();
 
     private WindowManager mWm;
     private ArrayList<View> mWindows = new ArrayList<>();
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 7186e22..53e99fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -199,6 +199,25 @@
         assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
     }
 
+    @Test
+    public void testUsesNoDisplaySourceHandoverDisplayIdIfSet() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FULLSCREEN);
+
+        mCurrent.mPreferredDisplayId = fullscreenDisplay.mDisplayId;
+        ActivityRecord reusableActivity = createSourceActivity(fullscreenDisplay);
+        ActivityRecord source = createSourceActivity(freeformDisplay);
+        source.mHandoverLaunchDisplayId = freeformDisplay.mDisplayId;
+        source.noDisplay = true;
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(reusableActivity.getTaskRecord(),
+                null /* layout */, mActivity, source, null /* options */, mCurrent, mResult));
+
+        assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
+    }
+
     // =====================================
     // Launch Windowing Mode Related Tests
     // =====================================
@@ -768,6 +787,21 @@
     }
 
     @Test
+    public void testReturnBoundsForFullscreenWindowingMode() {
+        final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FREEFORM);
+
+        mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
+        mCurrent.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+        mCurrent.mBounds.set(0, 0, 200, 100);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
+
+        assertEquals(new Rect(0, 0, 200, 100), mResult.mBounds);
+    }
+
+    @Test
     public void testUsesDisplayOrientationForNoSensorOrientation() {
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 7da85af..00bec3f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -21,6 +21,11 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.sameInstance;
@@ -133,17 +138,6 @@
         assertTrue(task.returnsToHomeStack());
     }
 
-    /** Ensures that bounds are clipped to their parent. */
-    @Test
-    public void testAppBounds_BoundsClipping() {
-        final Rect shiftedBounds = new Rect(mParentBounds);
-        shiftedBounds.offset(10, 10);
-        final Rect expectedBounds = new Rect(mParentBounds);
-        expectedBounds.intersect(shiftedBounds);
-        testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds,
-                expectedBounds);
-    }
-
     /** Ensures that empty bounds are not propagated to the configuration. */
     @Test
     public void testAppBounds_EmptyBounds() {
@@ -167,18 +161,108 @@
         final Rect insetBounds = new Rect(mParentBounds);
         insetBounds.inset(5, 5, 5, 5);
         testStackBoundsConfiguration(
-                WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds);
+                WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds);
     }
 
-    /** Ensures that full screen free form bounds are clipped */
+    /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
     @Test
-    public void testAppBounds_FullScreenFreeFormBounds() {
+    public void testBoundsOnModeChangeFreeformToFullscreen() {
         ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+        ActivityStack stack = new StackBuilder(mRootActivityContainer).setDisplay(display)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        TaskRecord task = stack.getChildAt(0);
+        task.getRootActivity().mAppWindowToken.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
         DisplayInfo info = new DisplayInfo();
         display.mDisplay.getDisplayInfo(info);
         final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
-        testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds,
-                mParentBounds);
+        final Rect freeformBounds = new Rect(fullScreenBounds);
+        freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+                (int) (freeformBounds.height() * 0.2));
+        task.setBounds(freeformBounds);
+
+        assertEquals(freeformBounds, task.getBounds());
+
+        // FULLSCREEN inherits bounds
+        stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertEquals(fullScreenBounds, task.getBounds());
+        assertEquals(freeformBounds, task.mLastNonFullscreenBounds);
+
+        // FREEFORM restores bounds
+        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(freeformBounds, task.getBounds());
+    }
+
+    /**
+     * This is a temporary hack to trigger an onConfigurationChange at the task level after an
+     * orientation is requested. Normally this is done by the onDescendentOrientationChanged call
+     * up the WM hierarchy, but since the WM hierarchy is mocked out, it doesn't happen here.
+     * TODO: remove this when we either get a WM hierarchy or when hierarchies are merged.
+     */
+    private void setActivityRequestedOrientation(ActivityRecord activity, int orientation) {
+        activity.setRequestedOrientation(orientation);
+        ConfigurationContainer taskRecord = activity.getParent();
+        taskRecord.onConfigurationChanged(taskRecord.getParent().getConfiguration());
+    }
+
+    /**
+     * Tests that a task with forced orientation has orientation-consistent bounds within the
+     * parent.
+     */
+    @Test
+    public void testFullscreenBoundsForcedOrientation() {
+        final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+        final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
+        DisplayInfo info = new DisplayInfo();
+        info.logicalWidth = fullScreenBounds.width();
+        info.logicalHeight = fullScreenBounds.height();
+        ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
+        assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null);
+        ActivityStack stack = new StackBuilder(mRootActivityContainer)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+        TaskRecord task = stack.getChildAt(0);
+        ActivityRecord root = task.getRootActivity();
+        ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
+        assertEquals(root, task.getRootActivity());
+
+        assertEquals(fullScreenBounds, task.getBounds());
+
+        // Setting app to fixed portrait fits within parent
+        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+        assertEquals(root, task.getRootActivity());
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
+        assertTrue(task.getBounds().width() < task.getBounds().height());
+        assertEquals(fullScreenBounds.height(), task.getBounds().height());
+
+        // Setting non-root app has no effect
+        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE);
+        assertTrue(task.getBounds().width() < task.getBounds().height());
+
+        // Setting app to unspecified restores
+        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_UNSPECIFIED);
+        assertEquals(fullScreenBounds, task.getBounds());
+
+        // Setting app to fixed landscape and changing display
+        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE);
+        display.setBounds(fullScreenBoundsPort);
+        assertTrue(task.getBounds().width() > task.getBounds().height());
+        assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
+
+        // in FREEFORM, no constraint
+        final Rect freeformBounds = new Rect(display.getBounds());
+        freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+                (int) (freeformBounds.height() * 0.2));
+        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        task.setBounds(freeformBounds);
+        assertEquals(freeformBounds, task.getBounds());
+
+        // FULLSCREEN letterboxes bounds
+        stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertTrue(task.getBounds().width() > task.getBounds().height());
+        assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
+
+        // FREEFORM restores bounds as before
+        stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(freeformBounds, task.getBounds());
     }
 
     /** Ensures that the alias intent won't have target component resolved. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 624ef9b..ca815ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -39,6 +39,7 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.Surface;
+import android.view.SurfaceControl;
 
 import androidx.test.filters.SmallTest;
 
@@ -65,7 +66,7 @@
         final TaskSnapshot snapshot = new TaskSnapshot(new ComponentName("", ""), buffer,
                 ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */,
                 WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */);
-        mSurface = new TaskSnapshotSurface(mWm, new Window(), new Surface(), snapshot, "Test",
+        mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
                 Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds,
                 ORIENTATION_PORTRAIT);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
deleted file mode 100644
index 04e433e..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManagerInternal;
-import android.content.Context;
-import android.hardware.display.DisplayManagerInternal;
-import android.os.PowerManagerInternal;
-import android.os.PowerSaveState;
-import android.view.Display;
-import android.view.InputChannel;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-
-import com.android.server.LocalServices;
-import com.android.server.input.InputManagerService;
-import com.android.server.policy.WindowManagerPolicy;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure
- * to properly tear it down after.
- *
- * <p>
- * Usage:
- * <pre>
- * {@literal @}Rule
- *  public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
- * </pre>
- */
-public class WindowManagerServiceRule implements TestRule {
-
-    private WindowManagerService mService;
-    private TestWindowManagerPolicy mPolicy;
-    // Record all {@link SurfaceControl.Transaction} created while testing and releases native
-    // resources when test finishes.
-    private final List<WeakReference<Transaction>> mSurfaceTransactions = new ArrayList<>();
-    // Record all {@link SurfaceControl} created while testing and releases native resources when
-    // test finishes.
-    private final List<WeakReference<SurfaceControl>> mSurfaceControls = new ArrayList<>();
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                runWithDexmakerShareClassLoader(this::setUp);
-                try {
-                    base.evaluate();
-                } finally {
-                    tearDown();
-                }
-            }
-
-            private void setUp() {
-                final Context context = getInstrumentation().getTargetContext();
-
-                removeServices();
-
-                LocalServices.addService(DisplayManagerInternal.class,
-                        mock(DisplayManagerInternal.class));
-
-                LocalServices.addService(PowerManagerInternal.class,
-                        mock(PowerManagerInternal.class));
-                final PowerManagerInternal pm =
-                        LocalServices.getService(PowerManagerInternal.class);
-                doNothing().when(pm).registerLowPowerModeObserver(any());
-                PowerSaveState state = new PowerSaveState.Builder().build();
-                doReturn(state).when(pm).getLowPowerState(anyInt());
-
-                LocalServices.addService(ActivityManagerInternal.class,
-                        mock(ActivityManagerInternal.class));
-                LocalServices.addService(ActivityTaskManagerInternal.class,
-                        mock(ActivityTaskManagerInternal.class));
-                final ActivityTaskManagerInternal atm =
-                        LocalServices.getService(ActivityTaskManagerInternal.class);
-                doAnswer((InvocationOnMock invocationOnMock) -> {
-                    final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
-                    if (runnable != null) {
-                        runnable.run();
-                    }
-                    return null;
-                }).when(atm).notifyKeyguardFlagsChanged(any(), anyInt());
-
-                InputManagerService ims = mock(InputManagerService.class);
-                // InputChannel is final and can't be mocked.
-                InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
-                if (input != null && input.length > 1) {
-                    doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
-                }
-                ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
-                when(atms.getGlobalLock()).thenReturn(new WindowManagerGlobalLock());
-
-                mService = WindowManagerService.main(context, ims, false, false,
-                        mPolicy = new TestWindowManagerPolicy(
-                                WindowManagerServiceRule.this::getWindowManagerService), atms);
-                mService.mTransactionFactory = () -> {
-                    final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
-                    mSurfaceTransactions.add(new WeakReference<>(transaction));
-                    return transaction;
-                };
-                mService.mSurfaceBuilderFactory = session -> new SurfaceControl.Builder(session) {
-                    @Override
-                    public SurfaceControl build() {
-                        final SurfaceControl control = super.build();
-                        mSurfaceControls.add(new WeakReference<>(control));
-                        return control;
-                    }
-                };
-
-                mService.onInitReady();
-
-                final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
-                // Display creation is driven by the ActivityManagerService via
-                // ActivityStackSupervisor. We emulate those steps here.
-                mService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
-            }
-
-            private void removeServices() {
-                LocalServices.removeServiceForTest(DisplayManagerInternal.class);
-                LocalServices.removeServiceForTest(PowerManagerInternal.class);
-                LocalServices.removeServiceForTest(ActivityManagerInternal.class);
-                LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
-                LocalServices.removeServiceForTest(WindowManagerInternal.class);
-                LocalServices.removeServiceForTest(WindowManagerPolicy.class);
-            }
-
-            private void tearDown() {
-                cancelAllPendingAnimations();
-                waitUntilWindowManagerHandlersIdle();
-                destroyAllSurfaceTransactions();
-                destroyAllSurfaceControls();
-                removeServices();
-                mService = null;
-                mPolicy = null;
-            }
-        };
-    }
-
-    WindowManagerService getWindowManagerService() {
-        return mService;
-    }
-
-    private void cancelAllPendingAnimations() {
-        for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
-            final SurfaceControl sc = reference.get();
-            if (sc != null) {
-                mService.mSurfaceAnimationRunner.onAnimationCancelled(sc);
-            }
-        }
-    }
-
-    void waitUntilWindowManagerHandlersIdle() {
-        final WindowManagerService wm = getWindowManagerService();
-        if (wm == null) {
-            return;
-        }
-        wm.mH.removeCallbacksAndMessages(null);
-        wm.mAnimationHandler.removeCallbacksAndMessages(null);
-        SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null);
-        wm.mH.runWithScissors(() -> { }, 0);
-        wm.mAnimationHandler.runWithScissors(() -> { }, 0);
-        SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
-    }
-
-    private void destroyAllSurfaceTransactions() {
-        for (final WeakReference<Transaction> reference : mSurfaceTransactions) {
-            final Transaction transaction = reference.get();
-            if (transaction != null) {
-                reference.clear();
-                transaction.close();
-            }
-        }
-    }
-
-    private void destroyAllSurfaceControls() {
-        for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
-            final SurfaceControl control = reference.get();
-            if (control != null) {
-                reference.clear();
-                control.destroy();
-            }
-        }
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
deleted file mode 100644
index 343d359..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.junit.Assert.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Build/InstallRun:
- *  atest FrameworksServicesTests:WindowManagerServiceRuleTest
- */
-@Presubmit
-@SmallTest
-public class WindowManagerServiceRuleTest {
-
-    @Rule
-    public final WindowManagerServiceRule mRule = new WindowManagerServiceRule();
-
-    @Test
-    public void testWindowManagerSetUp() {
-        assertThat(mRule.getWindowManagerService(), notNullValue());
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 7f78034..c09cd46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.Surface.ROTATION_0;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -31,6 +32,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -56,6 +58,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Size;
 import android.view.DisplayCutout;
+import android.view.InsetsSource;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
@@ -326,6 +329,20 @@
     }
 
     @Test
+    public void testVisibleWithInsetsProvider() throws Exception {
+        final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        topBar.mHasSurface = true;
+        assertTrue(topBar.isVisible());
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .setWindow(topBar, null);
+        mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(app);
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .onInsetsModified(app, new InsetsSource(TYPE_TOP_BAR));
+        assertFalse(topBar.isVisible());
+    }
+
+    @Test
     public void testIsSelfOrAncestorWindowAnimating() {
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
         final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 5c3368b..3813891 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -53,6 +53,7 @@
 import com.android.server.AttributeCache;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Rule;
@@ -95,17 +96,22 @@
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
 
-    @Rule
-    public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
-
-    static WindowState.PowerManagerWrapper sPowerManagerWrapper;  // TODO(roosa): make non-static.
+    static WindowState.PowerManagerWrapper sPowerManagerWrapper;
 
     @BeforeClass
     public static void setUpOnceBase() {
         AttributeCache.init(getInstrumentation().getTargetContext());
+
+        WmServiceUtils.setUpWindowManagerService();
+
         sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
     }
 
+    @AfterClass
+    public static void tearDonwOnceBase() {
+        WmServiceUtils.tearDownWindowManagerService();
+    }
+
     @Before
     public void setUpBase() {
         // If @Before throws an exception, the error isn't logged. This will make sure any failures
@@ -115,7 +121,7 @@
 
             final Context context = getInstrumentation().getTargetContext();
 
-            mWm = mWmRule.getWindowManagerService();
+            mWm = WmServiceUtils.getWindowManagerService();
             beforeCreateDisplay();
 
             context.getDisplay().getDisplayInfo(mDisplayInfo);
@@ -214,7 +220,7 @@
      * Waits until the main handler for WM has processed all messages.
      */
     void waitUntilHandlersIdle() {
-        mWmRule.waitUntilWindowManagerHandlersIdle();
+        WmServiceUtils.waitUntilWindowManagerHandlersIdle();
     }
 
     private WindowToken createWindowToken(
diff --git a/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java
new file mode 100644
index 0000000..2465e5d8
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+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.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManagerInternal;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.os.UserHandle;
+import android.view.Display;
+import android.view.InputChannel;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.server.LocalServices;
+import com.android.server.LockGuard;
+import com.android.server.Watchdog;
+import com.android.server.input.InputManagerService;
+import com.android.server.policy.WindowManagerPolicy;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.quality.Strictness;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A Test utility class to create a mock {@link WindowManagerService} instance for tests.
+ */
+class WmServiceUtils {
+    private static StaticMockitoSession sMockitoSession;
+    private static WindowManagerService sService;
+    private static TestWindowManagerPolicy sPolicy;
+
+    static void setUpWindowManagerService() {
+        sMockitoSession = mockitoSession()
+                .spyStatic(LockGuard.class)
+                .spyStatic(Watchdog.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+
+        runWithDexmakerShareClassLoader(WmServiceUtils::setUpTestWindowService);
+    }
+
+    static void tearDownWindowManagerService() {
+        waitUntilWindowManagerHandlersIdle();
+        removeLocalServices();
+        sService = null;
+        sPolicy = null;
+
+        sMockitoSession.finishMocking();
+    }
+
+    private static void setUpTestWindowService() {
+        doReturn(null).when(() -> LockGuard.installLock(any(), anyInt()));
+        doReturn(mock(Watchdog.class)).when(Watchdog::getInstance);
+
+        final Context context = getInstrumentation().getTargetContext();
+        spyOn(context);
+
+        doReturn(null).when(context)
+                .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class));
+        doReturn(null).when(context)
+                .registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class),
+                        any(IntentFilter.class), nullable(String.class), nullable(Handler.class));
+
+        final ContentResolver contentResolver = context.getContentResolver();
+        spyOn(contentResolver);
+        doNothing().when(contentResolver)
+                .registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class),
+                        anyInt());
+
+        final AppOpsManager appOpsManager = mock(AppOpsManager.class);
+        doReturn(appOpsManager).when(context)
+                .getSystemService(eq(Context.APP_OPS_SERVICE));
+
+        removeLocalServices();
+
+        final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class);
+        LocalServices.addService(DisplayManagerInternal.class, dmi);
+
+        final PowerManagerInternal pmi = mock(PowerManagerInternal.class);
+        LocalServices.addService(PowerManagerInternal.class, pmi);
+        final PowerSaveState state = new PowerSaveState.Builder().build();
+        doReturn(state).when(pmi).getLowPowerState(anyInt());
+
+        final ActivityManagerInternal ami = mock(ActivityManagerInternal.class);
+        LocalServices.addService(ActivityManagerInternal.class, ami);
+
+        final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class);
+        LocalServices.addService(ActivityTaskManagerInternal.class, atmi);
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final Runnable runnable = invocationOnMock.getArgument(0);
+            if (runnable != null) {
+                runnable.run();
+            }
+            return null;
+        }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt());
+
+        final InputManagerService ims = mock(InputManagerService.class);
+        // InputChannel is final and can't be mocked.
+        final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
+        if (input != null && input.length > 1) {
+            doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
+        }
+
+        final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
+        final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock();
+        doReturn(wmLock).when(atms).getGlobalLock();
+
+        sPolicy = new TestWindowManagerPolicy(WmServiceUtils::getWindowManagerService);
+        sService = WindowManagerService.main(context, ims, false, false, sPolicy, atms);
+
+        sService.onInitReady();
+
+        final Display display = sService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+        // Display creation is driven by the ActivityManagerService via
+        // ActivityStackSupervisor. We emulate those steps here.
+        sService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
+    }
+
+    private static void removeLocalServices() {
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.removeServiceForTest(PowerManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+        LocalServices.removeServiceForTest(WindowManagerInternal.class);
+        LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+    }
+
+    static WindowManagerService getWindowManagerService() {
+        return sService;
+    }
+
+    static void waitUntilWindowManagerHandlersIdle() {
+        final WindowManagerService wm = getWindowManagerService();
+        if (wm == null) {
+            return;
+        }
+        // Removing delayed FORCE_GC message decreases time for waiting idle.
+        wm.mH.removeMessages(WindowManagerService.H.FORCE_GC);
+        waitHandlerIdle(wm.mH);
+        waitHandlerIdle(wm.mAnimationHandler);
+        waitHandlerIdle(SurfaceAnimationThread.getHandler());
+    }
+
+    private static void waitHandlerIdle(Handler handler) {
+        if (!handler.hasMessagesOrCallbacks()) {
+            return;
+        }
+        final CountDownLatch latch = new CountDownLatch(1);
+        // Wait for delayed messages are processed.
+        handler.getLooper().getQueue().addIdleHandler(() -> {
+            if (handler.hasMessagesOrCallbacks()) {
+                return true; // keep idle handler.
+            }
+            latch.countDown();
+            return false; // remove idle handler.
+        });
+        try {
+            latch.await();
+        } catch (InterruptedException e) {
+        }
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 8e1ede1..2ed11fe 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -23,7 +23,6 @@
 import android.os.Message;
 import android.os.SystemClock;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -62,6 +61,8 @@
 
     private static final long ONE_MINUTE = 60_000L;
 
+    private static final Integer ONE = new Integer(1);
+
     /** Collection of data for each user that has reported usage */
     @GuardedBy("mLock")
     private final SparseArray<UserData> mUsers = new SparseArray<>();
@@ -79,11 +80,11 @@
         private @UserIdInt
         int userId;
 
-        /** Set of the currently active entities */
-        private final ArraySet<String> currentlyActive = new ArraySet<>();
+        /** Count of the currently active entities */
+        public final ArrayMap<String, Integer> currentlyActive = new ArrayMap<>();
 
         /** Map from entity name for quick lookup */
-        private final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>();
+        public final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>();
 
         private UserData(@UserIdInt int userId) {
             this.userId = userId;
@@ -94,7 +95,7 @@
             // TODO: Consider using a bloom filter here if number of actives becomes large
             final int size = entities.length;
             for (int i = 0; i < size; i++) {
-                if (currentlyActive.contains(entities[i])) {
+                if (currentlyActive.containsKey(entities[i])) {
                     return true;
                 }
             }
@@ -137,7 +138,7 @@
             pw.print(" Currently Active:");
             final int nActive = currentlyActive.size();
             for (int i = 0; i < nActive; i++) {
-                pw.print(currentlyActive.valueAt(i));
+                pw.print(currentlyActive.keyAt(i));
                 pw.print(", ");
             }
             pw.println();
@@ -233,6 +234,7 @@
         protected long mUsageTimeMs;
         protected int mActives;
         protected long mLastKnownUsageTimeMs;
+        protected long mLastUsageEndTimeMs;
         protected WeakReference<UserData> mUserRef;
         protected WeakReference<ObserverAppData> mObserverAppRef;
         protected PendingIntent mLimitReachedCallback;
@@ -271,9 +273,15 @@
         @GuardedBy("mLock")
         void noteUsageStart(long startTimeMs, long currentTimeMs) {
             if (mActives++ == 0) {
+                // If last known usage ended after the start of this usage, there is overlap
+                // between the last usage session and this one. Avoid double counting by only
+                // counting from the end of the last session. This has a rare side effect that some
+                // usage will not be accounted for if the previous session started and stopped
+                // within this current usage.
+                startTimeMs = mLastUsageEndTimeMs > startTimeMs ? mLastUsageEndTimeMs : startTimeMs;
                 mLastKnownUsageTimeMs = startTimeMs;
                 final long timeRemaining =
-                        mTimeLimitMs - mUsageTimeMs + currentTimeMs - startTimeMs;
+                        mTimeLimitMs - mUsageTimeMs - currentTimeMs + startTimeMs;
                 if (timeRemaining > 0) {
                     if (DEBUG) {
                         Slog.d(TAG, "Posting timeout for " + mObserverId + " for "
@@ -287,7 +295,7 @@
                     mActives = mObserved.length;
                     final UserData user = mUserRef.get();
                     if (user == null) return;
-                    final Object[] array = user.currentlyActive.toArray();
+                    final Object[] array = user.currentlyActive.keySet().toArray();
                     Slog.e(TAG,
                             "Too many noted usage starts! Observed entities: " + Arrays.toString(
                                     mObserved) + "   Active Entities: " + Arrays.toString(array));
@@ -300,6 +308,8 @@
             if (--mActives == 0) {
                 final boolean limitNotCrossed = mUsageTimeMs < mTimeLimitMs;
                 mUsageTimeMs += stopTimeMs - mLastKnownUsageTimeMs;
+
+                mLastUsageEndTimeMs = stopTimeMs;
                 if (limitNotCrossed && mUsageTimeMs >= mTimeLimitMs) {
                     // Crossed the limit
                     if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + mObserverId);
@@ -312,7 +322,7 @@
                     mActives = 0;
                     final UserData user = mUserRef.get();
                     if (user == null) return;
-                    final Object[] array = user.currentlyActive.toArray();
+                    final Object[] array = user.currentlyActive.keySet().toArray();
                     Slog.e(TAG,
                             "Too many noted usage stops! Observed entities: " + Arrays.toString(
                                     mObserved) + "   Active Entities: " + Arrays.toString(array));
@@ -409,7 +419,6 @@
     }
 
     class SessionUsageGroup extends UsageGroup {
-        private long mLastUsageEndTimeMs;
         private long mNewSessionThresholdMs;
         private PendingIntent mSessionEndCallback;
 
@@ -451,7 +460,6 @@
         public void noteUsageStop(long stopTimeMs) {
             super.noteUsageStop(stopTimeMs);
             if (mActives == 0) {
-                mLastUsageEndTimeMs = stopTimeMs;
                 if (mUsageTimeMs >= mTimeLimitMs) {
                     // Usage has ended. Schedule the session end callback to be triggered once
                     // the new session threshold has been reached
@@ -467,7 +475,10 @@
             UserData user = mUserRef.get();
             if (user == null) return;
             if (mListener != null) {
-                mListener.onSessionEnd(mObserverId, user.userId, mUsageTimeMs, mSessionEndCallback);
+                mListener.onSessionEnd(mObserverId,
+                                       user.userId,
+                                       mUsageTimeMs,
+                                       mSessionEndCallback);
             }
         }
 
@@ -599,7 +610,7 @@
         // TODO: Consider using a bloom filter here if number of actives becomes large
         final int size = group.mObserved.length;
         for (int i = 0; i < size; i++) {
-            if (user.currentlyActive.contains(group.mObserved[i])) {
+            if (user.currentlyActive.containsKey(group.mObserved[i])) {
                 // Entity is currently active. Start group's usage.
                 group.noteUsageStart(currentTimeMs);
             }
@@ -717,21 +728,28 @@
     /**
      * Called when an entity becomes active.
      *
-     * @param name   The entity that became active
-     * @param userId The user
+     * @param name      The entity that became active
+     * @param userId    The user
+     * @param timeAgoMs Time since usage was started
      */
-    public void noteUsageStart(String name, int userId) throws IllegalArgumentException {
+    public void noteUsageStart(String name, int userId, long timeAgoMs)
+            throws IllegalArgumentException {
         synchronized (mLock) {
             UserData user = getOrCreateUserDataLocked(userId);
             if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became active");
-            if (user.currentlyActive.contains(name)) {
-                throw new IllegalArgumentException(
-                        "Unable to start usage for " + name + ", already in use");
+
+            final int index = user.currentlyActive.indexOfKey(name);
+            if (index >= 0) {
+                final Integer count = user.currentlyActive.valueAt(index);
+                if (count != null) {
+                    // There are multiple instances of this entity. Just increment the count.
+                    user.currentlyActive.setValueAt(index, count + 1);
+                    return;
+                }
             }
             final long currentTime = getUptimeMillis();
 
-            // Add to the list of active entities
-            user.currentlyActive.add(name);
+            user.currentlyActive.put(name, ONE);
 
             ArrayList<UsageGroup> groups = user.observedMap.get(name);
             if (groups == null) return;
@@ -739,12 +757,22 @@
             final int size = groups.size();
             for (int i = 0; i < size; i++) {
                 UsageGroup group = groups.get(i);
-                group.noteUsageStart(currentTime);
+                group.noteUsageStart(currentTime - timeAgoMs, currentTime);
             }
         }
     }
 
     /**
+     * Called when an entity becomes active.
+     *
+     * @param name   The entity that became active
+     * @param userId The user
+     */
+    public void noteUsageStart(String name, int userId) throws IllegalArgumentException {
+        noteUsageStart(name, userId, 0);
+    }
+
+    /**
      * Called when an entity becomes inactive.
      *
      * @param name   The entity that became inactive
@@ -754,10 +782,21 @@
         synchronized (mLock) {
             UserData user = getOrCreateUserDataLocked(userId);
             if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became inactive");
-            if (!user.currentlyActive.remove(name)) {
+
+            final int index = user.currentlyActive.indexOfKey(name);
+            if (index < 0) {
                 throw new IllegalArgumentException(
                         "Unable to stop usage for " + name + ", not in use");
             }
+
+            final Integer count = user.currentlyActive.valueAt(index);
+            if (!count.equals(ONE)) {
+                // There are multiple instances of this entity. Just decrement the count.
+                user.currentlyActive.setValueAt(index, count - 1);
+                return;
+            }
+
+            user.currentlyActive.removeAt(index);
             final long currentTime = getUptimeMillis();
 
             // Check if any of the groups need to watch for this entity
@@ -769,6 +808,7 @@
                 UsageGroup group = groups.get(i);
                 group.noteUsageStop(currentTime);
             }
+
         }
     }
 
@@ -780,7 +820,8 @@
 
     @GuardedBy("mLock")
     private void postInformSessionEndListenerLocked(SessionUsageGroup group, long timeout) {
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
+        mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
                 timeout);
     }
 
@@ -800,7 +841,27 @@
         mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
     }
 
-    void dump(PrintWriter pw) {
+    void dump(String[] args, PrintWriter pw) {
+        if (args != null) {
+            for (int i = 0; i < args.length; i++) {
+                String arg = args[i];
+                if ("actives".equals(arg)) {
+                    synchronized (mLock) {
+                        final int nUsers = mUsers.size();
+                        for (int user = 0; user < nUsers; user++) {
+                            final ArrayMap<String, Integer> actives =
+                                    mUsers.valueAt(user).currentlyActive;
+                            final int nActive = actives.size();
+                            for (int active = 0; active < nActive; active++) {
+                                pw.println(actives.keyAt(active));
+                            }
+                        }
+                    }
+                    return;
+                }
+            }
+        }
+
         synchronized (mLock) {
             pw.println("\n  App Time Limits");
             final int nUsers = mUsers.size();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 57dc08f..f146370 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -53,6 +53,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.IDeviceIdleController;
 import android.os.Looper;
 import android.os.Message;
@@ -105,6 +106,8 @@
     private static final boolean ENABLE_KERNEL_UPDATES = true;
     private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
 
+    private static final char TOKEN_DELIMITER = '/';
+
     // Handler message types.
     static final int MSG_REPORT_EVENT = 0;
     static final int MSG_FLUSH_TO_DISK = 1;
@@ -135,6 +138,10 @@
     /** Manages app time limit observers */
     AppTimeLimitController mAppTimeLimit;
 
+    final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
+    final SparseArray<String> mVisibleActivities = new SparseArray();
+
+
     private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener =
             new UsageStatsManagerInternal.AppIdleStateChangeListener() {
                 @Override
@@ -270,7 +277,7 @@
                     mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
                 }
             } else if (Intent.ACTION_USER_STARTED.equals(action)) {
-                if (userId >=0) {
+                if (userId >= 0) {
                     mAppStandby.postCheckIdleStates(userId);
                 }
             }
@@ -434,17 +441,46 @@
             mAppStandby.reportEvent(event, elapsedRealtime, userId);
             switch (event.mEventType) {
                 case Event.ACTIVITY_RESUMED:
-                    try {
-                        mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
-                    } catch (IllegalArgumentException iae) {
-                        Slog.e(TAG, "Failed to note usage start", iae);
+                    synchronized (mVisibleActivities) {
+                        mVisibleActivities.put(event.mInstanceId, event.getClassName());
+                        try {
+                            mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
+                        } catch (IllegalArgumentException iae) {
+                            Slog.e(TAG, "Failed to note usage start", iae);
+                        }
                     }
                     break;
-                case Event.ACTIVITY_PAUSED:
-                    try {
-                        mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
-                    } catch (IllegalArgumentException iae) {
-                        Slog.e(TAG, "Failed to note usage stop", iae);
+                case Event.ACTIVITY_STOPPED:
+                case Event.ACTIVITY_DESTROYED:
+                    ArraySet<String> tokens;
+                    synchronized (mUsageReporters) {
+                        tokens = mUsageReporters.removeReturnOld(event.mInstanceId);
+                    }
+                    if (tokens != null) {
+                        synchronized (tokens) {
+                            final int size = tokens.size();
+                            // Stop usage on behalf of a UsageReporter that stopped
+                            for (int i = 0; i < size; i++) {
+                                final String token = tokens.valueAt(i);
+                                try {
+                                    mAppTimeLimit.noteUsageStop(
+                                            buildFullToken(event.getPackageName(), token), userId);
+                                } catch (IllegalArgumentException iae) {
+                                    Slog.w(TAG, "Failed to stop usage for during reporter death: "
+                                            + iae);
+                                }
+                            }
+                        }
+                    }
+
+                    synchronized (mVisibleActivities) {
+                        if (mVisibleActivities.removeReturnOld(event.mInstanceId) != null) {
+                            try {
+                                mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
+                            } catch (IllegalArgumentException iae) {
+                                Slog.w(TAG, "Failed to note usage stop", iae);
+                            }
+                        }
                     }
                     break;
             }
@@ -599,6 +635,14 @@
         return beginTime <= currentTime && beginTime < endTime;
     }
 
+    private String buildFullToken(String packageName, String token) {
+        final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1);
+        sb.append(packageName);
+        sb.append(TOKEN_DELIMITER);
+        sb.append(token);
+        return sb.toString();
+    }
+
     private void flushToDiskLocked() {
         final int userCount = mUserState.size();
         for (int i = 0; i < userCount; i++) {
@@ -627,8 +671,7 @@
                     String arg = args[i];
                     if ("--checkin".equals(arg)) {
                         checkin = true;
-                    } else
-                    if ("-c".equals(arg)) {
+                    } else if ("-c".equals(arg)) {
                         compact = true;
                     } else if ("flush".equals(arg)) {
                         flushToDiskLocked();
@@ -637,6 +680,15 @@
                     } else if ("is-app-standby-enabled".equals(arg)) {
                         pw.println(mAppStandby.mAppIdleEnabled);
                         return;
+                    } else if ("apptimelimit".equals(arg)) {
+                        if (i + 1 >= args.length) {
+                            mAppTimeLimit.dump(null, pw);
+                        } else {
+                            final String[] remainingArgs =
+                                    Arrays.copyOfRange(args, i + 1, args.length);
+                            mAppTimeLimit.dump(remainingArgs, pw);
+                        }
+                        return;
                     } else if (arg != null && !arg.startsWith("-")) {
                         // Anything else that doesn't start with '-' is a pkg to filter
                         pkg = arg;
@@ -666,7 +718,7 @@
                 mAppStandby.dumpState(args, pw);
             }
 
-            mAppTimeLimit.dump(pw);
+            mAppTimeLimit.dump(null, pw);
         }
     }
 
@@ -1231,16 +1283,82 @@
             final int userId = UserHandle.getUserId(callingUid);
             final long token = Binder.clearCallingIdentity();
             try {
-                UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId, userId);
+                UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId,
+                        userId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public void reportUsageStart(IBinder activity, String token, String callingPackage) {
+            reportPastUsageStart(activity, token, 0, callingPackage);
+        }
+
+        @Override
+        public void reportPastUsageStart(IBinder activity, String token, long timeAgoMs,
+                String callingPackage) {
+
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long binderToken = Binder.clearCallingIdentity();
+            try {
+                ArraySet<String> tokens;
+                synchronized (mUsageReporters) {
+                    tokens = mUsageReporters.get(activity.hashCode());
+                    if (tokens == null) {
+                        tokens = new ArraySet();
+                        mUsageReporters.put(activity.hashCode(), tokens);
+                    }
+                }
+
+                synchronized (tokens) {
+                    if (!tokens.add(token)) {
+                        throw new IllegalArgumentException(token + " for " + callingPackage
+                                + " is already reported as started for this activity");
+                    }
+                }
+
+                mAppTimeLimit.noteUsageStart(buildFullToken(callingPackage, token),
+                        userId, timeAgoMs);
+            } finally {
+                Binder.restoreCallingIdentity(binderToken);
+            }
+        }
+
+        @Override
+        public void reportUsageStop(IBinder activity, String token, String callingPackage) {
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long binderToken = Binder.clearCallingIdentity();
+            try {
+                ArraySet<String> tokens;
+                synchronized (mUsageReporters) {
+                    tokens = mUsageReporters.get(activity.hashCode());
+                    if (tokens == null) {
+                        throw new IllegalArgumentException(
+                                "Unknown reporter trying to stop token " + token + " for "
+                                        + callingPackage);
+                    }
+                }
+
+                synchronized (tokens) {
+                    if (!tokens.remove(token)) {
+                        throw new IllegalArgumentException(token + " for " + callingPackage
+                                + " is already reported as stopped for this activity");
+                    }
+                }
+                mAppTimeLimit.noteUsageStop(buildFullToken(callingPackage, token), userId);
+            } finally {
+                Binder.restoreCallingIdentity(binderToken);
+            }
+        }
     }
 
     void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
             long timeLimitMs, PendingIntent callbackIntent, int userId) {
-        mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
+        mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs,
+                callbackIntent,
                 userId);
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 294b750..f1e2281 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1744,8 +1744,8 @@
                     mGadgetProxy.linkToDeath(new UsbGadgetDeathRecipient(),
                             USB_GADGET_HAL_DEATH_COOKIE);
                     mCurrentFunctions = UsbManager.FUNCTION_NONE;
-                    mGadgetProxy.getCurrentUsbFunctions(new UsbGadgetCallback());
                     mCurrentUsbFunctionsRequested = true;
+                    mGadgetProxy.getCurrentUsbFunctions(new UsbGadgetCallback());
                 }
                 String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
                 updateState(state);
diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp
index b3b0900..59a80fb 100644
--- a/startop/iorap/Android.bp
+++ b/startop/iorap/Android.bp
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 java_library_static {
-  name: "libiorap-java",
+  name: "services.startop.iorap",
 
   aidl: {
     include_dirs: [
@@ -21,6 +21,8 @@
     ],
   },
 
+  libs: ["services.core"],
+
   srcs: [
       ":iorap-aidl",
       "**/*.java",
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
new file mode 100644
index 0000000..c2e4581
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
@@ -0,0 +1,368 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+// TODO: fix this. either move this class into system server or add a dependency on
+// these wm classes to libiorap-java and libiorap-java-tests (somehow).
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Objects;
+
+/**
+ * Provide a hint to iorapd that an app launch sequence has transitioned state.<br /><br />
+ *
+ * Knowledge of when an activity starts/stops can be used by iorapd to increase system
+ * performance (e.g. by launching perfetto tracing to record an io profile, or by
+ * playing back an ioprofile via readahead) over the long run.<br /><br />
+ *
+ * /@see com.google.android.startop.iorap.IIorap#onAppLaunchEvent <br /><br />
+ * @see com.android.server.wm.ActivityMetricsLaunchObserver
+ *      ActivityMetricsLaunchObserver for the possible event states.
+ * @hide
+ */
+public abstract class AppLaunchEvent implements Parcelable {
+    @LongDef
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SequenceId {}
+
+    public final @SequenceId
+    long sequenceId;
+
+    protected AppLaunchEvent(@SequenceId long sequenceId) {
+        this.sequenceId = sequenceId;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof AppLaunchEvent) {
+            return equals((AppLaunchEvent) other);
+        }
+        return false;
+    }
+
+    protected boolean equals(AppLaunchEvent other) {
+        return sequenceId == other.sequenceId;
+    }
+
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() +
+                "{" + "sequenceId=" + Long.toString(sequenceId) +
+                toStringBody() + "}";
+    }
+
+    protected String toStringBody() { return ""; };
+
+    // List of possible variants:
+
+    public static final class IntentStarted extends AppLaunchEvent {
+        @NonNull
+        public final Intent intent;
+
+        public IntentStarted(@SequenceId long sequenceId, Intent intent) {
+            super(sequenceId);
+            this.intent = intent;
+
+            Objects.requireNonNull(intent, "intent");
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof IntentStarted) {
+                return intent.equals(((IntentStarted)other).intent) &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", intent=" + intent.toString();
+        }
+
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+            super.writeToParcelImpl(p, flags);
+            intent.writeToParcel(p, flags);
+        }
+
+        IntentStarted(Parcel p) {
+            super(p);
+            intent = Intent.CREATOR.createFromParcel(p);
+        }
+    }
+
+    public static final class IntentFailed extends AppLaunchEvent {
+        public IntentFailed(@SequenceId long sequenceId) {
+            super(sequenceId);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof IntentFailed) {
+                return super.equals(other);
+            }
+            return false;
+        }
+
+        IntentFailed(Parcel p) {
+            super(p);
+        }
+    }
+
+    public static abstract class BaseWithActivityRecordData extends AppLaunchEvent {
+        public final @NonNull
+        @ActivityRecordProto byte[] activityRecordSnapshot;
+
+        protected BaseWithActivityRecordData(@SequenceId long sequenceId,
+                @NonNull @ActivityRecordProto byte[] snapshot) {
+            super(sequenceId);
+            activityRecordSnapshot = snapshot;
+
+            Objects.requireNonNull(snapshot, "snapshot");
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof BaseWithActivityRecordData) {
+                return activityRecordSnapshot.equals(
+                        ((BaseWithActivityRecordData)other).activityRecordSnapshot) &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", " + activityRecordSnapshot.toString();
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags);
+        }
+
+        BaseWithActivityRecordData(Parcel p) {
+            super(p);
+            activityRecordSnapshot = ActivityRecordProtoParcelable.create(p);
+        }
+    }
+
+    public static final class ActivityLaunched extends BaseWithActivityRecordData {
+        public final @ActivityMetricsLaunchObserver.Temperature
+        int temperature;
+
+        public ActivityLaunched(@SequenceId long sequenceId,
+                @NonNull @ActivityRecordProto byte[] snapshot, 
+                @ActivityMetricsLaunchObserver.Temperature int temperature) {
+            super(sequenceId, snapshot);
+            this.temperature = temperature;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ActivityLaunched) {
+                return temperature == ((ActivityLaunched)other).temperature &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", temperature=" + Integer.toString(temperature);
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           p.writeInt(temperature);
+        }
+
+        ActivityLaunched(Parcel p) {
+            super(p);
+            temperature = p.readInt();
+        }
+    }
+
+    public static final class ActivityLaunchFinished extends BaseWithActivityRecordData {
+        public ActivityLaunchFinished(@SequenceId long sequenceId,
+                @NonNull @ActivityRecordProto byte[] snapshot) {
+            super(sequenceId, snapshot);
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ActivityLaunched) {
+                return super.equals(other);
+            }
+            return false;
+        }
+    }
+
+     public static class ActivityLaunchCancelled extends AppLaunchEvent {
+        public final @Nullable
+        @ActivityRecordProto byte[] activityRecordSnapshot;
+
+        public ActivityLaunchCancelled(@SequenceId long sequenceId,
+                @Nullable @ActivityRecordProto byte[] snapshot) {
+            super(sequenceId);
+            activityRecordSnapshot = snapshot;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ActivityLaunchCancelled) {
+                return Objects.equals(activityRecordSnapshot,
+                        ((ActivityLaunchCancelled)other).activityRecordSnapshot) &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", " + activityRecordSnapshot.toString();
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           if (activityRecordSnapshot != null) {
+               p.writeBoolean(true);
+               ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags);
+           } else {
+               p.writeBoolean(false);
+           }
+        }
+
+        ActivityLaunchCancelled(Parcel p) {
+            super(p);
+            if (p.readBoolean()) {
+                activityRecordSnapshot = ActivityRecordProtoParcelable.create(p);
+            } else {
+                activityRecordSnapshot = null;
+            }
+        }
+    }
+
+    @Override
+    public @ContentsFlags int describeContents() { return 0; }
+
+    @Override
+    public void writeToParcel(Parcel p, @WriteFlags int flags) {
+        p.writeInt(getTypeIndex());
+
+        writeToParcelImpl(p, flags);
+    }
+
+
+    public static Creator<AppLaunchEvent> CREATOR =
+            new Creator<AppLaunchEvent>() {
+        @Override
+        public AppLaunchEvent createFromParcel(Parcel source) {
+            int typeIndex = source.readInt();
+
+            Class<?> kls = getClassFromTypeIndex(typeIndex);
+            if (kls == null) {
+                throw new IllegalArgumentException("Invalid type index: " + typeIndex);
+            }
+
+            try {
+                return (AppLaunchEvent) kls.getConstructor(Parcel.class).newInstance(source);
+            } catch (InstantiationException e) {
+                throw new AssertionError(e);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError(e);
+            } catch (InvocationTargetException e) {
+                throw new AssertionError(e);
+            } catch (NoSuchMethodException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public AppLaunchEvent[] newArray(int size) {
+            return new AppLaunchEvent[0];
+        }
+    };
+
+    protected void writeToParcelImpl(Parcel p, int flags) {
+        p.writeLong(sequenceId);
+    }
+
+    protected AppLaunchEvent(Parcel p) {
+        sequenceId = p.readLong();
+    }
+
+    private int getTypeIndex() {
+        for (int i = 0; i < sTypes.length; ++i) {
+            if (sTypes[i].equals(this.getClass())) {
+                return i;
+            }
+        }
+        throw new AssertionError("sTypes did not include this type: " + this.getClass());
+    }
+
+    private static @Nullable Class<?> getClassFromTypeIndex(int typeIndex) {
+        if (typeIndex >= 0 && typeIndex < sTypes.length) {
+            return sTypes[typeIndex];
+        }
+        return null;
+    }
+
+    // Index position matters: It is used to encode the specific type in parceling.
+    // Keep up-to-date with C++ side.
+    private static Class<?>[] sTypes = new Class[] {
+            IntentStarted.class,
+            IntentFailed.class,
+            ActivityLaunched.class,
+            ActivityLaunchFinished.class,
+            ActivityLaunchCancelled.class,
+    };
+
+    // TODO: move to @ActivityRecordProto byte[] once we have unit tests.
+    public static class ActivityRecordProtoParcelable {
+        public static void write(Parcel p, @ActivityRecordProto byte[] activityRecordSnapshot,
+                int flags) {
+            p.writeByteArray(activityRecordSnapshot);
+        }
+
+        public static @ActivityRecordProto byte[] create(Parcel p) {
+            byte[] data = p.createByteArray();
+
+            return data;
+        }
+    }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
new file mode 100644
index 0000000..7fcad36
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -0,0 +1,216 @@
+/*
+ * 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.google.android.startop.iorap;
+// TODO: rename to com.android.server.startop.iorap
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
+import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+/**
+ * System-server-local proxy into the {@code IIorap} native service.
+ */
+public class IorapForwardingService extends SystemService {
+
+    public static final boolean DEBUG = true; // TODO: read from a getprop?
+    public static final String TAG = "IorapForwardingService";
+
+    private IIorap mIorapRemote;
+
+    /**
+     * Initializes the system service.
+     * <p>
+     * Subclasses must define a single argument constructor that accepts the context
+     * and passes it to super.
+     * </p>
+     *
+     * @param context The system server context.
+     */
+    public IorapForwardingService(Context context) {
+       super(context);
+    }
+
+    //<editor-fold desc="Providers">
+    /*
+     * Providers for external dependencies:
+     * - These are marked as protected to allow tests to inject different values via mocks.
+     */
+
+    @VisibleForTesting
+    protected ActivityMetricsLaunchObserverRegistry provideLaunchObserverRegistry() {
+        ActivityTaskManagerInternal amtInternal =
+                LocalServices.getService(ActivityTaskManagerInternal.class);
+        ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
+                amtInternal.getLaunchObserverRegistry();
+        return launchObserverRegistry;
+    }
+
+    @VisibleForTesting
+    protected IIorap provideIorapRemote() {
+        try {
+            return IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"));
+        } catch (ServiceManager.ServiceNotFoundException e) {
+            // TODO: how do we handle service being missing?
+            throw new AssertionError(e);
+        }
+    }
+
+    //</editor-fold>
+
+    @Override
+    public void onStart() {
+        if (DEBUG) {
+            Log.v(TAG, "onStart");
+        }
+
+        // Connect to the native binder service.
+        mIorapRemote = provideIorapRemote();
+        invokeRemote( () -> mIorapRemote.setTaskListener(new RemoteTaskListener()) );
+
+        // Listen to App Launch Sequence events from ActivityTaskManager,
+        // and forward them to the native binder service.
+        ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
+                provideLaunchObserverRegistry();
+        launchObserverRegistry.registerLaunchObserver(new AppLaunchObserver());
+    }
+
+    private class AppLaunchObserver implements ActivityMetricsLaunchObserver {
+        // We add a synthetic sequence ID here to make it easier to differentiate new
+        // launch sequences on the native side.
+        private @AppLaunchEvent.SequenceId long mSequenceId = -1;
+
+        @Override
+        public void onIntentStarted(@NonNull Intent intent) {
+            // #onIntentStarted [is the only transition that] initiates a new launch sequence.
+            ++mSequenceId;
+
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s)",
+                        mSequenceId, intent));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                        new AppLaunchEvent.IntentStarted(mSequenceId, intent))
+            );
+        }
+
+        @Override
+        public void onIntentFailed() {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onIntentFailed(%d)", mSequenceId));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                        new AppLaunchEvent.IntentFailed(mSequenceId))
+            );
+        }
+
+        @Override
+        public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
+                @Temperature int temperature) {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunched(%d, %s, %d)",
+                        mSequenceId, activity, temperature));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                            new AppLaunchEvent.ActivityLaunched(mSequenceId, activity, temperature))
+            );
+        }
+
+        @Override
+        public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchCancelled(%d, %s)",
+                        mSequenceId, activity));
+            }
+
+            invokeRemote(() ->
+                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                            new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId,
+                                    activity)));
+        }
+
+        @Override
+        public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)",
+                        mSequenceId, activity));
+            }
+
+            invokeRemote(() ->
+                mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                        new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId, activity))
+            );
+        }
+    }
+
+    private class RemoteTaskListener extends ITaskListener.Stub {
+        @Override
+        public void onProgress(RequestId requestId, TaskResult result) throws RemoteException {
+            if (DEBUG) {
+                Log.v(TAG,
+                        String.format("RemoteTaskListener#onProgress(%s, %s)", requestId, result));
+            }
+
+            // TODO: implement rest.
+        }
+
+        @Override
+        public void onComplete(RequestId requestId, TaskResult result) throws RemoteException {
+            if (DEBUG) {
+                Log.v(TAG,
+                        String.format("RemoteTaskListener#onComplete(%s, %s)", requestId, result));
+            }
+
+            // TODO: implement rest.
+        }
+    }
+
+    private interface RemoteRunnable {
+        void run() throws RemoteException;
+    }
+
+    private static void invokeRemote(RemoteRunnable r) {
+       try {
+           r.run();
+       } catch (RemoteException e) {
+           // TODO: what do we do with exceptions?
+           throw new AssertionError("not implemented", e);
+       }
+    }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
index 2c79319..adb3a91 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
@@ -71,7 +71,7 @@
 
     @Override
     public String toString() {
-        return String.format("{requestId: %ld}", requestId);
+        return String.format("{requestId: %d}", requestId);
     }
 
     @Override
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
index 7605784..5ac4a46 100644
--- a/startop/iorap/tests/Android.bp
+++ b/startop/iorap/tests/Android.bp
@@ -18,8 +18,15 @@
     srcs: ["src/**/*.kt"],
 
     static_libs: [
-      // non-test dependencies
-      "libiorap-java",
+      // Non-test dependencies
+
+      // library under test
+      "services.startop.iorap",
+      // need the system_server code to be on the classpath,
+      "services.core",
+
+      // Test Dependencies
+
       // test android dependencies
       "platform-test-annotations",
       "android-support-test",
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 91cec554..82056e9 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -34,6 +34,7 @@
     defaults: ["viewcompiler_defaults"],
     srcs: [
         "dex_builder.cc",
+        "dex_layout_compiler.cc",
         "java_lang_builder.cc",
         "tinyxml_layout_parser.cc",
         "util.cc",
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index 4449ea0..d4f38ed 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -14,16 +14,30 @@
 // limitations under the License.
 //
 
+genrule {
+    name: "generate_compiled_layout",
+    tools: [":viewcompiler"],
+    cmd: "$(location :viewcompiler) $(in) --dex --out $(out) --package android.startop.test",
+    srcs: ["res/layout/layout1.xml"],
+    out: [
+        "layout1.dex",
+    ],
+}
+
 android_test {
     name: "dex-builder-test",
-    srcs: ["src/android/startop/test/DexBuilderTest.java"],
+    srcs: [
+        "src/android/startop/test/DexBuilderTest.java",
+        "src/android/startop/test/LayoutCompilerTest.java",
+    ],
     sdk_version: "current",
-    data: [":generate_dex_testcases"],
+    data: [":generate_dex_testcases", ":generate_compiled_layout"],
     static_libs: [
         "android-support-test",
         "guava",
     ],
     manifest: "AndroidManifest.xml",
+    resource_dirs: ["res"],
     test_config: "AndroidTest.xml",
     test_suites: ["general-tests"],
 }
diff --git a/startop/view_compiler/dex_builder_test/AndroidTest.xml b/startop/view_compiler/dex_builder_test/AndroidTest.xml
index 6f90cf3..68d8fdc 100644
--- a/startop/view_compiler/dex_builder_test/AndroidTest.xml
+++ b/startop/view_compiler/dex_builder_test/AndroidTest.xml
@@ -25,6 +25,7 @@
         <option name="cleanup" value="true" />
         <option name="push" value="trivial.dex->/data/local/tmp/dex-builder-test/trivial.dex" />
         <option name="push" value="simple.dex->/data/local/tmp/dex-builder-test/simple.dex" />
+        <option name="push" value="layout1.dex->/data/local/tmp/dex-builder-test/layout1.dex" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/startop/view_compiler/dex_builder_test/res/layout/layout1.xml b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml
new file mode 100644
index 0000000..0f9375c
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+   android:layout_width="match_parent"
+   android:layout_height="match_parent"
+   android:paddingLeft="16dp"
+   android:paddingRight="16dp"
+   android:orientation="vertical"
+   android:gravity="center">
+
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+ </LinearLayout>
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
new file mode 100644
index 0000000..ce3ce83
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
@@ -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.
+ */
+
+package android.startop.test;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.view.View;
+import com.google.common.io.ByteStreams;
+import dalvik.system.InMemoryDexClassLoader;
+import dalvik.system.PathClassLoader;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import org.junit.Assert;
+import org.junit.Test;
+
+// Adding tests here requires changes in several other places. See README.md in
+// the view_compiler directory for more information.
+public class LayoutCompilerTest {
+    static ClassLoader loadDexFile(String filename) throws Exception {
+        return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
+                ClassLoader.getSystemClassLoader());
+    }
+
+    @Test
+    public void loadAndInflaterLayout1() throws Exception {
+        ClassLoader dex_file = loadDexFile("layout1.dex");
+        Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView");
+        Method layout1 = compiled_view.getMethod("layout1", Context.class, int.class);
+        Context context = InstrumentationRegistry.getTargetContext();
+        layout1.invoke(null, context, R.layout.layout1);
+    }
+}
diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc
new file mode 100644
index 0000000..c68793d
--- /dev/null
+++ b/startop/view_compiler/dex_layout_compiler.cc
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_layout_compiler.h"
+#include "layout_validation.h"
+
+#include "android-base/stringprintf.h"
+
+namespace startop {
+
+using android::base::StringPrintf;
+
+void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) {
+  if (0 == name.compare(u"merge")) {
+    message_ = "Merge tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"include")) {
+    message_ = "Include tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"view")) {
+    message_ = "View tags are not supported";
+    can_compile_ = false;
+  }
+  if (0 == name.compare(u"fragment")) {
+    message_ = "Fragment tags are not supported";
+    can_compile_ = false;
+  }
+}
+
+DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
+    : method_{method},
+      context_{dex::Value::Parameter(0)},
+      resid_{dex::Value::Parameter(1)},
+      inflater_{method->MakeRegister()},
+      xml_{method->MakeRegister()},
+      attrs_{method->MakeRegister()},
+      classname_tmp_{method->MakeRegister()},
+      xml_next_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"), "next",
+          dex::Prototype{dex::TypeDescriptor::Int()})},
+      try_create_view_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "tryCreateView",
+          dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"),
+                         dex::TypeDescriptor::FromClassname("android.view.View"),
+                         dex::TypeDescriptor::FromClassname("java.lang.String"),
+                         dex::TypeDescriptor::FromClassname("android.content.Context"),
+                         dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
+      generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "generateLayoutParams",
+          dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
+                         dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
+      add_view_{method->dex_file()->GetOrDeclareMethod(
+          dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "addView",
+          dex::Prototype{
+              dex::TypeDescriptor::Void(),
+              dex::TypeDescriptor::FromClassname("android.view.View"),
+              dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})},
+      // The register stack starts with one register, which will be null for the root view.
+      register_stack_{{method->MakeRegister()}} {}
+
+void DexViewBuilder::Start() {
+  dex::DexBuilder* const dex = method_->dex_file();
+
+  // LayoutInflater inflater = LayoutInflater.from(context);
+  auto layout_inflater_from = dex->GetOrDeclareMethod(
+      dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
+      "from",
+      dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
+                     dex::TypeDescriptor::FromClassname("android.content.Context")});
+  method_->AddInstruction(
+      dex::Instruction::InvokeStaticObject(layout_inflater_from.id, /*dest=*/inflater_, context_));
+
+  // Resources res = context.getResources();
+  auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context");
+  auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources");
+  auto get_resources =
+      dex->GetOrDeclareMethod(context_type, "getResources", dex::Prototype{resources_type});
+  method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_resources.id, xml_, context_));
+
+  // XmlResourceParser xml = res.getLayout(resid);
+  auto xml_resource_parser_type =
+      dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
+  auto get_layout =
+      dex->GetOrDeclareMethod(resources_type,
+                              "getLayout",
+                              dex::Prototype{xml_resource_parser_type, dex::TypeDescriptor::Int()});
+  method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_layout.id, xml_, xml_, resid_));
+
+  // AttributeSet attrs = Xml.asAttributeSet(xml);
+  auto as_attribute_set = dex->GetOrDeclareMethod(
+      dex::TypeDescriptor::FromClassname("android.util.Xml"),
+      "asAttributeSet",
+      dex::Prototype{dex::TypeDescriptor::FromClassname("android.util.AttributeSet"),
+                     dex::TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
+  method_->AddInstruction(dex::Instruction::InvokeStaticObject(as_attribute_set.id, attrs_, xml_));
+
+  // xml.next(); // start document
+  method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+}
+
+void DexViewBuilder::Finish() {}
+
+namespace {
+std::string ResolveName(const std::string& name) {
+  if (name == "View") return "android.view.View";
+  if (name == "ViewGroup") return "android.view.ViewGroup";
+  if (name.find(".") == std::string::npos) {
+    return StringPrintf("android.widget.%s", name.c_str());
+  }
+  return name;
+}
+}  // namespace
+
+void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) {
+  bool const is_root_view = view_stack_.empty();
+
+  // xml.next(); // start tag
+  method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+
+  dex::Value view = AcquireRegister();
+  // try to create the view using the factories
+  method_->BuildConstString(classname_tmp_,
+                            name);  // TODO: the need to fully qualify the classname
+  if (is_root_view) {
+    dex::Value null = AcquireRegister();
+    method_->BuildConst4(null, 0);
+    method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+        try_create_view_.id, view, inflater_, null, classname_tmp_, context_, attrs_));
+    ReleaseRegister();
+  } else {
+    method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+        try_create_view_.id, view, inflater_, GetCurrentView(), classname_tmp_, context_, attrs_));
+  }
+  auto label = method_->MakeLabel();
+  // branch if not null
+  method_->AddInstruction(
+      dex::Instruction::OpWithArgs(dex::Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
+
+  // If null, create the class directly.
+  method_->BuildNew(view,
+                    dex::TypeDescriptor::FromClassname(ResolveName(name)),
+                    dex::Prototype{dex::TypeDescriptor::Void(),
+                                   dex::TypeDescriptor::FromClassname("android.content.Context"),
+                                   dex::TypeDescriptor::FromClassname("android.util.AttributeSet")},
+                    context_,
+                    attrs_);
+
+  method_->AddInstruction(
+      dex::Instruction::OpWithArgs(dex::Instruction::Op::kBindLabel, /*dest=*/{}, label));
+
+  if (is_viewgroup) {
+    // Cast to a ViewGroup so we can add children later.
+    const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(
+        dex::TypeDescriptor::FromClassname("android.view.ViewGroup").descriptor());
+    method_->AddInstruction(dex::Instruction::Cast(view, dex::Value::Type(view_group_def->orig_index)));
+  }
+
+  if (!is_root_view) {
+    // layout_params = parent.generateLayoutParams(attrs);
+    dex::Value layout_params{AcquireRegister()};
+    method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+        generate_layout_params_.id, layout_params, GetCurrentView(), attrs_));
+    view_stack_.push_back({view, layout_params});
+  } else {
+    view_stack_.push_back({view, {}});
+  }
+}
+
+void DexViewBuilder::FinishView() {
+  if (view_stack_.size() == 1) {
+    method_->BuildReturn(GetCurrentView(), /*is_object=*/true);
+  } else {
+    // parent.add(view, layout_params)
+    method_->AddInstruction(dex::Instruction::InvokeVirtual(
+        add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
+    // xml.next(); // end tag
+    method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+  }
+  PopViewStack();
+}
+
+dex::Value DexViewBuilder::AcquireRegister() {
+  top_register_++;
+  if (register_stack_.size() == top_register_) {
+    register_stack_.push_back(method_->MakeRegister());
+  }
+  return register_stack_[top_register_];
+}
+
+void DexViewBuilder::ReleaseRegister() { top_register_--; }
+
+dex::Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
+dex::Value DexViewBuilder::GetCurrentLayoutParams() const {
+  return view_stack_.back().layout_params.value();
+}
+dex::Value DexViewBuilder::GetParentView() const {
+  return view_stack_[view_stack_.size() - 2].view;
+}
+
+void DexViewBuilder::PopViewStack() {
+  const auto& top = view_stack_.back();
+  // release the layout params if we have them
+  if (top.layout_params.has_value()) {
+    ReleaseRegister();
+  }
+  // Unconditionally release the view register.
+  ReleaseRegister();
+  view_stack_.pop_back();
+}
+
+}  // namespace startop
\ No newline at end of file
diff --git a/startop/view_compiler/dex_layout_compiler.h b/startop/view_compiler/dex_layout_compiler.h
new file mode 100644
index 0000000..170a1a6
--- /dev/null
+++ b/startop/view_compiler/dex_layout_compiler.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEX_LAYOUT_COMPILER_H_
+#define DEX_LAYOUT_COMPILER_H_
+
+#include "dex_builder.h"
+
+#include <codecvt>
+#include <locale>
+#include <string>
+#include <vector>
+
+namespace startop {
+
+// This visitor does the actual view compilation, using a supplied builder.
+template <typename Builder>
+class LayoutCompilerVisitor {
+ public:
+  explicit LayoutCompilerVisitor(Builder* builder) : builder_{builder} {}
+
+  void VisitStartDocument() { builder_->Start(); }
+  void VisitEndDocument() { builder_->Finish(); }
+  void VisitStartTag(const std::u16string& name) {
+    parent_stack_.push_back(ViewEntry{
+        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(name), {}});
+  }
+  void VisitEndTag() {
+    auto entry = parent_stack_.back();
+    parent_stack_.pop_back();
+
+    if (parent_stack_.empty()) {
+      GenerateCode(entry);
+    } else {
+      parent_stack_.back().children.push_back(entry);
+    }
+  }
+
+ private:
+  struct ViewEntry {
+    std::string name;
+    std::vector<ViewEntry> children;
+  };
+
+  void GenerateCode(const ViewEntry& view) {
+    builder_->StartView(view.name, !view.children.empty());
+    for (const auto& child : view.children) {
+      GenerateCode(child);
+    }
+    builder_->FinishView();
+  }
+
+  Builder* builder_;
+
+  std::vector<ViewEntry> parent_stack_;
+};
+
+class DexViewBuilder {
+ public:
+  DexViewBuilder(dex::MethodBuilder* method);
+
+  void Start();
+  void Finish();
+  void StartView(const std::string& name, bool is_viewgroup);
+  void FinishView();
+
+ private:
+  // Accessors for the stack of views that are under construction.
+  dex::Value AcquireRegister();
+  void ReleaseRegister();
+  dex::Value GetCurrentView() const;
+  dex::Value GetCurrentLayoutParams() const;
+  dex::Value GetParentView() const;
+  void PopViewStack();
+
+  dex::MethodBuilder* method_;
+
+  // Registers used for code generation
+  dex::Value const context_;
+  dex::Value const resid_;
+  const dex::Value inflater_;
+  const dex::Value xml_;
+  const dex::Value attrs_;
+  const dex::Value classname_tmp_;
+
+  const dex::MethodDeclData xml_next_;
+  const dex::MethodDeclData try_create_view_;
+  const dex::MethodDeclData generate_layout_params_;
+  const dex::MethodDeclData add_view_;
+
+  // used for keeping track of which registers are in use
+  size_t top_register_{0};
+  std::vector<dex::Value> register_stack_;
+
+  // Keep track of the views currently in progress.
+  struct ViewEntry {
+    dex::Value view;
+    std::optional<dex::Value> layout_params;
+  };
+  std::vector<ViewEntry> view_stack_;
+};
+
+}  // namespace startop
+
+#endif  // DEX_LAYOUT_COMPILER_H_
diff --git a/startop/view_compiler/java_lang_builder.cc b/startop/view_compiler/java_lang_builder.cc
index 0b8754f..920caee 100644
--- a/startop/view_compiler/java_lang_builder.cc
+++ b/startop/view_compiler/java_lang_builder.cc
@@ -67,7 +67,7 @@
           "}\n";     // end CompiledView
 }
 
-void JavaLangViewBuilder::StartView(const string& class_name) {
+void JavaLangViewBuilder::StartView(const string& class_name, bool /*is_viewgroup*/) {
   const string view_var = MakeVar("view");
   const string layout_var = MakeVar("layout");
   std::string parent = "null";
diff --git a/startop/view_compiler/java_lang_builder.h b/startop/view_compiler/java_lang_builder.h
index c8d20b2..69356d3 100644
--- a/startop/view_compiler/java_lang_builder.h
+++ b/startop/view_compiler/java_lang_builder.h
@@ -35,7 +35,7 @@
   void Finish() const;
 
   // Begin creating a view (i.e. process the opening tag)
-  void StartView(const std::string& class_name);
+  void StartView(const std::string& class_name, bool is_viewgroup);
   // Finish a view, after all of its child nodes have been processed.
   void FinishView();
 
diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc
index 609bcf3..ae00187 100644
--- a/startop/view_compiler/main.cc
+++ b/startop/view_compiler/main.cc
@@ -16,8 +16,11 @@
 
 #include "gflags/gflags.h"
 
+#include "android-base/stringprintf.h"
 #include "dex_builder.h"
+#include "dex_layout_compiler.h"
 #include "java_lang_builder.h"
+#include "layout_validation.h"
 #include "tinyxml_layout_parser.h"
 #include "util.h"
 
@@ -32,6 +35,12 @@
 namespace {
 
 using namespace tinyxml2;
+using android::base::StringPrintf;
+using startop::dex::ClassBuilder;
+using startop::dex::DexBuilder;
+using startop::dex::MethodBuilder;
+using startop::dex::Prototype;
+using startop::dex::TypeDescriptor;
 using namespace startop::util;
 using std::string;
 
@@ -41,34 +50,44 @@
 DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
 DEFINE_string(package, "", "The package name for the generated class (required)");
 
-class ViewCompilerXmlVisitor : public XMLVisitor {
+template <typename Visitor>
+class XmlVisitorAdapter : public XMLVisitor {
  public:
-  explicit ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {}
+  explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
 
   bool VisitEnter(const XMLDocument& /*doc*/) override {
-    builder_->Start();
+    visitor_->VisitStartDocument();
     return true;
   }
 
   bool VisitExit(const XMLDocument& /*doc*/) override {
-    builder_->Finish();
+    visitor_->VisitEndDocument();
     return true;
   }
 
   bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override {
-    builder_->StartView(element.Name());
+    visitor_->VisitStartTag(
+        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
+            element.Name()));
     return true;
   }
 
   bool VisitExit(const XMLElement& /*element*/) override {
-    builder_->FinishView();
+    visitor_->VisitEndTag();
     return true;
   }
 
  private:
-  JavaLangViewBuilder* builder_;
+  Visitor* visitor_;
 };
 
+template <typename Builder>
+void CompileLayout(XMLDocument* xml, Builder* builder) {
+  startop::LayoutCompilerVisitor visitor{builder};
+  XmlVisitorAdapter<decltype(visitor)> adapter{&visitor};
+  xml->Accept(&adapter);
+}
+
 }  // end namespace
 
 int main(int argc, char** argv) {
@@ -88,16 +107,8 @@
     return 1;
   }
 
-  if (FLAGS_dex) {
-    startop::dex::WriteTestDexFile("test.dex");
-    return 0;
-  }
-
   const char* const filename = argv[kFileNameParam];
-  const string layout_name = FindLayoutNameFromFilename(filename);
-
-  // We want to generate Java language code to inflate exactly this layout. This means
-  // generating code to walk the resource XML too.
+  const string layout_name = startop::util::FindLayoutNameFromFilename(filename);
 
   XMLDocument xml;
   xml.LoadFile(filename);
@@ -108,15 +119,34 @@
     return 1;
   }
 
+  const bool is_stdout = FLAGS_out == kStdoutFilename;
+
   std::ofstream outfile;
-  if (FLAGS_out != kStdoutFilename) {
+  if (!is_stdout) {
     outfile.open(FLAGS_out);
   }
-  JavaLangViewBuilder builder{
-      FLAGS_package, layout_name, FLAGS_out == kStdoutFilename ? std::cout : outfile};
 
-  ViewCompilerXmlVisitor visitor{&builder};
-  xml.Accept(&visitor);
+  if (FLAGS_dex) {
+    DexBuilder dex_file;
+    string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str());
+    ClassBuilder compiled_view{dex_file.MakeClass(class_name)};
+    MethodBuilder method{compiled_view.CreateMethod(
+        layout_name,
+        Prototype{TypeDescriptor::FromClassname("android.view.View"),
+                  TypeDescriptor::FromClassname("android.content.Context"),
+                  TypeDescriptor::Int()})};
+    startop::DexViewBuilder builder{&method};
+    CompileLayout(&xml, &builder);
+    method.Encode();
 
+    slicer::MemView image{dex_file.CreateImage()};
+
+    (is_stdout ? std::cout : outfile).write(image.ptr<const char>(), image.size());
+  } else {
+    // Generate Java language output.
+    JavaLangViewBuilder builder{FLAGS_package, layout_name, is_stdout ? std::cout : outfile};
+
+    CompileLayout(&xml, &builder);
+  }
   return 0;
 }
diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
index b906d0b..3299117 100644
--- a/telecomm/java/android/telecom/CallRedirectionService.java
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.app.Service;
 import android.content.Intent;
@@ -27,8 +28,8 @@
 import android.os.RemoteException;
 
 import com.android.internal.os.SomeArgs;
-import com.android.internal.telecom.ICallRedirectionService;
 import com.android.internal.telecom.ICallRedirectionAdapter;
+import com.android.internal.telecom.ICallRedirectionService;
 
 /**
  * This service can be implemented to interact between Telecom and its implementor
@@ -62,22 +63,35 @@
 
     /**
      * Telecom calls this method to inform the implemented {@link CallRedirectionService} of
-     * a new outgoing call which is being placed.
+     * a new outgoing call which is being placed. Telecom does not request to redirect emergency
+     * calls and does not request to redirect calls with gateway information.
      *
-     * The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()},
-     * {@link #redirectCall(Uri, PhoneAccountHandle)}, and {@link #cancelCall()} only from here.
+     * <p>Telecom will cancel the call if Telecom does not receive a response in 5 seconds from
+     * the implemented {@link CallRedirectionService} set by users.
      *
-     * @param handle the phone number dialed by the user
-     * @param targetPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
+     * <p>The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()},
+     * {@link #redirectCall(Uri, PhoneAccountHandle, boolean)}, and {@link #cancelCall()} only
+     * from here.
+     *
+     * @param handle the phone number dialed by the user, represented in E.164 format if possible
+     * @param initialPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
+     * @param allowInteractiveResponse a boolean to tell if the implemented
+     *                                 {@link CallRedirectionService} should allow interactive
+     *                                 responses with users. Will be {@code false} if, for example
+     *                                 the device is in car mode and the user would not be able to
+     *                                 interact with their device.
      */
-    public abstract void onPlaceCall(Uri handle, PhoneAccountHandle targetPhoneAccount);
+    public abstract void onPlaceCall(@NonNull Uri handle,
+                                     @NonNull PhoneAccountHandle initialPhoneAccount,
+                                     boolean allowInteractiveResponse);
 
     /**
      * The implemented {@link CallRedirectionService} calls this method to response a request
-     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that no changes
-     * are required to the outgoing call, and that the call should be placed as-is.
+     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+     * no changes are required to the outgoing call, and that the call should be placed as-is.
      *
-     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     * <p>This can only be called from implemented
+     * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
      *
      */
     public final void placeCallUnmodified() {
@@ -89,29 +103,39 @@
 
     /**
      * The implemented {@link CallRedirectionService} calls this method to response a request
-     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that changes
-     * are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing call.
+     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+     * changes are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing
+     * call. Telecom will cancel the call if the implemented {@link CallRedirectionService}
+     * replies Telecom a handle for an emergency number.
      *
-     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     * <p>This can only be called from implemented
+     * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
      *
      * @param handle the new phone number to dial
      * @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call.
      *                           If {@code null}, no change will be made to the
      *                           {@link PhoneAccountHandle} used to place the call.
+     * @param confirmFirst Telecom will ask users to confirm the redirection via a yes/no dialog
+     *                     if the confirmFirst is true, and if the redirection request of this
+     *                     response was sent with a true flag of allowInteractiveResponse via
+     *                     {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}
      */
-    public final void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) {
+    public final void redirectCall(@NonNull Uri handle,
+                                   @NonNull PhoneAccountHandle targetPhoneAccount,
+                                   boolean confirmFirst) {
         try {
-            mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount);
+            mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount, confirmFirst);
         } catch (RemoteException e) {
         }
     }
 
     /**
      * The implemented {@link CallRedirectionService} calls this method to response a request
-     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that an outgoing
-     * call should be canceled entirely.
+     * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+     * an outgoing call should be canceled entirely.
      *
-     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     * <p>This can only be called from implemented
+     * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
      *
      */
     public final void cancelCall() {
@@ -137,7 +161,8 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1;
-                        onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3);
+                        onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3,
+                                (boolean) args.arg4);
                     } finally {
                         args.recycle();
                     }
@@ -152,15 +177,20 @@
          * Telecom calls this method to inform the CallRedirectionService of a new outgoing call
          * which is about to be placed.
          * @param handle the phone number dialed by the user
-         * @param targetPhoneAccount the URI of the number the user dialed
+         * @param initialPhoneAccount the URI of the number the user dialed
+         * @param allowInteractiveResponse a boolean to tell if the implemented
+         *                                 {@link CallRedirectionService} should allow interactive
+         *                                 responses with users.
          */
         @Override
-        public void placeCall(ICallRedirectionAdapter adapter, Uri handle,
-                              PhoneAccountHandle targetPhoneAccount) {
+        public void placeCall(@NonNull ICallRedirectionAdapter adapter, @NonNull Uri handle,
+                              @NonNull PhoneAccountHandle initialPhoneAccount,
+                              boolean allowInteractiveResponse) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = adapter;
             args.arg2 = handle;
-            args.arg3 = targetPhoneAccount;
+            args.arg3 = initialPhoneAccount;
+            args.arg4 = allowInteractiveResponse;
             mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget();
         }
     }
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
index 46bf983..0a42a3f 100644
--- a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
@@ -31,5 +31,6 @@
 
     void placeCallUnmodified();
 
-    void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount);
+    void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount,
+            boolean confirmFirst);
 }
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
index d8d360b..c1bc440 100644
--- a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
@@ -30,5 +30,5 @@
  */
 oneway interface ICallRedirectionService {
     void placeCall(in ICallRedirectionAdapter adapter, in Uri handle,
-            in PhoneAccountHandle targetPhoneAccount);
+            in PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse);
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a9a128b..312b318 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2231,7 +2231,7 @@
      * e.g.) To use RSCP by default, set the value to "rscp". The signal strength level will
      * then be determined by #KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY
      * <p>
-     * Currently this only supports the value "rscp"
+     * Currently this supports the value "rscp" and "rssi".
      * @hide
      */
     // FIXME: this key and related keys must not be exposed without a consistent philosophy for
@@ -2875,7 +2875,7 @@
                         -95, /* SIGNAL_STRENGTH_GOOD */
                         -85  /* SIGNAL_STRENGTH_GREAT */
                 });
-        sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "");
+        sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi");
         sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false);
         sDefaults.putBoolean(KEY_CALL_FORWARDING_OVER_UT_WARNING_BOOL, false);
diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java
index f2c14a1..e6182ed 100644
--- a/telephony/java/android/telephony/CellSignalStrength.java
+++ b/telephony/java/android/telephony/CellSignalStrength.java
@@ -42,6 +42,9 @@
     public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
 
     /** @hide */
+    protected static final int NUM_SIGNAL_STRENGTH_THRESHOLDS = NUM_SIGNAL_STRENGTH_BINS - 1;
+
+    /** @hide */
     public static final String[] SIGNAL_STRENGTH_NAMES = {
         "none", "poor", "moderate", "good", "great"
     };
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 88f6fbc..0760407 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -21,6 +21,7 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.telephony.Rlog;
+import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -41,8 +42,18 @@
     private static final int WCDMA_RSSI_POOR = -107;
     private static final int WCDMA_RSSI_MIN = -113;
 
-    private static final int WCDMA_RSCP_MIN = -120;
+    private static final int[] sRssiThresholds = new int[]{
+            WCDMA_RSSI_POOR, WCDMA_RSSI_MODERATE, WCDMA_RSSI_GOOD, WCDMA_RSSI_GREAT};
+
     private static final int WCDMA_RSCP_MAX = -24;
+    private static final int WCDMA_RSCP_GREAT = -85;
+    private static final int WCDMA_RSCP_GOOD = -95;
+    private static final int WCDMA_RSCP_MODERATE = -105;
+    private static final int WCDMA_RSCP_POOR = -115;
+    private static final int WCDMA_RSCP_MIN = -120;
+
+    private static final int[] sRscpThresholds = new int[] {
+            WCDMA_RSCP_POOR, WCDMA_RSCP_MODERATE, WCDMA_RSCP_GOOD, WCDMA_RSCP_GREAT};
 
     // TODO: Because these are used as values in CarrierConfig, they should be exposed somehow.
     /** @hide */
@@ -54,6 +65,9 @@
     /** @hide */
     public static final String LEVEL_CALCULATION_METHOD_RSCP = "rscp";
 
+    // Default to RSSI for backwards compatibility with older devices
+    private static final String sLevelCalculationMethod = LEVEL_CALCULATION_METHOD_RSSI;
+
     private int mRssi; // in dBm [-113, 51] or CellInfo.UNAVAILABLE if unknown
     private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
                                // CellInfo.UNAVAILABLE if unknown
@@ -121,10 +135,6 @@
         mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
-    private static final String sLevelCalculationMethod = LEVEL_CALCULATION_METHOD_RSSI;
-    private static final int[] sThresholds = new int[]{
-            WCDMA_RSSI_POOR, WCDMA_RSSI_GOOD, WCDMA_RSSI_GOOD, WCDMA_RSSI_GREAT};
-
     /**
      * Retrieve an abstract level value for the overall signal strength.
      *
@@ -140,41 +150,46 @@
     @Override
     public void updateLevel(PersistableBundle cc, ServiceState ss) {
         String calcMethod;
-        int[] thresholds;
+        int[] rscpThresholds;
 
         if (cc == null) {
             calcMethod = sLevelCalculationMethod;
-            thresholds = sThresholds;
+            rscpThresholds = sRscpThresholds;
         } else {
             // TODO: abstract this entire thing into a series of functions
             calcMethod = cc.getString(
                     CarrierConfigManager.KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING,
                     sLevelCalculationMethod);
-            thresholds = cc.getIntArray(
+            if (TextUtils.isEmpty(calcMethod)) calcMethod = sLevelCalculationMethod;
+            rscpThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY);
-            if (thresholds == null) thresholds = sThresholds;
+            if (rscpThresholds == null || rscpThresholds.length != NUM_SIGNAL_STRENGTH_THRESHOLDS) {
+                rscpThresholds = sRscpThresholds;
+            }
         }
 
-        int level = thresholds.length;
+        int level = NUM_SIGNAL_STRENGTH_THRESHOLDS;
         switch (calcMethod) {
             case LEVEL_CALCULATION_METHOD_RSCP:
                 if (mRscp < WCDMA_RSCP_MIN || mRscp > WCDMA_RSCP_MAX) {
                     mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
                     return;
                 }
-                while (level > 0 && mRscp < thresholds[level - 1]) level--;
+                while (level > 0 && mRscp < rscpThresholds[level - 1]) level--;
                 mLevel = level;
                 return;
+            default:
+                loge("Invalid Level Calculation Method for CellSignalStrengthWcdma = "
+                        + calcMethod);
+                /** fall through */
             case LEVEL_CALCULATION_METHOD_RSSI:
                 if (mRssi < WCDMA_RSSI_MIN || mRssi > WCDMA_RSSI_MAX) {
                     mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
                     return;
                 }
-                while (level > 0 && mRssi < thresholds[level - 1]) level--;
+                while (level > 0 && mRssi < sRssiThresholds[level - 1]) level--;
                 mLevel = level;
                 return;
-            default:
-                mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         }
     }
 
@@ -204,7 +219,7 @@
     }
 
     /**
-     * Get the signal strength as dBm
+     * Get the RSSI as dBm
      *
      * @hide
      */
@@ -214,12 +229,32 @@
 
     /**
      * Get the RSCP as dBm
+     *
      * @hide
      */
     public int getRscp() {
         return mRscp;
     }
 
+    /**
+     * Get the Ec/No as dB
+     *
+     * @hide
+     */
+    public int getEcNo() {
+        return mEcNo;
+    }
+
+    /**
+     * Return the Bit Error Rate
+     *
+     * @returns the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or UNAVAILABLE.
+     * @hide
+     */
+    public int getBitErrorRate() {
+        return mBitErrorRate;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(mRssi, mBitErrorRate, mRscp, mEcNo, mLevel);
@@ -304,9 +339,16 @@
     };
 
     /**
-     * log
+     * log warning
      */
     private static void log(String s) {
         Rlog.w(LOG_TAG, s);
     }
+
+    /**
+     * log error
+     */
+    private static void loge(String s) {
+        Rlog.e(LOG_TAG, s);
+    }
 }
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index b258f52..8373899 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -17,10 +17,11 @@
 package android.telephony;
 
 import android.annotation.UnsupportedAppUsage;
+import android.net.LinkProperties;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.TelephonyManager;
-import android.net.LinkProperties;
+
+import java.util.Objects;
 
 /**
  * Contains precise data connection state.
@@ -32,7 +33,6 @@
  *   <li>Network type of the connection.
  *   <li>APN type.
  *   <li>APN.
- *   <li>Data connection change reason.
  *   <li>The properties of the network link.
  *   <li>Data connection fail cause.
  * </ul>
@@ -45,7 +45,6 @@
     private int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
     private String mAPNType = "";
     private String mAPN = "";
-    private String mReason = "";
     private LinkProperties mLinkProperties = null;
     private String mFailCause = "";
 
@@ -55,14 +54,12 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public PreciseDataConnectionState(int state, int networkType,
-            String apnType, String apn, String reason,
-            LinkProperties linkProperties, String failCause) {
+    public PreciseDataConnectionState(int state, int networkType, String apnType, String apn,
+                                      LinkProperties linkProperties, String failCause) {
         mState = state;
         mNetworkType = networkType;
         mAPNType = apnType;
         mAPN = apn;
-        mReason = reason;
         mLinkProperties = linkProperties;
         mFailCause = failCause;
     }
@@ -83,7 +80,6 @@
         mNetworkType = in.readInt();
         mAPNType = in.readString();
         mAPN = in.readString();
-        mReason = in.readString();
         mLinkProperties = (LinkProperties)in.readParcelable(null);
         mFailCause = in.readString();
     }
@@ -144,14 +140,6 @@
     }
 
     /**
-     * Get data connection change reason.
-     */
-    @UnsupportedAppUsage
-    public String getDataConnectionChangeReason() {
-        return mReason;
-    }
-
-    /**
      * Get the properties of the network link.
      */
     @UnsupportedAppUsage
@@ -178,7 +166,6 @@
         out.writeInt(mNetworkType);
         out.writeString(mAPNType);
         out.writeString(mAPN);
-        out.writeString(mReason);
         out.writeParcelable(mLinkProperties, flags);
         out.writeString(mFailCause);
     }
@@ -197,16 +184,7 @@
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + mState;
-        result = prime * result + mNetworkType;
-        result = prime * result + ((mAPNType == null) ? 0 : mAPNType.hashCode());
-        result = prime * result + ((mAPN == null) ? 0 : mAPN.hashCode());
-        result = prime * result + ((mReason == null) ? 0 : mReason.hashCode());
-        result = prime * result + ((mLinkProperties == null) ? 0 : mLinkProperties.hashCode());
-        result = prime * result + ((mFailCause == null) ? 0 : mFailCause.hashCode());
-        return result;
+        return Objects.hash(mState, mNetworkType, mAPNType, mAPN, mLinkProperties, mFailCause);
     }
 
     @Override
@@ -252,13 +230,6 @@
         if (mNetworkType != other.mNetworkType) {
             return false;
         }
-        if (mReason == null) {
-            if (other.mReason != null) {
-                return false;
-            }
-        } else if (!mReason.equals(other.mReason)) {
-            return false;
-        }
         if (mState != other.mState) {
             return false;
         }
@@ -273,7 +244,6 @@
         sb.append(", Network type: " + mNetworkType);
         sb.append(", APN type: " + mAPNType);
         sb.append(", APN: " + mAPN);
-        sb.append(", Change reason: " + mReason);
         sb.append(", Link properties: " + mLinkProperties);
         sb.append(", Fail cause: " + mFailCause);
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4598afa..8dfdb2f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -772,7 +772,6 @@
      * The {@link #EXTRA_DATA_NETWORK_TYPE} extra indicates the connection network type.
      * The {@link #EXTRA_DATA_APN_TYPE} extra indicates the APN type.
      * The {@link #EXTRA_DATA_APN} extra indicates the APN.
-     * The {@link #EXTRA_DATA_CHANGE_REASON} extra indicates the connection change reason.
      * The {@link #EXTRA_DATA_IFACE_PROPERTIES} extra indicates the connection interface.
      * The {@link #EXTRA_DATA_FAILURE_CAUSE} extra indicates the connection fail cause.
      *
@@ -783,7 +782,6 @@
      * @see #EXTRA_DATA_NETWORK_TYPE
      * @see #EXTRA_DATA_APN_TYPE
      * @see #EXTRA_DATA_APN
-     * @see #EXTRA_DATA_CHANGE_REASON
      * @see #EXTRA_DATA_IFACE
      * @see #EXTRA_DATA_FAILURE_CAUSE
      * @hide
@@ -873,18 +871,6 @@
 
     /**
      * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an String representation of the change reason.
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getStringExtra(String name)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_CHANGE_REASON = PhoneConstants.STATE_CHANGE_REASON_KEY;
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
      * for an String representation of the data interface.
      *
      * <p class="note">
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
index 5122c7d..709b3aa 100644
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java
+++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
@@ -23,6 +23,10 @@
  * @hide - TODO(sahinc) make this public
  */
 public class Rcs1To1Thread extends RcsThread {
+    public Rcs1To1Thread(int threadId) {
+        super(threadId);
+    }
+
     public static final Creator<Rcs1To1Thread> CREATOR = new Creator<Rcs1To1Thread>() {
         @Override
         public Rcs1To1Thread createFromParcel(Parcel in) {
@@ -36,6 +40,7 @@
     };
 
     protected Rcs1To1Thread(Parcel in) {
+        super(in);
     }
 
     @Override
@@ -45,5 +50,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(RCS_1_TO_1_TYPE);
+        super.writeToParcel(dest, flags);
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java
index 150c4d4..d954b2d 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThread.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThread.java
@@ -36,6 +36,7 @@
     };
 
     protected RcsGroupThread(Parcel in) {
+        super(in);
     }
 
     @Override
@@ -45,5 +46,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(RCS_GROUP_TYPE);
+        super.writeToParcel(dest, flags);
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java
index 4198c78..1bf6ffd 100644
--- a/telephony/java/android/telephony/ims/RcsMessageStore.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStore.java
@@ -16,6 +16,8 @@
 
 package android.telephony.ims;
 
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.Rlog;
@@ -27,26 +29,93 @@
  * @hide - TODO make this public
  */
 public class RcsMessageStore {
-    private static final String TAG = "RcsMessageStore";
-    private static final boolean VDBG = false;
+    static final String TAG = "RcsMessageStore";
 
     /**
-     * Delete the RcsThread identified by the given threadId.
+     * Returns the first chunk of existing {@link RcsThread}s in the common storage.
+     * @param queryParameters Parameters to specify to return a subset of all RcsThreads.
+     *                        Passing a value of null will return all threads.
+     */
+    @WorkerThread
+    public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParameters queryParameters) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.getRcsThreads(queryParameters);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the next chunk of {@link RcsThread}s in the common storage.
+     * @param continuationToken A token to continue the query to get the next chunk. This is
+     *                          obtained through {@link RcsThreadQueryResult#nextChunkToken}.
+     */
+    @WorkerThread
+    public RcsThreadQueryResult getRcsThreads(RcsThreadQueryContinuationToken continuationToken) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.getRcsThreadsWithToken(continuationToken);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates a new 1 to 1 thread with the given participant and persists it in the storage.
+     */
+    @WorkerThread
+    public Rcs1To1Thread createRcs1To1Thread(RcsParticipant recipient) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.createRcs1To1Thread(recipient);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcs1To1Thread", re);
+        }
+
+        return null;
+    }
+
+    /**
+     * Delete the {@link RcsThread} identified by the given threadId.
      * @param threadId threadId of the thread to be deleted.
      */
+    @WorkerThread
     public void deleteThread(int threadId) {
-        if (VDBG) logd("deleteThread: threadId: " + threadId);
         try {
             IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
             if (iRcs != null) {
                 iRcs.deleteThread(threadId);
             }
         } catch (RemoteException re) {
-
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during deleteThread", re);
         }
     }
 
-    private static void logd(String msg) {
-        Rlog.d(TAG, msg);
+    /**
+     * Creates a new participant and persists it in the storage.
+     * @param canonicalAddress The defining address (e.g. phone number) of the participant.
+     */
+    public RcsParticipant createRcsParticipant(String canonicalAddress) {
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                return iRcs.createRcsParticipant(canonicalAddress);
+            }
+        } catch (RemoteException re) {
+            Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcsParticipant", re);
+        }
+
+        return null;
     }
 }
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
index 318dba3..70500aa 100644
--- a/telephony/java/android/telephony/ims/RcsParticipant.java
+++ b/telephony/java/android/telephony/ims/RcsParticipant.java
@@ -23,6 +23,16 @@
  * @hide - TODO(sahinc) make this public
  */
 public class RcsParticipant implements Parcelable {
+    /**
+     * Returns the row id of this participant.
+     *
+     * TODO(sahinc) implement
+     * @hide
+     */
+    public int getId() {
+        return 12345;
+    }
+
     public static final Creator<RcsParticipant> CREATOR = new Creator<RcsParticipant>() {
         @Override
         public RcsParticipant createFromParcel(Parcel in) {
diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
index 2969ffd..c0a0d94 100644
--- a/telephony/java/android/telephony/ims/RcsThread.java
+++ b/telephony/java/android/telephony/ims/RcsThread.java
@@ -16,7 +16,9 @@
 
 package android.telephony.ims;
 
+import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 /**
  * RcsThread represents a single RCS conversation thread. It holds messages that were sent and
@@ -24,5 +26,50 @@
  * @hide - TODO(sahinc) make this public
  */
 public abstract class RcsThread implements Parcelable {
+    // Since this is an abstract class that gets parcelled, the sub-classes need to write these
+    // magic values into the parcel so that we know which type to unparcel into.
+    protected static final int RCS_1_TO_1_TYPE = 998;
+    protected static final int RCS_GROUP_TYPE = 999;
 
+    protected int mThreadId;
+
+    protected RcsThread(int threadId) {
+        mThreadId = threadId;
+    }
+
+    protected RcsThread(Parcel in) {
+        mThreadId = in.readInt();
+    }
+
+    public static final Creator<RcsThread> CREATOR = new Creator<RcsThread>() {
+        @Override
+        public RcsThread createFromParcel(Parcel in) {
+            int type = in.readInt();
+
+            switch (type) {
+                case RCS_1_TO_1_TYPE:
+                    return new Rcs1To1Thread(in);
+                case RCS_GROUP_TYPE:
+                    return new RcsGroupThread(in);
+                default:
+                    Log.e(RcsMessageStore.TAG, "Cannot unparcel RcsThread, wrong type: " + type);
+            }
+            return null;
+        }
+
+        @Override
+        public RcsThread[] newArray(int size) {
+            return new RcsThread[0];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mThreadId);
+    }
 }
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
new file mode 100644
index 0000000..7bcebfa
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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.telephony.ims;
+
+parcelable RcsThreadQueryContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java
new file mode 100644
index 0000000..931e93d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A continuation token to provide for {@link RcsMessageStore#getRcsThreads}. Use this token to
+ * break large queries into manageable chunks
+ * @hide - TODO make this public
+ */
+public class RcsThreadQueryContinuationToken implements Parcelable {
+    protected RcsThreadQueryContinuationToken(Parcel in) {
+    }
+
+    public static final Creator<RcsThreadQueryContinuationToken> CREATOR =
+            new Creator<RcsThreadQueryContinuationToken>() {
+                @Override
+                public RcsThreadQueryContinuationToken createFromParcel(Parcel in) {
+                    return new RcsThreadQueryContinuationToken(in);
+                }
+
+                @Override
+                public RcsThreadQueryContinuationToken[] newArray(int size) {
+                    return new RcsThreadQueryContinuationToken[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
new file mode 100644
index 0000000..feb2d4d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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.telephony.ims;
+
+parcelable RcsThreadQueryParameters;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
new file mode 100644
index 0000000..f2c4ab1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.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.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} in
+ * order to select a subset of {@link RcsThread}s present in the message store.
+ * @hide TODO - make the Builder and builder() public. The rest should stay internal only.
+ */
+public class RcsThreadQueryParameters implements Parcelable {
+    private final boolean mIsGroup;
+    private final Set<RcsParticipant> mRcsParticipants;
+    private final int mLimit;
+    private final boolean mIsAscending;
+
+    RcsThreadQueryParameters(boolean isGroup, Set<RcsParticipant> participants, int limit,
+            boolean isAscending) {
+        mIsGroup = isGroup;
+        mRcsParticipants = participants;
+        mLimit = limit;
+        mIsAscending = isAscending;
+    }
+
+    /**
+     * Returns a new builder to build a query with.
+     * TODO - make public
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * the list of participants.
+     * @hide
+     */
+    public Set<RcsParticipant> getRcsParticipants() {
+        return mRcsParticipants;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * whether group threads should be queried
+     * @hide
+     */
+    public boolean isGroupThread() {
+        return mIsGroup;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+     * the number of tuples the result query should be limited to.
+     */
+    public int getLimit() {
+        return mLimit;
+    }
+
+    /**
+     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to
+     * determine the sort order.
+     */
+    public boolean isAscending() {
+        return mIsAscending;
+    }
+
+    /**
+     * A helper class to build the {@link RcsThreadQueryParameters}.
+     */
+    public static class Builder {
+        private boolean mIsGroupThread;
+        private Set<RcsParticipant> mParticipants;
+        private int mLimit = 100;
+        private boolean mIsAscending;
+
+        /**
+         * Package private constructor for {@link RcsThreadQueryParameters.Builder}. To obtain this,
+         * {@link RcsThreadQueryParameters#builder()} needs to be called.
+         */
+        Builder() {
+            mParticipants = new HashSet<>();
+        }
+
+        /**
+         * Limits the query to only return group threads.
+         * @param isGroupThread Whether to limit the query result to group threads.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder isGroupThread(boolean isGroupThread) {
+            mIsGroupThread = isGroupThread;
+            return this;
+        }
+
+        /**
+         * Limits the query to only return threads that contain the given participant.
+         * @param participant The participant that must be included in all of the returned threads.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder withParticipant(RcsParticipant participant) {
+            mParticipants.add(participant);
+            return this;
+        }
+
+        /**
+         * Limits the query to only return threads that contain the given list of participants.
+         * @param participants An iterable list of participants that must be included in all of the
+         *                     returned threads.
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder withParticipants(Iterable<RcsParticipant> participants) {
+            for (RcsParticipant participant : participants) {
+                mParticipants.add(participant);
+            }
+            return this;
+        }
+
+        /**
+         * Desired number of threads to be returned from the query. Passing in 0 will return all
+         * existing threads at once. The limit defaults to 100.
+         * @param limit The number to limit the query result to.
+         * @return The same instance of the builder to chain parameters.
+         * @throws InvalidParameterException If the given limit is negative.
+         */
+        @CheckResult
+        public Builder limitResultsTo(int limit) throws InvalidParameterException {
+            if (limit < 0) {
+                throw new InvalidParameterException("The query limit must be non-negative");
+            }
+
+            mLimit = limit;
+            return this;
+        }
+
+        /**
+         * Sorts the results returned from the query via thread IDs.
+         *
+         * TODO - add sorting support for other fields
+         *
+         * @param isAscending whether to sort in ascending order or not
+         * @return The same instance of the builder to chain parameters.
+         */
+        @CheckResult
+        public Builder sort(boolean isAscending) {
+            mIsAscending = isAscending;
+            return this;
+        }
+
+        /**
+         * Builds the {@link RcsThreadQueryParameters} to use in
+         * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)}
+         *
+         * @return An instance of {@link RcsThreadQueryParameters} to use with the thread query.
+         */
+        public RcsThreadQueryParameters build() {
+            return new RcsThreadQueryParameters(
+                    mIsGroupThread, mParticipants, mLimit, mIsAscending);
+        }
+    }
+
+    /**
+     * Parcelable boilerplate below.
+     */
+    protected RcsThreadQueryParameters(Parcel in) {
+        mIsGroup = in.readBoolean();
+
+        ArrayList<RcsParticipant> participantArrayList = new ArrayList<>();
+        in.readTypedList(participantArrayList, RcsParticipant.CREATOR);
+        mRcsParticipants = new HashSet<>(participantArrayList);
+
+        mLimit = in.readInt();
+        mIsAscending = in.readBoolean();
+    }
+
+    public static final Creator<RcsThreadQueryParameters> CREATOR =
+            new Creator<RcsThreadQueryParameters>() {
+                @Override
+                public RcsThreadQueryParameters createFromParcel(Parcel in) {
+                    return new RcsThreadQueryParameters(in);
+                }
+
+                @Override
+                public RcsThreadQueryParameters[] newArray(int size) {
+                    return new RcsThreadQueryParameters[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mIsGroup);
+        dest.writeTypedList(new ArrayList<>(mRcsParticipants));
+        dest.writeInt(mLimit);
+        dest.writeBoolean(mIsAscending);
+    }
+
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
new file mode 100644
index 0000000..4b06529
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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.telephony.ims;
+
+parcelable RcsThreadQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
new file mode 100644
index 0000000..47715f8
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken,
+ * RcsThreadQueryParameters)}
+ * call. This class allows getting the token for querying the next batch of threads in order to
+ * prevent handling large amounts of data at once.
+ *
+ * @hide
+ */
+public class RcsThreadQueryResult implements Parcelable {
+    private RcsThreadQueryContinuationToken mContinuationToken;
+    private List<RcsThread> mRcsThreads;
+
+    /**
+     * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+     * to create query results
+     *
+     * @hide
+     */
+    public RcsThreadQueryResult(
+            RcsThreadQueryContinuationToken continuationToken, List<RcsThread> rcsThreads) {
+        mContinuationToken = continuationToken;
+        mRcsThreads = rcsThreads;
+    }
+
+    /**
+     * Returns a token to call
+     * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)}
+     * to get the next batch of {@link RcsThread}s.
+     */
+    public RcsThreadQueryContinuationToken nextChunkToken() {
+        return mContinuationToken;
+    }
+
+    /**
+     * Returns all the RcsThreads in the current query result. Call {@link
+     * RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)} to get the next batch of
+     * {@link RcsThread}s.
+     */
+    public List<RcsThread> getThreads() {
+        return mRcsThreads;
+    }
+
+    protected RcsThreadQueryResult(Parcel in) {
+        // TODO - implement
+    }
+
+    public static final Creator<RcsThreadQueryResult> CREATOR =
+            new Creator<RcsThreadQueryResult>() {
+                @Override
+                public RcsThreadQueryResult createFromParcel(Parcel in) {
+                    return new RcsThreadQueryResult(in);
+                }
+
+                @Override
+                public RcsThreadQueryResult[] newArray(int size) {
+                    return new RcsThreadQueryResult[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        // TODO - implement
+    }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
index b2e2fad..9badac5 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcs.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
@@ -16,14 +16,29 @@
 
 package android.telephony.ims.aidl;
 
+import android.telephony.ims.RcsParticipant;
+import android.telephony.ims.Rcs1To1Thread;
+import android.telephony.ims.RcsThreadQueryContinuationToken;
+import android.telephony.ims.RcsThreadQueryParameters;
+import android.telephony.ims.RcsThreadQueryResult;
+
 /**
  * RPC definition between RCS storage APIs and phone process.
  * {@hide}
  */
 interface IRcs {
     // RcsMessageStore APIs
+    RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParameters queryParameters);
+
+    RcsThreadQueryResult getRcsThreadsWithToken(
+        in RcsThreadQueryContinuationToken continuationToken);
+
     void deleteThread(int threadId);
 
+    Rcs1To1Thread createRcs1To1Thread(in RcsParticipant participant);
+
+    RcsParticipant createRcsParticipant(String canonicalAddress);
+
     // RcsThread APIs
     int getMessageCount(int rcsThreadId);
 }
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index d9f5c3f..02a6f31 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -50,13 +50,13 @@
     void notifyDataActivity(int state);
     void notifyDataActivityForSubscriber(in int subId, int state);
     void notifyDataConnection(int state, boolean isDataConnectivityPossible,
-            String reason, String apn, String apnType, in LinkProperties linkProperties,
+            String apn, String apnType, in LinkProperties linkProperties,
             in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
     void notifyDataConnectionForSubscriber(int subId, int state, boolean isDataConnectivityPossible,
-            String reason, String apn, String apnType, in LinkProperties linkProperties,
+            String apn, String apnType, in LinkProperties linkProperties,
             in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
-    void notifyDataConnectionFailed(String reason, String apnType);
-    void notifyDataConnectionFailedForSubscriber(int subId, String reason, String apnType);
+    void notifyDataConnectionFailed(String apnType);
+    void notifyDataConnectionFailedForSubscriber(int subId, String apnType);
     void notifyCellLocation(in Bundle cellLocation);
     void notifyCellLocationForSubscriber(in int subId, in Bundle cellLocation);
     void notifyOtaspChanged(in int otaspMode);
@@ -67,7 +67,7 @@
     void notifyPreciseCallState(int ringingCallState, int foregroundCallState,
             int backgroundCallState);
     void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause);
-    void notifyPreciseDataConnectionFailed(String reason, String apnType, String apn,
+    void notifyPreciseDataConnectionFailed(String apnType, String apn,
             String failCause);
     void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
     void notifySrvccStateChanged(in int subId, in int lteState);
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 21f3b92..e87d28c 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -79,8 +79,6 @@
     public static final int SIM_ACTIVATION_TYPE_DATA = 1;
 
     public static final String PHONE_NAME_KEY = "phoneName";
-    public static final String FAILURE_REASON_KEY = "reason";
-    public static final String STATE_CHANGE_REASON_KEY = "reason";
     public static final String DATA_NETWORK_TYPE_KEY = "networkType";
     public static final String DATA_FAILURE_CAUSE_KEY = "failCause";
     public static final String DATA_APN_TYPE_KEY = "apnType";
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index 0ac35bc..e9a5ff7 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -119,7 +119,7 @@
 
         @Override
         public IBinder asBinder() {
-            throw new UnsupportedOperationException();
+            return MockContentProvider.this.getIContentProviderBinder();
         }
 
         @Override
@@ -279,6 +279,13 @@
     }
 
     /**
+     * @hide
+     */
+    public IBinder getIContentProviderBinder() {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    /**
      * Like {@link #attachInfo(Context, android.content.pm.ProviderInfo)}, but for use
      * when directly instantiating the provider for testing.
      *
diff --git a/tests/RcsTests/Android.mk b/tests/RcsTests/Android.mk
index adc7cab..7b348d7 100644
--- a/tests/RcsTests/Android.mk
+++ b/tests/RcsTests/Android.mk
@@ -11,7 +11,7 @@
 
 LOCAL_CERTIFICATE := platform
 LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit android-support-test mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := junit android-support-test mockito-target-minus-junit4 truth-prebuilt
 
 include $(BUILD_PACKAGE)
 
diff --git a/tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
similarity index 97%
rename from tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
rename to tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
index 290e04c..44277ed 100644
--- a/tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.tests.rcs;
+package com.android.tests.ims;
 
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.ims.RcsMessageStore;
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
new file mode 100644
index 0000000..a890a38
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipant;
+import android.telephony.ims.RcsThreadQueryParameters;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsThreadQueryParametersTest {
+    private RcsThreadQueryParameters mRcsThreadQueryParameters;
+    @Mock RcsParticipant mMockParticipant;
+
+    @Test
+    public void testUnparceling() {
+        String key = "some key";
+        mRcsThreadQueryParameters = RcsThreadQueryParameters.builder()
+                .isGroupThread(true)
+                .withParticipant(mMockParticipant)
+                .limitResultsTo(50)
+                .sort(true)
+                .build();
+
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(key, mRcsThreadQueryParameters);
+        mRcsThreadQueryParameters = bundle.getParcelable(key);
+
+        assertThat(mRcsThreadQueryParameters.isGroupThread()).isTrue();
+        assertThat(mRcsThreadQueryParameters.getRcsParticipants()).contains(mMockParticipant);
+        assertThat(mRcsThreadQueryParameters.getLimit()).isEqualTo(50);
+        assertThat(mRcsThreadQueryParameters.isAscending()).isTrue();
+    }
+}
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
index f2bd407..34aa258 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -14,27 +14,49 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-# RollbackTestAppV1
+# RollbackTestAppAv1.apk
 include $(CLEAR_VARS)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_MANIFEST_FILE := TestApp/AndroidManifestV1.xml
-LOCAL_PACKAGE_NAME := RollbackTestAppV1
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Av1.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppAv1
 include $(BUILD_PACKAGE)
-ROLLBACK_TEST_APP_V1 := $(LOCAL_INSTALLED_MODULE)
+ROLLBACK_TEST_APP_AV1 := $(LOCAL_INSTALLED_MODULE)
 
-# RollbackTestAppV2
+# RollbackTestAppAv2.apk
 include $(CLEAR_VARS)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_MANIFEST_FILE := TestApp/AndroidManifestV2.xml
-LOCAL_PACKAGE_NAME := RollbackTestAppV2
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Av2.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppAv2
 include $(BUILD_PACKAGE)
-ROLLBACK_TEST_APP_V2 := $(LOCAL_INSTALLED_MODULE)
+ROLLBACK_TEST_APP_AV2 := $(LOCAL_INSTALLED_MODULE)
+
+# RollbackTestAppBv1.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Bv1.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppBv1
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_BV1 := $(LOCAL_INSTALLED_MODULE)
+
+# RollbackTestAppBv2.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Bv2.xml
+LOCAL_PACKAGE_NAME := RollbackTestAppBv2
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_BV2 := $(LOCAL_INSTALLED_MODULE)
 
 # RollbackTest
 include $(CLEAR_VARS)
@@ -43,12 +65,17 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_JAVA_RESOURCE_FILES := $(ROLLBACK_TEST_APP_V1) $(ROLLBACK_TEST_APP_V2)
-LOCAL_SDK_VERSION := test_current
+LOCAL_JAVA_RESOURCE_FILES := \
+  $(ROLLBACK_TEST_APP_AV1) \
+  $(ROLLBACK_TEST_APP_AV2) \
+  $(ROLLBACK_TEST_APP_BV1) \
+  $(ROLLBACK_TEST_APP_BV2)
+LOCAL_SDK_VERSION := system_current
 LOCAL_TEST_CONFIG := RollbackTest.xml
 include $(BUILD_PACKAGE)
 
 # Clean up local variables
-ROLLBACK_TEST_APP_V1 :=
-ROLLBACK_TEST_APP_V2 :=
+ROLLBACK_TEST_APP_AV1 :=
+ROLLBACK_TEST_APP_AV2 :=
+ROLLBACK_TEST_APP_BV1 :=
+ROLLBACK_TEST_APP_BV2 :=
diff --git a/tests/RollbackTest/AndroidManifest.xml b/tests/RollbackTest/AndroidManifest.xml
index d1535a3..e57a768 100644
--- a/tests/RollbackTest/AndroidManifest.xml
+++ b/tests/RollbackTest/AndroidManifest.xml
@@ -17,9 +17,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.tests.rollback" >
 
-    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
-    <uses-permission android:name="android.permission.DELETE_PACKAGES" />
-
     <application>
         <receiver android:name="com.android.tests.rollback.LocalIntentSender"
                   android:exported="true" />
diff --git a/tests/RollbackTest/TEST_MAPPING b/tests/RollbackTest/TEST_MAPPING
new file mode 100644
index 0000000..c1d95ac
--- /dev/null
+++ b/tests/RollbackTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "RollbackTest"
+    }
+  ]
+}
diff --git a/tests/RollbackTest/TestApp/AndroidManifestV1.xml b/tests/RollbackTest/TestApp/Av1.xml
similarity index 79%
copy from tests/RollbackTest/TestApp/AndroidManifestV1.xml
copy to tests/RollbackTest/TestApp/Av1.xml
index 45e9584..996d831 100644
--- a/tests/RollbackTest/TestApp/AndroidManifestV1.xml
+++ b/tests/RollbackTest/TestApp/Av1.xml
@@ -15,14 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.tests.rollback.testapp"
+    package="com.android.tests.rollback.testapp.A"
     android:versionCode="1"
     android:versionName="1.0" >
 
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Rollback Test App V1">
+    <application android:label="Rollback Test App A v1">
+        <meta-data android:name="version" android:value="1" />
+        <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+                  android:exported="true" />
         <activity android:name="com.android.tests.rollback.testapp.MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/RollbackTest/TestApp/AndroidManifestV2.xml b/tests/RollbackTest/TestApp/Av2.xml
similarity index 79%
copy from tests/RollbackTest/TestApp/AndroidManifestV2.xml
copy to tests/RollbackTest/TestApp/Av2.xml
index 0104086..21c7260 100644
--- a/tests/RollbackTest/TestApp/AndroidManifestV2.xml
+++ b/tests/RollbackTest/TestApp/Av2.xml
@@ -15,14 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.tests.rollback.testapp"
+    package="com.android.tests.rollback.testapp.A"
     android:versionCode="2"
     android:versionName="2.0" >
 
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Rollback Test App V2">
+    <application android:label="Rollback Test App A v2">
+        <meta-data android:name="version" android:value="2" />
+        <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+                  android:exported="true" />
         <activity android:name="com.android.tests.rollback.testapp.MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/RollbackTest/TestApp/AndroidManifestV1.xml b/tests/RollbackTest/TestApp/Bv1.xml
similarity index 79%
rename from tests/RollbackTest/TestApp/AndroidManifestV1.xml
rename to tests/RollbackTest/TestApp/Bv1.xml
index 45e9584..de0fd0d 100644
--- a/tests/RollbackTest/TestApp/AndroidManifestV1.xml
+++ b/tests/RollbackTest/TestApp/Bv1.xml
@@ -15,14 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.tests.rollback.testapp"
+    package="com.android.tests.rollback.testapp.B"
     android:versionCode="1"
     android:versionName="1.0" >
 
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Rollback Test App V1">
+    <application android:label="Rollback Test App B v1">
+        <meta-data android:name="version" android:value="1" />
+        <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+                  android:exported="true" />
         <activity android:name="com.android.tests.rollback.testapp.MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/RollbackTest/TestApp/AndroidManifestV2.xml b/tests/RollbackTest/TestApp/Bv2.xml
similarity index 79%
rename from tests/RollbackTest/TestApp/AndroidManifestV2.xml
rename to tests/RollbackTest/TestApp/Bv2.xml
index 0104086..6c2e66a 100644
--- a/tests/RollbackTest/TestApp/AndroidManifestV2.xml
+++ b/tests/RollbackTest/TestApp/Bv2.xml
@@ -15,14 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.tests.rollback.testapp"
+    package="com.android.tests.rollback.testapp.B"
     android:versionCode="2"
     android:versionName="2.0" >
 
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Rollback Test App V2">
+    <application android:label="Rollback Test App B v2">
+        <meta-data android:name="version" android:value="2" />
+        <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
+                  android:exported="true" />
         <activity android:name="com.android.tests.rollback.testapp.MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/MainActivity.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/MainActivity.java
index 1856bac..9f1a060 100644
--- a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/MainActivity.java
+++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/MainActivity.java
@@ -27,5 +27,11 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        try {
+            new ProcessUserData().processUserData(this);
+        } catch (ProcessUserData.UserDataException e) {
+            throw new AssertionError("Failed to process app user data", e);
+        }
     }
 }
diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
new file mode 100644
index 0000000..fde6a83
--- /dev/null
+++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
@@ -0,0 +1,106 @@
+/*
+ * 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.tests.rollback.testapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Scanner;
+
+/**
+ * A broadcast reciever to check for and update user app data version
+ * compatibility.
+ */
+public class ProcessUserData extends BroadcastReceiver {
+
+    /**
+     * Exception thrown in case of issue with user data.
+     */
+    public static class UserDataException extends Exception {
+        public UserDataException(String message) {
+            super(message);
+        }
+
+        public UserDataException(String message, Throwable cause) {
+           super(message, cause);
+        }
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        try {
+            processUserData(context);
+            setResultCode(1);
+        } catch (UserDataException e) {
+            setResultCode(0);
+            setResultData(e.getMessage());
+        }
+    }
+
+    /**
+     * Update the app's user data version to match the app version.
+     *
+     * @param context The application context.
+     * @throws UserDataException in case of problems with app user data.
+     */
+    public void processUserData(Context context) throws UserDataException {
+        int appVersion = 0;
+        try {
+            ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
+                    context.getPackageName(), PackageManager.GET_META_DATA);
+            Bundle bundle = appInfo.metaData;
+            appVersion = bundle.getInt("version");
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new UserDataException("Unable to get app version info", e);
+        }
+
+        // Read the version of the app's user data and ensure it is compatible
+        // with our version of the application.
+        File versionFile = new File(context.getFilesDir(), "version.txt");
+        try {
+            Scanner s = new Scanner(versionFile);
+            int userDataVersion = s.nextInt();
+            s.close();
+
+            if (userDataVersion > appVersion) {
+                throw new UserDataException("User data is from version " + userDataVersion
+                        + ", which is not compatible with this version " + appVersion
+                        + " of the RollbackTestApp");
+            }
+        } catch (FileNotFoundException e) {
+            // No problem. This is a fresh install of the app or the user data
+            // has been wiped.
+        }
+
+        // Record the current version of the app in the user data.
+        try {
+            PrintWriter pw = new PrintWriter(versionFile);
+            pw.println(appVersion);
+            pw.close();
+        } catch (IOException e) {
+            throw new UserDataException("Unable to write user data.", e);
+        }
+    }
+}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 02c1ce2..0ccfb19 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -16,13 +16,17 @@
 
 package com.android.tests.rollback;
 
+import android.Manifest;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
 import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
@@ -34,10 +38,13 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -49,7 +56,8 @@
 
     private static final String TAG = "RollbackTest";
 
-    private static final String TEST_APP_PACKAGE_NAME = "com.android.tests.rollback.testapp";
+    private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A";
+    private static final String TEST_APP_B = "com.android.tests.rollback.testapp.B";
 
     /**
      * Test basic rollbacks.
@@ -71,85 +79,94 @@
         };
         context.registerReceiver(enableRollbackReceiver, enableRollbackFilter);
 
-        // Register a broadcast receiver for notification when the rollback is
-        // done executing.
-        RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
-        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
 
-        // Uninstall com.android.tests.rollback.testapp
-        RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
-        assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+            // Register a broadcast receiver for notification when the rollback is
+            // done executing.
+            RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
 
-        // TODO: There is currently a race condition between when the app is
-        // uninstalled and when rollback manager deletes the rollback. Fix it
-        // so that's not the case!
-        for (int i = 0; i < 5; ++i) {
-            for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
-                if (TEST_APP_PACKAGE_NAME.equals(info.targetPackage.packageName)) {
-                    Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
-                    Thread.sleep(1000);
-                    break;
+            // Uninstall TEST_APP_A
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // TODO: There is currently a race condition between when the app is
+            // uninstalled and when rollback manager deletes the rollback. Fix it
+            // so that's not the case!
+            for (int i = 0; i < 5; ++i) {
+                for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
+                    if (TEST_APP_A.equals(info.targetPackage.packageName)) {
+                        Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
+                        Thread.sleep(1000);
+                        break;
+                    }
                 }
             }
-        }
 
-        // The app should not be available for rollback.
-        assertNull(rm.getAvailableRollback(TEST_APP_PACKAGE_NAME));
-        assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
+            // The app should not be available for rollback.
+            assertNull(rm.getAvailableRollback(TEST_APP_A));
+            assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
 
-        // There should be no recently executed rollbacks for this package.
-        for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
-            assertNotEquals(TEST_APP_PACKAGE_NAME, info.targetPackage.packageName);
-        }
-
-        // Install v1 of the app (without rollbacks enabled).
-        RollbackTestUtils.install("RollbackTestAppV1.apk", false);
-        assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
-
-        // Upgrade from v1 to v2, with rollbacks enabled.
-        RollbackTestUtils.install("RollbackTestAppV2.apk", true);
-        assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
-
-        // The app should now be available for rollback.
-        assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
-        RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
-        assertNotNull(rollback);
-        assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
-        assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
-        assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
-
-        // We should not have received any rollback requests yet.
-        // TODO: Possibly flaky if, by chance, some other app on device
-        // happens to be rolled back at the same time?
-        assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
-
-        // Roll back the app.
-        RollbackTestUtils.rollback(rollback);
-        assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
-
-        // Verify we received a broadcast for the rollback.
-        // TODO: Race condition between the timeout and when the broadcast is
-        // received could lead to test flakiness.
-        Intent broadcast = broadcastReceiver.poll(5, TimeUnit.SECONDS);
-        assertNotNull(broadcast);
-        assertEquals(TEST_APP_PACKAGE_NAME, broadcast.getData().getSchemeSpecificPart());
-        assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
-
-        // Verify the recent rollback has been recorded.
-        rollback = null;
-        for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
-            if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
-                assertNull(rollback);
-                rollback = r;
+            // There should be no recently executed rollbacks for this package.
+            for (RollbackInfo info : rm.getRecentlyExecutedRollbacks()) {
+                assertNotEquals(TEST_APP_A, info.targetPackage.packageName);
             }
-        }
-        assertNotNull(rollback);
-        assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
-        assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
-        assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
 
-        broadcastReceiver.unregister();
-        context.unregisterReceiver(enableRollbackReceiver);
+            // Install v1 of the app (without rollbacks enabled).
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // Upgrade from v1 to v2, with rollbacks enabled.
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // The app should now be available for rollback.
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+            // We should not have received any rollback requests yet.
+            // TODO: Possibly flaky if, by chance, some other app on device
+            // happens to be rolled back at the same time?
+            assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
+
+            // Roll back the app.
+            RollbackTestUtils.rollback(rollback);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // Verify we received a broadcast for the rollback.
+            // TODO: Race condition between the timeout and when the broadcast is
+            // received could lead to test flakiness.
+            Intent broadcast = broadcastReceiver.poll(5, TimeUnit.SECONDS);
+            assertNotNull(broadcast);
+            assertEquals(TEST_APP_A, broadcast.getData().getSchemeSpecificPart());
+            assertNull(broadcastReceiver.poll(0, TimeUnit.SECONDS));
+
+            // Verify the recent rollback has been recorded.
+            rollback = null;
+            for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+                if (TEST_APP_A.equals(r.targetPackage.packageName)) {
+                    assertNull(rollback);
+                    rollback = r;
+                }
+            }
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+
+            broadcastReceiver.unregister();
+            context.unregisterReceiver(enableRollbackReceiver);
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
     }
 
     /**
@@ -157,65 +174,74 @@
      */
     @Test
     public void testRollbackDataPersistence() throws Exception {
-        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
 
-        // Prep installation of com.android.tests.rollback.testapp
-        RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
-        RollbackTestUtils.install("RollbackTestAppV1.apk", false);
-        RollbackTestUtils.install("RollbackTestAppV2.apk", true);
-        assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
 
-        // The app should now be available for rollback.
-        assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
-        RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
-        assertNotNull(rollback);
-        assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
-        assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
-        assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+            // Prep installation of TEST_APP_A
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
 
-        // Reload the persisted data.
-        rm.reloadPersistedData();
+            // The app should now be available for rollback.
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
 
-        // The app should still be available for rollback.
-        assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
-        rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
-        assertNotNull(rollback);
-        assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
-        assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
-        assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+            // Reload the persisted data.
+            rm.reloadPersistedData();
 
-        // Roll back the app.
-        RollbackTestUtils.rollback(rollback);
-        assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+            // The app should still be available for rollback.
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            rollback = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
 
-        // Verify the recent rollback has been recorded.
-        rollback = null;
-        for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
-            if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
-                assertNull(rollback);
-                rollback = r;
+            // Roll back the app.
+            RollbackTestUtils.rollback(rollback);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // Verify the recent rollback has been recorded.
+            rollback = null;
+            for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+                if (TEST_APP_A.equals(r.targetPackage.packageName)) {
+                    assertNull(rollback);
+                    rollback = r;
+                }
             }
-        }
-        assertNotNull(rollback);
-        assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
-        assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
-        assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
 
-        // Reload the persisted data.
-        rm.reloadPersistedData();
+            // Reload the persisted data.
+            rm.reloadPersistedData();
 
-        // Verify the recent rollback is still recorded.
-        rollback = null;
-        for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
-            if (TEST_APP_PACKAGE_NAME.equals(r.targetPackage.packageName)) {
-                assertNull(rollback);
-                rollback = r;
+            // Verify the recent rollback is still recorded.
+            rollback = null;
+            for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+                if (TEST_APP_A.equals(r.targetPackage.packageName)) {
+                    assertNull(rollback);
+                    rollback = r;
+                }
             }
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
         }
-        assertNotNull(rollback);
-        assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
-        assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
-        assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
     }
 
     /**
@@ -224,26 +250,108 @@
      */
     @Test
     public void testRollbackExpiration() throws Exception {
-        RollbackManager rm = RollbackTestUtils.getRollbackManager();
-        RollbackTestUtils.uninstall("com.android.tests.rollback.testapp");
-        RollbackTestUtils.install("RollbackTestAppV1.apk", false);
-        RollbackTestUtils.install("RollbackTestAppV2.apk", true);
-        assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_PACKAGE_NAME));
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
 
-        // The app should now be available for rollback.
-        assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
-        RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_PACKAGE_NAME);
-        assertNotNull(rollback);
-        assertEquals(TEST_APP_PACKAGE_NAME, rollback.targetPackage.packageName);
-        assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
-        assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
 
-        // Expire the rollback.
-        rm.expireRollbackForPackage(TEST_APP_PACKAGE_NAME);
+            // The app should now be available for rollback.
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollback);
+            assertEquals(TEST_APP_A, rollback.targetPackage.packageName);
+            assertEquals(2, rollback.targetPackage.higherVersion.versionCode);
+            assertEquals(1, rollback.targetPackage.lowerVersion.versionCode);
 
-        // The rollback should no longer be available.
-        assertNull(rm.getAvailableRollback(TEST_APP_PACKAGE_NAME));
-        assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_PACKAGE_NAME));
+            // Expire the rollback.
+            rm.expireRollbackForPackage(TEST_APP_A);
+
+            // The rollback should no longer be available.
+            assertNull(rm.getAvailableRollback(TEST_APP_A));
+            assertFalse(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
+    private static final String NO_RESPONSE = "NO RESPONSE";
+
+    // Calls into the test app to process user data.
+    // Asserts if the user data could not be processed or was version
+    // incompatible with the previously processed user data.
+    private void processUserData(String packageName) throws Exception {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(packageName,
+                    "com.android.tests.rollback.testapp.ProcessUserData"));
+        Context context = InstrumentationRegistry.getContext();
+
+        HandlerThread handlerThread = new HandlerThread("RollbackTestHandlerThread");
+        handlerThread.start();
+
+        // It can sometimes take a while after rollback before the app will
+        // receive this broadcast, so try a few times in a loop.
+        String result = NO_RESPONSE;
+        for (int i = 0; result.equals(NO_RESPONSE) && i < 5; ++i) {
+            BlockingQueue<String> resultQueue = new LinkedBlockingQueue<>();
+            context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (getResultCode() == 1) {
+                        resultQueue.add("OK");
+                    } else {
+                        // If the test app doesn't receive the broadcast or
+                        // fails to set the result data, then getResultData
+                        // here returns the initial NO_RESPONSE data passed to
+                        // the sendOrderedBroadcast call.
+                        resultQueue.add(getResultData());
+                    }
+                }
+            }, new Handler(handlerThread.getLooper()), 0, NO_RESPONSE, null);
+
+            result = resultQueue.poll(10, TimeUnit.SECONDS);
+            if (result == null) {
+                result = "ProcessUserData broadcast timed out";
+            }
+        }
+
+        handlerThread.quit();
+        if (!"OK".equals(result)) {
+            fail(result);
+        }
+    }
+
+    /**
+     * Test that app user data is rolled back.
+     * TODO: Stop ignoring this test once user data rollback is supported.
+     */
+    @Ignore @Test
+    public void testUserDataRollback() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppV1.apk", false);
+            processUserData(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppV2.apk", true);
+            processUserData(TEST_APP_A);
+
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+            RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+            RollbackTestUtils.rollback(rollback);
+            processUserData(TEST_APP_A);
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
     }
 
     /**
@@ -270,4 +378,164 @@
         // called, even when the test fails?
         broadcastReceiver.unregister();
     }
+
+    /**
+     * Regression test for rollback in the case when multiple apps are
+     * available for rollback at the same time.
+     */
+    @Test
+    public void testMultipleRollbackAvailable() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+            // Prep installation of the test apps.
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            RollbackTestUtils.uninstall(TEST_APP_B);
+            RollbackTestUtils.install("RollbackTestAppBv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppBv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+            // Both test apps should now be available for rollback, and the
+            // targetPackage returned for rollback should be correct.
+            RollbackInfo rollbackA = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollbackA);
+            assertEquals(TEST_APP_A, rollbackA.targetPackage.packageName);
+
+            RollbackInfo rollbackB = rm.getAvailableRollback(TEST_APP_B);
+            assertNotNull(rollbackB);
+            assertEquals(TEST_APP_B, rollbackB.targetPackage.packageName);
+
+            // Executing rollback should roll back the correct package.
+            RollbackTestUtils.rollback(rollbackA);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            RollbackTestUtils.rollback(rollbackB);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Test that the MANAGE_ROLLBACKS permission is required to call
+     * RollbackManager APIs.
+     */
+    @Test
+    public void testManageRollbacksPermission() throws Exception {
+        // We shouldn't be allowed to call any of the RollbackManager APIs
+        // without the MANAGE_ROLLBACKS permission.
+        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+        try {
+            rm.getAvailableRollback(TEST_APP_A);
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+
+        try {
+            rm.getPackagesWithAvailableRollbacks();
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+
+        try {
+            rm.getRecentlyExecutedRollbacks();
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+
+        try {
+            // TODO: What if the implementation checks arguments for non-null
+            // first? Then this test isn't valid.
+            rm.executeRollback(null, null);
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+
+        try {
+            rm.reloadPersistedData();
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+
+        try {
+            rm.expireRollbackForPackage(TEST_APP_A);
+            fail("expected SecurityException");
+        } catch (SecurityException e) {
+            // Expected.
+        }
+    }
+
+    /**
+     * Test rollback of multi-package installs.
+     * TODO: Stop ignoring this test once support for multi-package rollback
+     * is implemented.
+     */
+    @Ignore @Test
+    public void testMultiPackage() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+            // Prep installation of the test apps.
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.uninstall(TEST_APP_B);
+            RollbackTestUtils.installMultiPackage(false,
+                    "RollbackTestAppAv1.apk",
+                    "RollbackTestAppBv1.apk");
+            RollbackTestUtils.installMultiPackage(true,
+                    "RollbackTestAppAv2.apk",
+                    "RollbackTestAppBv2.apk");
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+            // TEST_APP_A should now be available for rollback.
+            assertTrue(rm.getPackagesWithAvailableRollbacks().contains(TEST_APP_A));
+            RollbackInfo rollback = rm.getAvailableRollback(TEST_APP_A);
+            assertNotNull(rollback);
+
+            // TODO: Test the dependent apps for rollback are correct once we
+            // support that in the RollbackInfo API.
+
+            // Rollback the app. It should cause both test apps to be rolled
+            // back.
+            RollbackTestUtils.rollback(rollback);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_B));
+
+            // We should not see a recent rollback listed for TEST_APP_B
+            for (RollbackInfo r : rm.getRecentlyExecutedRollbacks()) {
+                assertNotEquals(TEST_APP_B, r.targetPackage.packageName);
+            }
+
+            // TODO: Test the listed dependent apps for the recently executed
+            // rollback are correct once we support that in the RollbackInfo
+            // API.
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
 }
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index c5ce3fa..fbc3d8f 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -135,15 +135,64 @@
         assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
     }
 
-    // TODO: Unused
-    static void adoptShellPermissionIdentity() {
+    /**
+     * Installs the apks with the given resource names as an atomic set.
+     *
+     * @param enableRollback if rollback should be enabled.
+     * @param resourceNames names of the class loader resource for the apks to
+     *        install.
+     * @throws AssertionError if the installation fails.
+     */
+    static void installMultiPackage(boolean enableRollback, String... resourceNames)
+            throws InterruptedException, IOException {
+        Context context = InstrumentationRegistry.getContext();
+        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
+
+        PackageInstaller.SessionParams multiPackageParams = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        multiPackageParams.setMultiPackage();
+        if (enableRollback) {
+            // TODO: Do we set this on the parent params, the child params, or
+            // both?
+            multiPackageParams.setEnableRollback();
+        }
+        int multiPackageId = packageInstaller.createSession(multiPackageParams);
+        PackageInstaller.Session multiPackage = packageInstaller.openSession(multiPackageId);
+
+        for (String resourceName : resourceNames) {
+            PackageInstaller.Session session = null;
+            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+            if (enableRollback) {
+                params.setEnableRollback();
+            }
+            int sessionId = packageInstaller.createSession(params);
+            session = packageInstaller.openSession(sessionId);
+
+            ClassLoader loader = RollbackTest.class.getClassLoader();
+            try (OutputStream packageInSession = session.openWrite("package", 0, -1);
+                 InputStream is = loader.getResourceAsStream(resourceName);) {
+                byte[] buffer = new byte[4096];
+                int n;
+                while ((n = is.read(buffer)) >= 0) {
+                    packageInSession.write(buffer, 0, n);
+                }
+            }
+            multiPackage.addChildSessionId(sessionId);
+        }
+
+        // Commit the session (this will start the installation workflow).
+        multiPackage.commit(LocalIntentSender.getIntentSender());
+        assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+    }
+
+    static void adoptShellPermissionIdentity(String... permissions) {
         InstrumentationRegistry
             .getInstrumentation()
             .getUiAutomation()
-            .adoptShellPermissionIdentity();
+            .adoptShellPermissionIdentity(permissions);
     }
 
-    // TODO: Unused
     static void dropShellPermissionIdentity() {
         InstrumentationRegistry
             .getInstrumentation()
diff --git a/tests/UsageReportingTest/Android.mk b/tests/UsageReportingTest/Android.mk
new file mode 100644
index 0000000..afb6e16b1
--- /dev/null
+++ b/tests/UsageReportingTest/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_USE_AAPT2 := true
+LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PACKAGE_NAME := UsageReportingTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+include $(BUILD_PACKAGE)
diff --git a/tests/UsageReportingTest/AndroidManifest.xml b/tests/UsageReportingTest/AndroidManifest.xml
new file mode 100644
index 0000000..be0b09e
--- /dev/null
+++ b/tests/UsageReportingTest/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Note: Add android:sharedUserId="android.uid.system" to the root element to simulate the system UID
+  caller case.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.usagereporter"
+    >
+
+    <application android:label="@string/reporter_app">
+        <activity android:name="UsageReporterActivity"
+                  android:label="UsageReporter">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
diff --git a/tests/UsageReportingTest/res/layout/row_item.xml b/tests/UsageReportingTest/res/layout/row_item.xml
new file mode 100644
index 0000000..1eb2dab
--- /dev/null
+++ b/tests/UsageReportingTest/res/layout/row_item.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="16dp"
+    android:paddingRight="16dp"
+    android:background="@color/inactive_color">
+
+    <TextView android:id="@+id/token"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:textStyle="bold"/>
+
+    <Button android:id="@+id/start" style="@style/ActionButton"
+            android:text="@string/start" />
+
+    <Button android:id="@+id/stop" style="@style/ActionButton"
+            android:text="@string/stop" />
+</LinearLayout>
diff --git a/tests/UsageReportingTest/res/menu/main.xml b/tests/UsageReportingTest/res/menu/main.xml
new file mode 100644
index 0000000..9847c2d
--- /dev/null
+++ b/tests/UsageReportingTest/res/menu/main.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/add_token"
+          android:title="@string/add_token"/>
+    <item android:id="@+id/add_many_tokens"
+          android:title="@string/add_many_tokens"/>
+    <item android:id="@+id/stop_all"
+          android:title="@string/stop_all_tokens"/>
+    <group android:checkableBehavior="all">
+        <item android:id="@+id/restore_on_start"
+              android:title="@string/restore_tokens_on_start"/>
+    </group>
+</menu>
\ No newline at end of file
diff --git a/tests/UsageReportingTest/res/values/colors.xml b/tests/UsageReportingTest/res/values/colors.xml
new file mode 100644
index 0000000..03bcf8a
--- /dev/null
+++ b/tests/UsageReportingTest/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?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>
+    <color name="active_color">#FFF</color>
+    <color name="inactive_color">#AAA</color>
+</resources>
\ No newline at end of file
diff --git a/tests/UsageReportingTest/res/values/strings.xml b/tests/UsageReportingTest/res/values/strings.xml
new file mode 100644
index 0000000..015290e
--- /dev/null
+++ b/tests/UsageReportingTest/res/values/strings.xml
@@ -0,0 +1,47 @@
+<?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>
+    <!-- Do not translate -->
+    <string name="reporter_app">Usage Reporter App</string>
+    <!-- Do not translate -->
+    <string name="start">Start</string>
+    <!-- Do not translate -->
+    <string name="stop">Stop</string>
+    <!-- Do not translate -->
+    <string name="default_token">SuperSecretToken</string>
+
+    <!-- Do not translate -->
+    <string name="add_token">Add Token</string>
+    <!-- Do not translate -->
+    <string name="add_many_tokens">Add Many Tokens</string>
+    <!-- Do not translate -->
+    <string name="stop_all_tokens">Stop All</string>
+    <!-- Do not translate -->
+    <string name="restore_tokens_on_start">Readd Tokens on Start</string>
+
+
+    <!-- Do not translate -->
+    <string name="token_query">Enter token(s) (delimit tokens with commas)</string>
+    <!-- Do not translate -->
+    <string name="many_tokens_query">Generate how many tokens?</string>
+    <!-- Do not translate -->
+    <string name="stop_all_tokens_query">Stop all tokens?</string>
+    <!-- Do not translate -->
+    <string name="ok">OK</string>
+    <!-- Do not translate -->
+    <string name="cancel">Cancel</string>
+</resources>
\ No newline at end of file
diff --git a/tests/UsageReportingTest/res/values/styles.xml b/tests/UsageReportingTest/res/values/styles.xml
new file mode 100644
index 0000000..e5b86c5
--- /dev/null
+++ b/tests/UsageReportingTest/res/values/styles.xml
@@ -0,0 +1,31 @@
+<?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="ActionButton">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textAppearance">@style/TextAppearance.ActionButton</item>
+    </style>
+
+    <style name="TextAppearance" parent="android:TextAppearance">
+    </style>
+
+    <style name="TextAppearance.ActionButton">
+        <item name="android:textStyle">italic</item>
+    </style>
+
+</resources>
diff --git a/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java b/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java
new file mode 100644
index 0000000..946be8f
--- /dev/null
+++ b/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java
@@ -0,0 +1,320 @@
+/*
+ * 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.tests.usagereporter;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+
+public class UsageReporterActivity extends ListActivity {
+
+    private Activity mActivity;
+    private final ArrayList<String> mTokens = new ArrayList();
+    private final ArraySet<String> mActives = new ArraySet();
+    private UsageStatsManager mUsageStatsManager;
+    private Adapter mAdapter;
+    private boolean mRestoreOnStart = false;
+    private static Context sContext;
+
+    /** Called with the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        sContext = getApplicationContext();
+
+        mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+
+        mAdapter = new Adapter();
+        setListAdapter(mAdapter);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mActivity = this;
+
+
+        if (mRestoreOnStart) {
+            ArrayList<String> removed = null;
+            for (String token : mActives) {
+                try {
+                    mUsageStatsManager.reportUsageStart(mActivity, token);
+                } catch (Exception e) {
+                    // Somthing went wrong, recover and move on
+                    if (removed == null) {
+                        removed = new ArrayList();
+                    }
+                    removed.add(token);
+                }
+            }
+            if (removed != null) {
+                for (String token : removed) {
+                    mActives.remove(token);
+                }
+            }
+        } else {
+            mActives.clear();
+        }
+    }
+
+    /**
+     * Called when the activity is about to start interacting with the user.
+     */
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mAdapter.notifyDataSetChanged();
+    }
+
+
+    /**
+     * Called when your activity's options menu needs to be created.
+     */
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.main, menu);
+        return super.onCreateOptionsMenu(menu);
+    }
+
+
+    /**
+     * Called when a menu item is selected.
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.add_token:
+                callAddToken();
+                return true;
+            case R.id.add_many_tokens:
+                callAddManyTokens();
+                return true;
+            case R.id.stop_all:
+                callStopAll();
+                return true;
+            case R.id.restore_on_start:
+                mRestoreOnStart = !mRestoreOnStart;
+                item.setChecked(mRestoreOnStart);
+                return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void callAddToken() {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(getString(R.string.token_query));
+        final EditText input = new EditText(this);
+        input.setInputType(InputType.TYPE_CLASS_TEXT);
+        input.setHint(getString(R.string.default_token));
+        builder.setView(input);
+
+        builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                String tokenNames = input.getText().toString().trim();
+                if (TextUtils.isEmpty(tokenNames)) {
+                    tokenNames = getString(R.string.default_token);
+                }
+                String[] tokens = tokenNames.split(",");
+                for (String token : tokens) {
+                    if (mTokens.contains(token)) continue;
+                    mTokens.add(token);
+                }
+                mAdapter.notifyDataSetChanged();
+
+            }
+        });
+        builder.setNegativeButton(getString(R.string.cancel),
+                                  new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.cancel();
+            }
+        });
+
+        builder.show();
+    }
+
+    private void callAddManyTokens() {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(getString(R.string.many_tokens_query));
+        final EditText input = new EditText(this);
+        input.setInputType(InputType.TYPE_CLASS_NUMBER);
+        builder.setView(input);
+
+        builder.setPositiveButton(getString(R.string.ok),
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                String val = input.getText().toString().trim();
+                if (TextUtils.isEmpty(val)) return;
+                int n = Integer.parseInt(val);
+                for (int i = 0; i < n; i++) {
+                    final String token = getString(R.string.default_token) + i;
+                    if (mTokens.contains(token)) continue;
+                    mTokens.add(token);
+                }
+                mAdapter.notifyDataSetChanged();
+
+            }
+        });
+        builder.setNegativeButton(getString(R.string.cancel),
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.cancel();
+            }
+        });
+
+        builder.show();
+    }
+
+    private void callStopAll() {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(getString(R.string.stop_all_tokens_query));
+
+        builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                for (String token : mActives) {
+                    mUsageStatsManager.reportUsageStop(mActivity, token);
+                }
+                mActives.clear();
+                mAdapter.notifyDataSetChanged();
+            }
+        });
+        builder.setNegativeButton(getString(R.string.cancel),
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.cancel();
+            }
+        });
+        builder.show();
+    }
+
+    /**
+     * A call-back for when the user presses the back button.
+     */
+    OnClickListener mStartListener = new OnClickListener() {
+        public void onClick(View v) {
+            final View parent = (View) v.getParent();
+            final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString();
+            try {
+                mUsageStatsManager.reportUsageStart(mActivity, token);
+            } catch (Exception e) {
+                Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show();
+            }
+            parent.setBackgroundColor(getColor(R.color.active_color));
+            mActives.add(token);
+        }
+    };
+
+    /**
+     * A call-back for when the user presses the clear button.
+     */
+    OnClickListener mStopListener = new OnClickListener() {
+        public void onClick(View v) {
+            final View parent = (View) v.getParent();
+
+            final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString();
+            try {
+                mUsageStatsManager.reportUsageStop(mActivity, token);
+            } catch (Exception e) {
+                Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show();
+            }
+            parent.setBackgroundColor(getColor(R.color.inactive_color));
+            mActives.remove(token);
+        }
+    };
+
+
+    private class Adapter extends BaseAdapter {
+        @Override
+        public int getCount() {
+            return mTokens.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mTokens.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final ViewHolder holder;
+            if (convertView == null) {
+                convertView = LayoutInflater.from(UsageReporterActivity.this)
+                        .inflate(R.layout.row_item, parent, false);
+                holder = new ViewHolder();
+                holder.tokenName = (TextView) convertView.findViewById(R.id.token);
+
+                holder.startButton = ((Button) convertView.findViewById(R.id.start));
+                holder.startButton.setOnClickListener(mStartListener);
+                holder.stopButton = ((Button) convertView.findViewById(R.id.stop));
+                holder.stopButton.setOnClickListener(mStopListener);
+                convertView.setTag(holder);
+            } else {
+                holder = (ViewHolder) convertView.getTag();
+            }
+
+            final String token = mTokens.get(position);
+            holder.tokenName.setText(mTokens.get(position));
+            if (mActives.contains(token)) {
+                convertView.setBackgroundColor(getColor(R.color.active_color));
+            } else {
+                convertView.setBackgroundColor(getColor(R.color.inactive_color));
+            }
+            return convertView;
+        }
+    }
+
+    private static class ViewHolder {
+        public TextView tokenName;
+        public Button startButton;
+        public Button stopButton;
+    }
+}
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
index 3d8ce21..3c628f6 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -155,7 +155,7 @@
                     intent.setPackage(getPackageName());
                     intent.putExtra(EXTRA_KEY_TIMEOUT, true);
                     mUsageStatsManager.registerAppUsageObserver(1, packages,
-                            30, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
+                            60, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
                                     1, intent, 0));
                 }
             }
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index d5987a5..8564698 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -28,6 +28,7 @@
 import android.view.IWindowSession;
 import android.view.InsetsState;
 import android.view.Surface;
+import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
@@ -106,7 +107,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 InsetsState());
+                                new SurfaceControl(), new InsetsState());
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 1e3a49b..2c2afd4 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -832,25 +832,23 @@
                 0 /* operations */);
 
         // Traffic measured for the root uid on the base interface if eBPF is in use.
-        // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
-        // overhead (20 bytes per packet), only for TX traffic.
         final NetworkStats.Entry ebpfRootUidEntry = new NetworkStats.Entry(
                 baseIface, rootUid, SET_DEFAULT, TAG_NONE,
                 163577 /* rxBytes */,
                 187 /* rxPackets */,
-                1169942 /* txBytes */,
-                13902 /* txPackets */,
+                17607 /* txBytes */,
+                97 /* txPackets */,
                 0 /* operations */);
 
         // Traffic measured for the root uid on the base interface if xt_qtaguid is in use.
         // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
-        // overhead (20 bytes per packet), in both directions.
+        // overhead (20 bytes per packet), in rx direction.
         final NetworkStats.Entry xtRootUidEntry = new NetworkStats.Entry(
                 baseIface, rootUid, SET_DEFAULT, TAG_NONE,
                 31113087 /* rxBytes */,
                 22588 /* rxPackets */,
-                1169942 /* txBytes */,
-                13902 /* txPackets */,
+                17607 /* txBytes */,
+                97 /* txPackets */,
                 0 /* operations */);
 
         final NetworkStats.Entry otherEntry = new NetworkStats.Entry(
diff --git a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java b/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
index 7f8e7b5..ba0448c 100644
--- a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
@@ -35,8 +35,8 @@
 import android.annotation.Nullable;
 import android.net.IpPrefix;
 import android.net.MacAddress;
-import android.net.util.SharedLog;
 import android.net.dhcp.DhcpServer.Clock;
+import android.net.util.SharedLog;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -126,7 +126,7 @@
         mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
 
         // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
-        requestAddresses((byte)11);
+        requestAddresses((byte) 11);
 
         try {
             mRepo.getOffer(null, TEST_MAC_2,
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
new file mode 100644
index 0000000..4a6f20a
--- /dev/null
+++ b/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import static android.net.InetAddresses.parseNumericAddress;
+
+import static com.google.android.collect.Sets.newHashSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.LinkAddress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DhcpServingParamsParcelExtTest {
+    private static final Inet4Address TEST_ADDRESS = inet4Addr("192.168.0.123");
+    private static final int TEST_ADDRESS_PARCELED = 0xc0a8007b;
+    private static final int TEST_PREFIX_LENGTH = 17;
+    private static final int TEST_LEASE_TIME_SECS = 120;
+    private static final int TEST_MTU = 1000;
+    private static final Set<Inet4Address> TEST_ADDRESS_SET =
+            newHashSet(inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124"));
+    private static final Set<Integer> TEST_ADDRESS_SET_PARCELED =
+            newHashSet(0xc0a8017b, 0xc0a8017c);
+
+    private DhcpServingParamsParcelExt mParcel;
+
+    @Before
+    public void setUp() {
+        mParcel = new DhcpServingParamsParcelExt();
+    }
+
+    @Test
+    public void testSetServerAddr() {
+        mParcel.setServerAddr(new LinkAddress(TEST_ADDRESS, TEST_PREFIX_LENGTH));
+
+        assertEquals(TEST_ADDRESS_PARCELED, mParcel.serverAddr);
+        assertEquals(TEST_PREFIX_LENGTH, mParcel.serverAddrPrefixLength);
+    }
+
+    @Test
+    public void testSetDefaultRouters() {
+        mParcel.setDefaultRouters(TEST_ADDRESS_SET);
+        assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.defaultRouters));
+    }
+
+    @Test
+    public void testSetDnsServers() {
+        mParcel.setDnsServers(TEST_ADDRESS_SET);
+        assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.dnsServers));
+    }
+
+    @Test
+    public void testSetExcludedAddrs() {
+        mParcel.setExcludedAddrs(TEST_ADDRESS_SET);
+        assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.excludedAddrs));
+    }
+
+    @Test
+    public void testSetDhcpLeaseTimeSecs() {
+        mParcel.setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS);
+        assertEquals(TEST_LEASE_TIME_SECS, mParcel.dhcpLeaseTimeSecs);
+    }
+
+    @Test
+    public void testSetLinkMtu() {
+        mParcel.setLinkMtu(TEST_MTU);
+        assertEquals(TEST_MTU, mParcel.linkMtu);
+    }
+
+    @Test
+    public void testSetMetered() {
+        mParcel.setMetered(true);
+        assertTrue(mParcel.metered);
+        mParcel.setMetered(false);
+        assertFalse(mParcel.metered);
+    }
+
+    private static Inet4Address inet4Addr(String addr) {
+        return (Inet4Address) parseNumericAddress(addr);
+    }
+
+    private static Set<Integer> asSet(int[] ints) {
+        return IntStream.of(ints).boxed().collect(Collectors.toSet());
+    }
+}
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java b/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
index b6a4073..2ab2246 100644
--- a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
@@ -16,6 +16,7 @@
 
 package android.net.dhcp;
 
+import static android.net.NetworkUtils.inet4AddressToIntHTH;
 import static android.net.dhcp.DhcpServingParams.MTU_UNSET;
 
 import static junit.framework.Assert.assertEquals;
@@ -27,6 +28,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.LinkAddress;
+import android.net.NetworkUtils;
 import android.net.dhcp.DhcpServingParams.InvalidParameterException;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -35,8 +37,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.reflect.Modifier;
 import java.net.Inet4Address;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -56,6 +60,7 @@
     private static final int TEST_MTU = 1500;
     private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>(
             Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
+    private static final boolean TEST_METERED = true;
 
     @Before
     public void setUp() {
@@ -65,7 +70,8 @@
                 .setDnsServers(TEST_DNS_SERVERS)
                 .setServerAddr(TEST_LINKADDR)
                 .setLinkMtu(TEST_MTU)
-                .setExcludedAddrs(TEST_EXCLUDED_ADDRS);
+                .setExcludedAddrs(TEST_EXCLUDED_ADDRS)
+                .setMetered(TEST_METERED);
     }
 
     @Test
@@ -91,6 +97,7 @@
         assertEquals(TEST_DNS_SERVERS, params.dnsServers);
         assertEquals(TEST_LINKADDR, params.serverAddr);
         assertEquals(TEST_MTU, params.linkMtu);
+        assertEquals(TEST_METERED, params.metered);
 
         assertContains(params.excludedAddrs, TEST_EXCLUDED_ADDRS);
         assertContains(params.excludedAddrs, TEST_DEFAULT_ROUTERS);
@@ -159,6 +166,39 @@
         mBuilder.setDefaultRouters(parseAddr("192.168.254.254")).build();
     }
 
+    @Test
+    public void testFromParcelableObject() throws InvalidParameterException {
+        final DhcpServingParams params = mBuilder.build();
+        final DhcpServingParamsParcel parcel = new DhcpServingParamsParcel();
+        parcel.defaultRouters = toIntArray(TEST_DEFAULT_ROUTERS);
+        parcel.dhcpLeaseTimeSecs = TEST_LEASE_TIME_SECS;
+        parcel.dnsServers = toIntArray(TEST_DNS_SERVERS);
+        parcel.serverAddr = inet4AddressToIntHTH(TEST_SERVER_ADDR);
+        parcel.serverAddrPrefixLength = TEST_LINKADDR.getPrefixLength();
+        parcel.linkMtu = TEST_MTU;
+        parcel.excludedAddrs = toIntArray(TEST_EXCLUDED_ADDRS);
+        parcel.metered = TEST_METERED;
+        final DhcpServingParams parceled = DhcpServingParams.fromParcelableObject(parcel);
+
+        assertEquals(params.defaultRouters, parceled.defaultRouters);
+        assertEquals(params.dhcpLeaseTimeSecs, parceled.dhcpLeaseTimeSecs);
+        assertEquals(params.dnsServers, parceled.dnsServers);
+        assertEquals(params.serverAddr, parceled.serverAddr);
+        assertEquals(params.linkMtu, parceled.linkMtu);
+        assertEquals(params.excludedAddrs, parceled.excludedAddrs);
+        assertEquals(params.metered, parceled.metered);
+
+        // Ensure that we do not miss any field if added in the future
+        final long numFields = Arrays.stream(DhcpServingParams.class.getDeclaredFields())
+                .filter(f -> !Modifier.isStatic(f.getModifiers()))
+                .count();
+        assertEquals(7, numFields);
+    }
+
+    private static int[] toIntArray(Collection<Inet4Address> addrs) {
+        return addrs.stream().mapToInt(NetworkUtils::inet4AddressToIntHTH).toArray();
+    }
+
     private static <T> void assertContains(@NonNull Set<T> set, @NonNull Set<T> subset) {
         for (final T elem : subset) {
             assertContains(set, elem);
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index 2c675c6..0178228 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -16,6 +16,16 @@
 
 package android.net.ip;
 
+import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
+import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+import static android.net.ip.IpServer.STATE_AVAILABLE;
+import static android.net.ip.IpServer.STATE_TETHERED;
+import static android.net.ip.IpServer.STATE_UNAVAILABLE;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -31,16 +41,6 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
-import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
-import static android.net.ConnectivityManager.TETHERING_USB;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.ip.IpServer.STATE_AVAILABLE;
-import static android.net.ip.IpServer.STATE_TETHERED;
-import static android.net.ip.IpServer.STATE_UNAVAILABLE;
-
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
@@ -60,8 +60,6 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.text.TextUtils;
 
-import java.net.Inet4Address;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -71,6 +69,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.net.Inet4Address;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class IpServerTest {
diff --git a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
index 788924b..90bf7b1 100644
--- a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -159,7 +159,7 @@
         assertStatsEntry(stats, "v4-wlan0", 1000, SET_DEFAULT, 0x0, 30812L, 2310L);
         assertStatsEntry(stats, "v4-wlan0", 10102, SET_DEFAULT, 0x0, 10022L, 3330L);
         assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 9532772L, 254112L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 15229L, 5766L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 15229L, 0L);
         assertStatsEntry(stats, "wlan0", 1000, SET_DEFAULT, 0x0, 6126L, 2013L);
         assertStatsEntry(stats, "wlan0", 10013, SET_DEFAULT, 0x0, 0L, 144L);
         assertStatsEntry(stats, "wlan0", 10018, SET_DEFAULT, 0x0, 5980263L, 167667L);
@@ -170,6 +170,8 @@
         assertStatsEntry(stats, "dummy0", 0, SET_DEFAULT, 0x0, 0L, 168L);
         assertStatsEntry(stats, "lo", 0, SET_DEFAULT, 0x0, 1288L, 1288L);
 
+        assertNoStatsEntry(stats, "wlan0", 1029, SET_DEFAULT, 0x0);
+
         NetworkStatsFactory.clearStackedIfaces();
     }
 
@@ -191,12 +193,12 @@
         // Stats snapshot before the download
         stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_before);
         assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesBefore, 5199872L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesBefore, 647888L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesBefore, 0L);
 
         // Stats snapshot after the download
         stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_after);
         assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 647587L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 0L);
 
         NetworkStatsFactory.clearStackedIfaces();
     }
@@ -252,6 +254,15 @@
         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
     }
 
+    private static void assertNoStatsEntry(NetworkStats stats, String iface, int uid, int set,
+            int tag) {
+        final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO);
+        if (i >= 0) {
+            fail("unexpected NetworkStats entry at " + i);
+        }
+    }
+
     private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
             int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) {
         final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO,
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat b/tests/net/res/raw/xt_qtaguid_with_clat
index 77e5c7b..6cd7499 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat
+++ b/tests/net/res/raw/xt_qtaguid_with_clat
@@ -7,7 +7,7 @@
 7 v4-wlan0 0x0 10060 1 1448660 1041 31192 753 1448660 1041 0 0 0 0 31192 753 0 0 0 0
 8 v4-wlan0 0x0 10102 0 9702 16 2870 23 9702 16 0 0 0 0 2870 23 0 0 0 0
 9 v4-wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-10 wlan0 0x0 0 0 11058671 7892 312046 5113 11043898 7811 13117 61 1656 20 306544 5046 3230 38 2272 29
+10 wlan0 0x0 0 0 11058671 7892 0 0 11043898 7811 13117 61 1656 20 0 0 0 0 0 0
 11 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 12 wlan0 0x0 1000 0 6126 13 2013 16 5934 11 192 2 0 0 1821 14 192 2 0 0
 13 wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
@@ -41,3 +41,5 @@
 41 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 42 lo 0x0 0 0 1288 16 1288 16 0 0 532 8 756 8 0 0 532 8 756 8
 43 lo 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+44 wlan0 0x0 1029 0 0 0 312046 5113 0 0 0 0 0 0 306544 5046 3230 38 2272 29
+45 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
index c78f84f..9f86153 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
@@ -9,7 +9,7 @@
 9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0
 10 v4-wlan0 0x0 10106 0 2232 18 2232 18 0 0 2232 18 0 0 0 0 2232 18 0 0
 11 v4-wlan0 0x0 10106 1 432952718 314238 5442288 121260 432950238 314218 2480 20 0 0 5433900 121029 8388 231 0 0
-12 wlan0 0x0 0 0 440746376 329772 8524052 130894 439660007 315369 232001 1276 854368 13127 7871216 121284 108568 1325 544268 8285
+12 wlan0 0x0 0 0 440746376 329772 0 0 439660007 315369 232001 1276 854368 13127 0 0 0 0 0 0
 13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0
 15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0
@@ -185,3 +185,5 @@
 185 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 186 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3
 187 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+188 wlan0 0x0 1029 0 0 0 8524052 130894 0 0 0 0 0 0 7871216 121284 108568 1325 544268 8285
+189 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before
index d035387..ce4bcc3 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before
@@ -9,7 +9,7 @@
 9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0
 10 v4-wlan0 0x0 10106 0 1488 12 1488 12 0 0 1488 12 0 0 0 0 1488 12 0 0
 11 v4-wlan0 0x0 10106 1 323981189 235142 3509032 84542 323979453 235128 1736 14 0 0 3502676 84363 6356 179 0 0
-12 wlan0 0x0 0 0 330187296 250652 5855801 94173 329106990 236273 226202 1255 854104 13124 5208040 84634 103637 1256 544124 8283
+12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0
 13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0
 15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0
@@ -183,3 +183,5 @@
 183 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 184 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3
 185 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+186 wlan0 0x0 1029 0 0 0 5855801 94173 0 0 0 0 0 0 5208040 84634 103637 1256 544124 8283
+187 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/tests/net/res/raw/xt_qtaguid_with_clat_simple
index 7f0e56f..8c132e7 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_simple
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_simple
@@ -1,5 +1,6 @@
 idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
-2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 4100 41 0 0 0 0 0 0 0 0
+2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0
 3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 wlan0 0x0 0 0 46860 213 4920 41 46860 213 4920 41 0 0 0 0 0 0 0 0
+4 wlan0 0x0 0 0 46860 213 0 0 46860 213 0 0 0 0 0 0 0 0 0 0
 5 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+6 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0
diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h
index 09430f2..b4ea624 100644
--- a/tools/aapt/ConfigDescription.h
+++ b/tools/aapt/ConfigDescription.h
@@ -29,7 +29,7 @@
         size = sizeof(android::ResTable_config);
     }
 
-    ConfigDescription(const android::ResTable_config&o) {  // NOLINT(implicit)
+    ConfigDescription(const android::ResTable_config&o) {  // NOLINT(google-explicit-constructor)
         *static_cast<android::ResTable_config*>(this) = o;
         size = sizeof(android::ResTable_config);
     }
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index ba498e1..c42a888 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -170,6 +170,7 @@
     srcs: [
         "test/Builders.cpp",
         "test/Common.cpp",
+        "test/Fixture.cpp",
         "**/*_test.cpp",
     ] + toolSources,
     static_libs: [
@@ -177,7 +178,10 @@
         "libgmock",
     ],
     defaults: ["aapt2_defaults"],
-    data: ["integration-tests/CompileTest/**/*"],
+    data: [
+         "integration-tests/CompileTest/**/*",
+         "integration-tests/CommandTests/**/*"
+    ],
 }
 
 // ==========================================================
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 9460c9e..5f664f5 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -445,7 +445,7 @@
  public:
   using xml::ConstVisitor::Visit;
 
-  XmlPrinter(Printer* printer) : printer_(printer) {
+  explicit XmlPrinter(Printer* printer) : printer_(printer) {
   }
 
   void Visit(const xml::Element* el) override {
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index dd5c751..67ba895 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -113,7 +113,7 @@
   ResourceNameRef() = default;
   ResourceNameRef(const ResourceNameRef&) = default;
   ResourceNameRef(ResourceNameRef&&) = default;
-  ResourceNameRef(const ResourceName& rhs);  // NOLINT(implicit)
+  ResourceNameRef(const ResourceName& rhs);  // NOLINT(google-explicit-constructor)
   ResourceNameRef(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
   ResourceNameRef& operator=(const ResourceNameRef& rhs) = default;
   ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
@@ -144,7 +144,7 @@
 
   ResourceId();
   ResourceId(const ResourceId& rhs);
-  ResourceId(uint32_t res_id);  // NOLINT(implicit)
+  ResourceId(uint32_t res_id);  // NOLINT(google-explicit-constructor)
   ResourceId(uint8_t p, uint8_t t, uint16_t e);
 
   bool is_valid() const;
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index f63a074..52375a3 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -584,7 +584,7 @@
 
 class CompileContext : public IAaptContext {
  public:
-  CompileContext(IDiagnostics* diagnostics) : diagnostics_(diagnostics) {
+  explicit CompileContext(IDiagnostics* diagnostics) : diagnostics_(diagnostics) {
   }
 
   PackageType GetPackageType() override {
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 4492f6b..85f9080 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -60,16 +60,17 @@
 class BinaryApkSerializer : public IApkSerializer {
  public:
   BinaryApkSerializer(IAaptContext* context, const Source& source,
-                   const TableFlattenerOptions& options)
-      : IApkSerializer(context, source), tableFlattenerOptions_(options) {}
+                      const TableFlattenerOptions& table_flattener_options,
+                      const XmlFlattenerOptions& xml_flattener_options)
+      : IApkSerializer(context, source),
+        table_flattener_options_(table_flattener_options),
+        xml_flattener_options_(xml_flattener_options) {}
 
   bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
                     IArchiveWriter* writer, uint32_t compression_flags) override {
     BigBuffer buffer(4096);
-    XmlFlattenerOptions options = {};
-    options.use_utf16 = utf16;
-    options.keep_raw_values = true;
-    XmlFlattener flattener(&buffer, options);
+    xml_flattener_options_.use_utf16 = utf16;
+    XmlFlattener flattener(&buffer, xml_flattener_options_);
     if (!flattener.Consume(context_, xml)) {
       return false;
     }
@@ -80,7 +81,7 @@
 
   bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
     BigBuffer buffer(4096);
-    TableFlattener table_flattener(tableFlattenerOptions_, &buffer);
+    TableFlattener table_flattener(table_flattener_options_, &buffer);
     if (!table_flattener.Consume(context_, table)) {
       return false;
     }
@@ -136,7 +137,8 @@
   }
 
  private:
-  TableFlattenerOptions tableFlattenerOptions_;
+  TableFlattenerOptions table_flattener_options_;
+  XmlFlattenerOptions xml_flattener_options_;
 
   DISALLOW_COPY_AND_ASSIGN(BinaryApkSerializer);
 };
@@ -252,13 +254,15 @@
 };
 
 int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer,
-            ApkFormat output_format, TableFlattenerOptions& options) {
+            ApkFormat output_format, TableFlattenerOptions table_flattener_options,
+            XmlFlattenerOptions xml_flattener_options) {
   // Do not change the ordering of strings in the values string pool
-  options.sort_stringpool_entries = false;
+  table_flattener_options.sort_stringpool_entries = false;
 
   unique_ptr<IApkSerializer> serializer;
   if (output_format == ApkFormat::kBinary) {
-    serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), options));
+    serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), table_flattener_options,
+                                             xml_flattener_options));
   } else if (output_format == ApkFormat::kProto) {
     serializer.reset(new ProtoApkSerializer(context, apk->GetSource()));
   } else {
@@ -378,7 +382,8 @@
     return 1;
   }
 
-  return Convert(&context, apk.get(), writer.get(), format, options_);
+  return Convert(&context, apk.get(), writer.get(), format, table_flattener_options_,
+                 xml_flattener_options_);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
index 14016b1..7e2029d 100644
--- a/tools/aapt2/cmd/Convert.h
+++ b/tools/aapt2/cmd/Convert.h
@@ -20,6 +20,7 @@
 #include "Command.h"
 #include "LoadedApk.h"
 #include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
 
 namespace aapt {
 
@@ -33,8 +34,12 @@
         kOutputFormatProto, kOutputFormatBinary, kOutputFormatBinary), &output_format_);
     AddOptionalSwitch("--enable-sparse-encoding",
         "Enables encoding sparse entries using a binary search tree.\n"
-            "This decreases APK size at the cost of resource retrieval performance.",
-        &options_.use_sparse_entries);
+        "This decreases APK size at the cost of resource retrieval performance.",
+         &table_flattener_options_.use_sparse_entries);
+    AddOptionalSwitch("--keep-raw-values",
+        android::base::StringPrintf("Preserve raw attribute values in xml files when using the"
+            " '%s' output format", kOutputFormatBinary),
+        &xml_flattener_options_.keep_raw_values);
     AddOptionalSwitch("-v", "Enables verbose logging", &verbose_);
   }
 
@@ -44,14 +49,16 @@
   const static char* kOutputFormatProto;
   const static char* kOutputFormatBinary;
 
-  TableFlattenerOptions options_;
+  TableFlattenerOptions table_flattener_options_;
+  XmlFlattenerOptions xml_flattener_options_;
   std::string output_path_;
   Maybe<std::string> output_format_;
   bool verbose_ = false;
 };
 
 int Convert(IAaptContext* context, LoadedApk* input, IArchiveWriter* output_writer,
-            ApkFormat output_format, TableFlattenerOptions& options);
+            ApkFormat output_format,TableFlattenerOptions table_flattener_options,
+            XmlFlattenerOptions xml_flattener_options);
 
 }  // namespace aapt
 
diff --git a/tools/aapt2/cmd/Convert_test.cpp b/tools/aapt2/cmd/Convert_test.cpp
new file mode 100644
index 0000000..2e43150
--- /dev/null
+++ b/tools/aapt2/cmd/Convert_test.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Convert.h"
+
+#include "LoadedApk.h"
+#include "test/Test.h"
+
+using testing::Eq;
+using testing::Ne;
+
+namespace aapt {
+
+using ConvertTest = CommandTestFixture;
+
+TEST_F(ConvertTest, RemoveRawXmlStrings) {
+  StdErrDiagnostics diag;
+  const std::string compiled_files_dir = GetTestPath("compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
+                          compiled_files_dir, &diag));
+
+  const std::string out_apk = GetTestPath("out.apk");
+  std::vector<std::string> link_args = {
+      "--manifest", GetDefaultManifest(),
+      "-o", out_apk,
+      "--keep-raw-values",
+      "--proto-format"
+  };
+
+  ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+  const std::string out_convert_apk = GetTestPath("out_convert.apk");
+  std::vector<android::StringPiece> convert_args = {
+      "-o", out_convert_apk,
+      "--output-format", "binary",
+      out_apk,
+  };
+  ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0));
+
+  // Load the binary xml tree
+  android::ResXMLTree tree;
+  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_convert_apk, &diag);
+  AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+  // Check that the raw string index has not been assigned
+  EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
+}
+
+TEST_F(ConvertTest, KeepRawXmlStrings) {
+  StdErrDiagnostics diag;
+  const std::string compiled_files_dir = GetTestPath("compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
+                          compiled_files_dir, &diag));
+
+  const std::string out_apk = GetTestPath("out.apk");
+  std::vector<std::string> link_args = {
+      "--manifest", GetDefaultManifest(),
+      "-o", out_apk,
+      "--keep-raw-values",
+      "--proto-format"
+  };
+
+  ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+  const std::string out_convert_apk = GetTestPath("out_convert.apk");
+  std::vector<android::StringPiece> convert_args = {
+      "-o", out_convert_apk,
+      "--output-format", "binary",
+      "--keep-raw-values",
+      out_apk,
+  };
+  ASSERT_THAT(ConvertCommand().Execute(convert_args, &std::cerr), Eq(0));
+
+  // Load the binary xml tree
+  android::ResXMLTree tree;
+  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_convert_apk, &diag);
+  AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+  // Check that the raw string index has been set to the correct string pool entry
+  int32_t raw_index = tree.getAttributeValueStringID(0);
+  ASSERT_THAT(raw_index, Ne(-1));
+  EXPECT_THAT(util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)), Eq("007"));
+}
+
+}  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 1b5601d..729447e 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -78,7 +78,7 @@
 
 class LinkContext : public IAaptContext {
  public:
-  LinkContext(IDiagnostics* diagnostics)
+  explicit LinkContext(IDiagnostics* diagnostics)
       : diagnostics_(diagnostics), name_mangler_({}), symbols_(&name_mangler_) {
   }
 
@@ -163,7 +163,7 @@
 // See b/37498913.
 class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
  public:
-  FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) {
+  explicit FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) {
   }
 
   virtual ~FeatureSplitSymbolTableDelegate() = default;
@@ -1545,7 +1545,8 @@
   // to the IArchiveWriter.
   bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
                 ResourceTable* table) {
-    const bool keep_raw_values = context_->GetPackageType() == PackageType::kStaticLib;
+    const bool keep_raw_values = (context_->GetPackageType() == PackageType::kStaticLib)
+                                 || options_.keep_raw_values;
     bool result = FlattenXml(context_, *manifest, kAndroidManifestPath, keep_raw_values,
                              true /*utf16*/, options_.output_format, writer);
     if (!result) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index f740d53..f70470a 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -75,6 +75,7 @@
 
   // Flattening options.
   TableFlattenerOptions table_flattener_options;
+  bool keep_raw_values = false;
 
   // Split APK options.
   TableSplitterOptions table_splitter_options;
@@ -244,6 +245,8 @@
         &options_.extensions_to_not_compress);
     AddOptionalSwitch("--no-compress", "Do not compress any resources.",
         &options_.do_not_compress_anything);
+    AddOptionalSwitch("--keep-raw-values", "Preserve raw attribute values in xml files.",
+        &options_.keep_raw_values);
     AddOptionalSwitch("--warn-manifest-validation",
         "Treat manifest validation errors as warnings.",
         &options_.manifest_fixer_options.warn_validation);
@@ -252,7 +255,6 @@
             "Syntax: path/to/output.apk:<config>[,<config>[...]].\n"
             "On Windows, use a semicolon ';' separator instead.",
         &split_args_);
-    AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
     AddOptionalSwitch("--debug-mode",
         "Inserts android:debuggable=\"true\" in to the application node of the\n"
             "manifest, making the application debuggable even on production devices.",
@@ -260,6 +262,7 @@
     AddOptionalSwitch("--strict-visibility",
         "Do not allow overlays with different visibility levels.",
         &options_.strict_visibility);
+    AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
   }
 
   int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
new file mode 100644
index 0000000..3c8b72d
--- /dev/null
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -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.
+ */
+
+#include "Link.h"
+
+#include "LoadedApk.h"
+#include "test/Test.h"
+
+using testing::Eq;
+using testing::Ne;
+
+namespace aapt {
+
+using LinkTest = CommandTestFixture;
+
+TEST_F(LinkTest, RemoveRawXmlStrings) {
+  StdErrDiagnostics diag;
+  const std::string compiled_files_dir = GetTestPath("compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
+                          compiled_files_dir, &diag));
+
+  const std::string out_apk = GetTestPath("out.apk");
+  std::vector<std::string> link_args = {
+      "--manifest", GetDefaultManifest(),
+      "-o", out_apk,
+  };
+
+  ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+  // Load the binary xml tree
+  android::ResXMLTree tree;
+  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+  AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+  // Check that the raw string index has not been assigned
+  EXPECT_THAT(tree.getAttributeValueStringID(0), Eq(-1));
+}
+
+TEST_F(LinkTest, KeepRawXmlStrings) {
+  StdErrDiagnostics diag;
+  const std::string compiled_files_dir = GetTestPath("compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/xml/test.xml"), R"(<Item AgentCode="007"/>)",
+                          compiled_files_dir, &diag));
+
+  const std::string out_apk = GetTestPath("out.apk");
+  std::vector<std::string> link_args = {
+      "--manifest", GetDefaultManifest(),
+      "-o", out_apk,
+      "--keep-raw-values"
+  };
+
+  ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+  // Load the binary xml tree
+  android::ResXMLTree tree;
+  std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+  AssertLoadXml(apk.get(), "res/xml/test.xml", &tree);
+
+  // Check that the raw string index has been set to the correct string pool entry
+  int32_t raw_index = tree.getAttributeValueStringID(0);
+  ASSERT_THAT(raw_index, Ne(-1));
+  EXPECT_THAT(util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)), Eq("007"));
+}
+
+}  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/CommandTests/android-28.jar b/tools/aapt2/integration-tests/CommandTests/android-28.jar
new file mode 100644
index 0000000..ef7576d
--- /dev/null
+++ b/tools/aapt2/integration-tests/CommandTests/android-28.jar
Binary files differ
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 38b4860..f9656d1 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -49,7 +49,7 @@
  public:
   KeepSet() = default;
 
-  KeepSet(bool conditional_keep_rules) : conditional_keep_rules_(conditional_keep_rules) {
+  explicit KeepSet(bool conditional_keep_rules) : conditional_keep_rules_(conditional_keep_rules) {
   }
 
   inline void AddManifestClass(const UsageLocation& file, const std::string& class_name) {
diff --git a/tools/aapt2/link/XmlCompatVersioner.h b/tools/aapt2/link/XmlCompatVersioner.h
index 099e23c..9980618 100644
--- a/tools/aapt2/link/XmlCompatVersioner.h
+++ b/tools/aapt2/link/XmlCompatVersioner.h
@@ -55,7 +55,7 @@
  public:
   using Rules = std::unordered_map<ResourceId, std::unique_ptr<IDegradeRule>>;
 
-  XmlCompatVersioner(const Rules* rules);
+  explicit XmlCompatVersioner(const Rules* rules);
 
   std::vector<std::unique_ptr<xml::XmlResource>> Process(IAaptContext* context,
                                                          xml::XmlResource* doc,
@@ -83,7 +83,7 @@
 
 class DegradeToManyRule : public IDegradeRule {
  public:
-  DegradeToManyRule(std::vector<ReplacementAttr> attrs);
+  explicit DegradeToManyRule(std::vector<ReplacementAttr> attrs);
   virtual ~DegradeToManyRule() = default;
 
   std::vector<DegradeResult> Degrade(const xml::Element& src_el, const xml::Attribute& src_attr,
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 51a2e37..2d8bd02 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -71,7 +71,7 @@
     bool is_dynamic = false;
   };
 
-  SymbolTable(NameMangler* mangler);
+  explicit SymbolTable(NameMangler* mangler);
 
   // Overrides the default ISymbolTableDelegate, which allows a custom defined strategy for
   // looking up resources from a set of sources.
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 50b41f1..777ca5c 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -173,10 +173,12 @@
 template <typename TValue>
 class ValueEqMatcher {
  public:
+  // NOLINTNEXTLINE(google-explicit-constructor)
   ValueEqMatcher(TValue expected) : expected_(std::move(expected)) {
   }
 
   template <typename T>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   operator ::testing::Matcher<T>() const {
     return ::testing::Matcher<T>(new ValueEqImpl<T>(&expected_));
   }
@@ -188,10 +190,12 @@
 template <typename TValue>
 class ValueEqPointerMatcher {
  public:
+  // NOLINTNEXTLINE(google-explicit-constructor)
   ValueEqPointerMatcher(const TValue* expected) : expected_(expected) {
   }
 
   template <typename T>
+  // NOLINTNEXTLINE(google-explicit-constructor)
   operator ::testing::Matcher<T>() const {
     return ::testing::Matcher<T>(new ValueEqImpl<T>(expected_));
   }
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
new file mode 100644
index 0000000..aae79fa
--- /dev/null
+++ b/tools/aapt2/test/Fixture.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test/Fixture.h"
+
+#include <dirent.h>
+
+#include "android-base/errors.h"
+#include "android-base/file.h"
+#include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
+#include "androidfw/StringPiece.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "cmd/Compile.h"
+#include "cmd/Link.h"
+#include "io/FileStream.h"
+#include "io/Util.h"
+#include "util/Files.h"
+
+using testing::Eq;
+using testing::Ne;
+
+namespace aapt {
+
+void ClearDirectory(const android::StringPiece& path) {
+  const std::string root_dir = path.to_string();
+  std::unique_ptr<DIR, decltype(closedir)*> dir(opendir(root_dir.data()), closedir);
+  if (!dir) {
+    StdErrDiagnostics().Error(DiagMessage() << android::base::SystemErrorCodeToString(errno));
+    return;
+  }
+
+  while (struct dirent* entry = readdir(dir.get())) {
+    // Do not delete hidden files and do not recurse to the parent of this directory
+    if (util::StartsWith(entry->d_name, ".")) {
+      continue;
+    }
+
+    std::string full_path = file::BuildPath({root_dir, entry->d_name});
+    if (file::GetFileType(full_path) == file::FileType::kDirectory) {
+      ClearDirectory(full_path);
+#ifdef _WIN32
+      _rmdir(full_path.c_str());
+#else
+      rmdir(full_path.c_str());
+#endif
+    } else {
+      android::base::utf8::unlink(full_path.c_str());
+    }
+  }
+}
+
+void TestDirectoryFixture::SetUp() {
+  temp_dir_ = file::BuildPath({android::base::GetExecutableDirectory(),
+                               "_temp",
+                               testing::UnitTest::GetInstance()->current_test_case()->name(),
+                               testing::UnitTest::GetInstance()->current_test_info()->name()});
+  ASSERT_TRUE(file::mkdirs(temp_dir_));
+  ClearDirectory(temp_dir_);
+}
+
+void TestDirectoryFixture::TearDown() {
+  ClearDirectory(temp_dir_);
+}
+
+bool TestDirectoryFixture::WriteFile(const std::string& path, const std::string& contents) {
+  CHECK(util::StartsWith(path, temp_dir_))
+      << "Attempting to create a file outside of test temporary directory.";
+
+  // Create any intermediate directories specified in the path
+  auto pos = std::find(path.rbegin(), path.rend(), file::sDirSep);
+  if (pos != path.rend()) {
+    std::string dirs = path.substr(0, (&*pos - path.data()));
+    file::mkdirs(dirs);
+  }
+
+  return android::base::WriteStringToFile(contents, path);
+}
+
+bool CommandTestFixture::CompileFile(const std::string& path, const std::string& contents,
+                                     const android::StringPiece& out_dir, IDiagnostics* diag) {
+  CHECK(WriteFile(path, contents));
+  CHECK(file::mkdirs(out_dir.data()));
+  return CompileCommand(diag).Execute({path, "-o", out_dir, "-v"}, &std::cerr) == 0;
+}
+
+bool CommandTestFixture::Link(const std::vector<std::string>& args,
+                              const android::StringPiece& flat_dir, IDiagnostics* diag) {
+  std::vector<android::StringPiece> link_args;
+  for(const std::string& arg : args) {
+    link_args.emplace_back(arg);
+  }
+
+  // Link against the android SDK
+  std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(),
+                                             "integration-tests", "CommandTests",
+                                             "android-28.jar"});
+  link_args.insert(link_args.end(), {"-I", android_sdk});
+
+  // Add the files from the compiled resources directory to the link file arguments
+  Maybe<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
+  if (compiled_files) {
+    for (std::string& compile_file : compiled_files.value()) {
+      compile_file = file::BuildPath({flat_dir, compile_file});
+      link_args.emplace_back(std::move(compile_file));
+    }
+  }
+
+  return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
+}
+
+std::string CommandTestFixture::GetDefaultManifest() {
+  const std::string manifest_file = GetTestPath("AndroidManifest.xml");
+  CHECK(WriteFile(manifest_file, R"(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.aapt.command.test">
+      </manifest>)"));
+  return manifest_file;
+}
+
+void CommandTestFixture::AssertLoadXml(LoadedApk *apk, const android::StringPiece &xml_path,
+                                       android::ResXMLTree *out_tree) {
+  ASSERT_THAT(apk, Ne(nullptr));
+
+  io::IFile* file = apk->GetFileCollection()->FindFile(xml_path);
+  ASSERT_THAT(file, Ne(nullptr));
+
+  std::unique_ptr<io::IData> data = file->OpenAsData();
+  ASSERT_THAT(data, Ne(nullptr));
+
+  out_tree->setTo(data->data(), data->size());
+  ASSERT_THAT(out_tree->getError(), Eq(android::OK));
+  while (out_tree->next() != android::ResXMLTree::START_TAG) {
+    ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+    ASSERT_THAT(out_tree->getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
+  }
+}
+
+} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/test/Fixture.h b/tools/aapt2/test/Fixture.h
new file mode 100644
index 0000000..89d3b7b
--- /dev/null
+++ b/tools/aapt2/test/Fixture.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_TEST_FIXTURE_H
+#define AAPT_TEST_FIXTURE_H
+
+#include "android-base/file.h"
+#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "io/Util.h"
+#include "util/Files.h"
+#include "LoadedApk.h"
+
+namespace aapt {
+
+class TestDirectoryFixture : public ::testing::Test {
+ public:
+  TestDirectoryFixture() = default;
+  virtual ~TestDirectoryFixture() = default;
+
+  // Creates the test directory or clears its contents if it contains previously created files.
+  void SetUp() override;
+
+  // Clears the contents of the test directory.
+  void TearDown() override;
+
+  // Retrieve the test directory of the fixture.
+  const android::StringPiece GetTestDirectory() {
+    return temp_dir_;
+  }
+
+  // Retrieves the absolute path of the specified relative path in the test directory. Directories
+  // should be separated using forward slashes ('/'), and these slashes will be translated to
+  // backslashes when running Windows tests.
+  const std::string GetTestPath(const android::StringPiece& path) {
+    std::string base = temp_dir_;
+    for (android::StringPiece part : util::Split(path, '/')) {
+      file::AppendPath(&base, part);
+    }
+    return base;
+  }
+
+  // Creates a file with the specified contents, creates any intermediate directories in the
+  // process. The file path must be an absolute path within the test directory.
+  bool WriteFile(const std::string& path, const std::string& contents);
+
+ private:
+  std::string temp_dir_;
+  DISALLOW_COPY_AND_ASSIGN(TestDirectoryFixture);
+};
+
+class CommandTestFixture : public TestDirectoryFixture {
+ public:
+  CommandTestFixture() = default;
+  virtual ~CommandTestFixture() = default;
+
+  // Wries the contents of the file to the specified path. The file is compiled and the flattened
+  // file is written to the out directory.
+  bool CompileFile(const std::string& path, const std::string& contents,
+                   const android::StringPiece& flat_out_dir, IDiagnostics* diag);
+
+  // Executes the link command with the specified arguments. The flattened files residing in the
+  // flat directory will be added to the link command as file arguments.
+  bool Link(const std::vector<std::string>& args, const android::StringPiece& flat_dir,
+            IDiagnostics* diag);
+
+  // Creates a minimal android manifest within the test directory and returns the file path.
+  std::string GetDefaultManifest();
+
+  // Asserts that loading the tree from the specified file in the apk succeeds.
+  void AssertLoadXml(LoadedApk* apk, const android::StringPiece& xml_path,
+                     android::ResXMLTree* out_tree);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CommandTestFixture);
+};
+
+} // namespace aapt
+
+#endif  // AAPT_TEST_FIXTURE_H
\ No newline at end of file
diff --git a/tools/aapt2/test/Test.h b/tools/aapt2/test/Test.h
index a24c01c..7d96d1f 100644
--- a/tools/aapt2/test/Test.h
+++ b/tools/aapt2/test/Test.h
@@ -23,5 +23,6 @@
 #include "test/Builders.h"
 #include "test/Common.h"
 #include "test/Context.h"
+#include "test/Fixture.h"
 
 #endif  // AAPT_TEST_TEST_H
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 5cfbbf2..7b268bb 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -180,6 +180,17 @@
   base->append(part.data(), part.size());
 }
 
+std::string BuildPath(std::vector<const StringPiece>&& args) {
+  if (args.empty()) {
+    return "";
+  }
+  std::string out = args[0].to_string();
+  for (int i = 1; i < args.size(); i++) {
+    file::AppendPath(&out, args[i]);
+  }
+  return out;
+}
+
 std::string PackageToPath(const StringPiece& package) {
   std::string out_path;
   for (const StringPiece& part : util::Tokenize(package, '.')) {
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 219e1a0..5839552 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -57,6 +57,9 @@
 // Appends a path to `base`, separated by the directory separator.
 void AppendPath(std::string* base, android::StringPiece part);
 
+// Concatenates the list of paths and separates each part with the directory separator.
+std::string BuildPath(std::vector<const android::StringPiece>&& args);
+
 // Makes all the directories in `path`. The last element in the path is interpreted as a directory.
 bool mkdirs(const std::string& path);
 
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index 031276c..047e1a5 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -44,12 +44,12 @@
   Maybe(const Maybe& rhs);
 
   template <typename U>
-  Maybe(const Maybe<U>& rhs);  // NOLINT(implicit)
+  Maybe(const Maybe<U>& rhs);  // NOLINT(google-explicit-constructor)
 
   Maybe(Maybe&& rhs) noexcept;
 
   template <typename U>
-  Maybe(Maybe<U>&& rhs);  // NOLINT(implicit)
+  Maybe(Maybe<U>&& rhs);  // NOLINT(google-explicit-constructor)
 
   Maybe& operator=(const Maybe& rhs);
 
@@ -64,12 +64,12 @@
   /**
    * Construct a Maybe holding a value.
    */
-  Maybe(const T& value);  // NOLINT(implicit)
+  Maybe(const T& value);  // NOLINT(google-explicit-constructor)
 
   /**
    * Construct a Maybe holding a value.
    */
-  Maybe(T&& value);  // NOLINT(implicit)
+  Maybe(T&& value);  // NOLINT(google-explicit-constructor)
 
   /**
    * True if this holds a value, false if
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 6476abd..acf1f1e 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -26,7 +26,7 @@
 $ apilint.py /tmp/currentblame.txt previous.txt --no-color
 """
 
-import re, sys, collections, traceback, argparse
+import re, sys, collections, traceback, argparse, itertools
 
 
 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
@@ -50,45 +50,37 @@
     return "\033[%sm" % (";".join(codes))
 
 
-def ident(raw):
-    """Strips superficial signature changes, giving us a strong key that
-    can be used to identify members across API levels."""
-    raw = raw.replace(" deprecated ", " ")
-    raw = raw.replace(" synchronized ", " ")
-    raw = raw.replace(" final ", " ")
-    raw = re.sub("<.+?>", "", raw)
-    if " throws " in raw:
-        raw = raw[:raw.index(" throws ")]
-    return raw
-
-
 class Field():
-    def __init__(self, clazz, line, raw, blame):
+    def __init__(self, clazz, line, raw, blame, sig_format = 1):
         self.clazz = clazz
         self.line = line
         self.raw = raw.strip(" {;")
         self.blame = blame
 
-        # drop generics for now; may need multiple passes
-        raw = re.sub("<[^<]+?>", "", raw)
-        raw = re.sub("<[^<]+?>", "", raw)
+        if sig_format == 2:
+            V2LineParser(raw).parse_into_field(self)
+        elif sig_format == 1:
+            # drop generics for now; may need multiple passes
+            raw = re.sub("<[^<]+?>", "", raw)
+            raw = re.sub("<[^<]+?>", "", raw)
 
-        raw = raw.split()
-        self.split = list(raw)
+            raw = raw.split()
+            self.split = list(raw)
 
-        for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
-            while r in raw: raw.remove(r)
+            for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
+                while r in raw: raw.remove(r)
 
-        # ignore annotations for now
-        raw = [ r for r in raw if not r.startswith("@") ]
+            # ignore annotations for now
+            raw = [ r for r in raw if not r.startswith("@") ]
 
-        self.typ = raw[0]
-        self.name = raw[1].strip(";")
-        if len(raw) >= 4 and raw[2] == "=":
-            self.value = raw[3].strip(';"')
-        else:
-            self.value = None
-        self.ident = ident(self.raw)
+            self.typ = raw[0]
+            self.name = raw[1].strip(";")
+            if len(raw) >= 4 and raw[2] == "=":
+                self.value = raw[3].strip(';"')
+            else:
+                self.value = None
+
+        self.ident = "-".join((self.typ, self.name, self.value or ""))
 
     def __hash__(self):
         return hash(self.raw)
@@ -96,48 +88,55 @@
     def __repr__(self):
         return self.raw
 
-
 class Method():
-    def __init__(self, clazz, line, raw, blame):
+    def __init__(self, clazz, line, raw, blame, sig_format = 1):
         self.clazz = clazz
         self.line = line
         self.raw = raw.strip(" {;")
         self.blame = blame
 
-        # drop generics for now; may need multiple passes
-        raw = re.sub("<[^<]+?>", "", raw)
-        raw = re.sub("<[^<]+?>", "", raw)
+        if sig_format == 2:
+            V2LineParser(raw).parse_into_method(self)
+        elif sig_format == 1:
+            # drop generics for now; may need multiple passes
+            raw = re.sub("<[^<]+?>", "", raw)
+            raw = re.sub("<[^<]+?>", "", raw)
 
-        # handle each clause differently
-        raw_prefix, raw_args, _, raw_throws = re.match(r"(.*?)\((.*?)\)( throws )?(.*?);$", raw).groups()
+            # handle each clause differently
+            raw_prefix, raw_args, _, raw_throws = re.match(r"(.*?)\((.*?)\)( throws )?(.*?);$", raw).groups()
 
-        # parse prefixes
-        raw = re.split("[\s]+", raw_prefix)
-        for r in ["", ";"]:
-            while r in raw: raw.remove(r)
-        self.split = list(raw)
+            # parse prefixes
+            raw = re.split("[\s]+", raw_prefix)
+            for r in ["", ";"]:
+                while r in raw: raw.remove(r)
+            self.split = list(raw)
 
-        for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default", "operator"]:
-            while r in raw: raw.remove(r)
+            for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default", "operator", "synchronized"]:
+                while r in raw: raw.remove(r)
 
-        self.typ = raw[0]
-        self.name = raw[1]
+            self.typ = raw[0]
+            self.name = raw[1]
 
-        # parse args
-        self.args = []
-        for arg in re.split(",\s*", raw_args):
-            arg = re.split("\s", arg)
-            # ignore annotations for now
-            arg = [ a for a in arg if not a.startswith("@") ]
-            if len(arg[0]) > 0:
-                self.args.append(arg[0])
+            # parse args
+            self.args = []
+            for arg in re.split(",\s*", raw_args):
+                arg = re.split("\s", arg)
+                # ignore annotations for now
+                arg = [ a for a in arg if not a.startswith("@") ]
+                if len(arg[0]) > 0:
+                    self.args.append(arg[0])
 
-        # parse throws
-        self.throws = []
-        for throw in re.split(",\s*", raw_throws):
-            self.throws.append(throw)
+            # parse throws
+            self.throws = []
+            for throw in re.split(",\s*", raw_throws):
+                self.throws.append(throw)
+        else:
+            raise ValueError("Unknown signature format: " + sig_format)
 
-        self.ident = ident(self.raw)
+        self.ident = "-".join((self.typ, self.name, "-".join(self.args)))
+
+    def sig_matches(self, typ, name, args):
+        return typ == self.typ and name == self.name and args == self.args
 
     def __hash__(self):
         return hash(self.raw)
@@ -147,7 +146,7 @@
 
 
 class Class():
-    def __init__(self, pkg, line, raw, blame):
+    def __init__(self, pkg, line, raw, blame, sig_format = 1):
         self.pkg = pkg
         self.line = line
         self.raw = raw.strip(" {;")
@@ -156,31 +155,44 @@
         self.fields = []
         self.methods = []
 
-        # drop generics for now; may need multiple passes
-        raw = re.sub("<[^<]+?>", "", raw)
-        raw = re.sub("<[^<]+?>", "", raw)
+        if sig_format == 2:
+            V2LineParser(raw).parse_into_class(self)
+        elif sig_format == 1:
+            # drop generics for now; may need multiple passes
+            raw = re.sub("<[^<]+?>", "", raw)
+            raw = re.sub("<[^<]+?>", "", raw)
 
-        raw = raw.split()
-        self.split = list(raw)
-        if "class" in raw:
-            self.fullname = raw[raw.index("class")+1]
-        elif "interface" in raw:
-            self.fullname = raw[raw.index("interface")+1]
-        elif "@interface" in raw:
-            self.fullname = raw[raw.index("@interface")+1]
-        else:
-            raise ValueError("Funky class type %s" % (self.raw))
+            raw = raw.split()
+            self.split = list(raw)
+            if "class" in raw:
+                self.fullname = raw[raw.index("class")+1]
+            elif "interface" in raw:
+                self.fullname = raw[raw.index("interface")+1]
+            elif "@interface" in raw:
+                self.fullname = raw[raw.index("@interface")+1]
+            else:
+                raise ValueError("Funky class type %s" % (self.raw))
 
-        if "extends" in raw:
-            self.extends = raw[raw.index("extends")+1]
-            self.extends_path = self.extends.split(".")
+            if "extends" in raw:
+                self.extends = raw[raw.index("extends")+1]
+            else:
+                self.extends = None
+
+            if "implements" in raw:
+                self.implements = raw[raw.index("implements")+1]
+            else:
+                self.implements = None
         else:
-            self.extends = None
-            self.extends_path = []
+            raise ValueError("Unknown signature format: " + sig_format)
 
         self.fullname = self.pkg.name + "." + self.fullname
         self.fullname_path = self.fullname.split(".")
 
+        if self.extends is not None:
+            self.extends_path = self.extends.split(".")
+        else:
+            self.extends_path = []
+
         self.name = self.fullname[self.fullname.rindex(".")+1:]
 
     def merge_from(self, other):
@@ -208,6 +220,318 @@
     def __repr__(self):
         return self.raw
 
+class V2Tokenizer(object):
+    __slots__ = ["raw"]
+
+    DELIMITER = re.compile(r'\s+|[()@<>;,={}/"!?]|\[\]|\.\.\.')
+    STRING_SPECIAL = re.compile(r'["\\]')
+
+    def __init__(self, raw):
+        self.raw = raw
+
+    def tokenize(self):
+        tokens = []
+        current = 0
+        raw = self.raw
+        length = len(raw)
+
+        while current < length:
+            while current < length:
+                start = current
+                match = V2Tokenizer.DELIMITER.search(raw, start)
+                if match is not None:
+                    match_start = match.start()
+                    if match_start == current:
+                        end = match.end()
+                    else:
+                        end = match_start
+                else:
+                    end = length
+
+                token = raw[start:end]
+                current = end
+
+                if token == "" or token[0] == " ":
+                    continue
+                else:
+                    break
+
+            if token == "@":
+                if raw[start:start+11] == "@interface ":
+                    current = start + 11
+                    tokens.append("@interface")
+                    continue
+            elif token == '/':
+                if raw[start:start+2] == "//":
+                    current = length
+                    continue
+            elif token == '"':
+                current, string_token = self.tokenize_string(raw, length, current)
+                tokens.append(token + string_token)
+                continue
+
+            tokens.append(token)
+
+        return tokens
+
+    def tokenize_string(self, raw, length, current):
+        start = current
+        end = length
+        while start < end:
+            match = V2Tokenizer.STRING_SPECIAL.search(raw, start)
+            if match:
+                if match.group() == '"':
+                    end = match.end()
+                    break
+                elif match.group() == '\\':
+                    # ignore whatever is after the slash
+                    start += 2
+                else:
+                    raise ValueError("Unexpected match: `%s`" % (match.group()))
+            else:
+                raise ValueError("Unexpected EOF tokenizing string: `%s`" % (raw[current - 1:],))
+
+        token = raw[current:end]
+        return end, token
+
+class V2LineParser(object):
+    __slots__ = ["tokenized", "current", "len"]
+
+    MODIFIERS = set("public protected internal private abstract default static final transient volatile synchronized".split())
+    JAVA_LANG_TYPES = set("AbstractMethodError AbstractStringBuilder Appendable ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException AssertionError AutoCloseable Boolean BootstrapMethodError Byte Character CharSequence Class ClassCastException ClassCircularityError ClassFormatError ClassLoader ClassNotFoundException Cloneable CloneNotSupportedException Comparable Compiler Deprecated Double Enum EnumConstantNotPresentException Error Exception ExceptionInInitializerError Float FunctionalInterface IllegalAccessError IllegalAccessException IllegalArgumentException IllegalMonitorStateException IllegalStateException IllegalThreadStateException IncompatibleClassChangeError IndexOutOfBoundsException InheritableThreadLocal InstantiationError InstantiationException Integer InternalError InterruptedException Iterable LinkageError Long Math NegativeArraySizeException NoClassDefFoundError NoSuchFieldError NoSuchFieldException NoSuchMethodError NoSuchMethodException NullPointerException Number NumberFormatException Object OutOfMemoryError Override Package package-info.java Process ProcessBuilder ProcessEnvironment ProcessImpl Readable ReflectiveOperationException Runnable Runtime RuntimeException RuntimePermission SafeVarargs SecurityException SecurityManager Short StackOverflowError StackTraceElement StrictMath String StringBuffer StringBuilder StringIndexOutOfBoundsException SuppressWarnings System Thread ThreadDeath ThreadGroup ThreadLocal Throwable TypeNotPresentException UNIXProcess UnknownError UnsatisfiedLinkError UnsupportedClassVersionError UnsupportedOperationException VerifyError VirtualMachineError Void".split())
+
+    def __init__(self, raw):
+        self.tokenized = V2Tokenizer(raw).tokenize()
+        self.current = 0
+        self.len = len(self.tokenized)
+
+    def parse_into_method(self, method):
+        method.split = []
+        kind = self.parse_one_of("ctor", "method")
+        method.split.append(kind)
+        annotations = self.parse_annotations()
+        method.split.extend(self.parse_modifiers())
+        self.parse_matching_paren("<", ">")
+        if "@Deprecated" in annotations:
+            method.split.append("deprecated")
+        if kind == "ctor":
+            method.typ = "ctor"
+        else:
+            method.typ = self.parse_type()
+            method.split.append(method.typ)
+        method.name = self.parse_name()
+        method.split.append(method.name)
+        self.parse_token("(")
+        method.args = self.parse_args()
+        self.parse_token(")")
+        method.throws = self.parse_throws()
+        if "@interface" in method.clazz.split:
+            self.parse_annotation_default()
+        self.parse_token(";")
+        self.parse_eof()
+
+    def parse_into_class(self, clazz):
+        clazz.split = []
+        annotations = self.parse_annotations()
+        if "@Deprecated" in annotations:
+            clazz.split.append("deprecated")
+        clazz.split.extend(self.parse_modifiers())
+        kind = self.parse_one_of("class", "interface", "@interface", "enum")
+        if kind == "enum":
+            # enums are implicitly final
+            clazz.split.append("final")
+        clazz.split.append(kind)
+        clazz.fullname = self.parse_name()
+        self.parse_matching_paren("<", ">")
+        extends = self.parse_extends()
+        clazz.extends = extends[0] if extends else None
+        implements = self.parse_implements()
+        clazz.implements = implements[0] if implements else None
+        # The checks assume that interfaces are always found in implements, which isn't true for
+        # subinterfaces.
+        if not implements and "interface" in clazz.split:
+            clazz.implements = clazz.extends
+        self.parse_token("{")
+        self.parse_eof()
+
+    def parse_into_field(self, field):
+        kind = self.parse_token("field")
+        field.split = [kind]
+        annotations = self.parse_annotations()
+        if "@Deprecated" in annotations:
+            field.split.append("deprecated")
+        field.split.extend(self.parse_modifiers())
+        field.typ = self.parse_type()
+        field.split.append(field.typ)
+        field.name = self.parse_name()
+        field.split.append(field.name)
+        if self.parse_if("="):
+            field.value = self.parse_value_stripped()
+        else:
+            field.value = None
+
+        self.parse_token(";")
+        self.parse_eof()
+
+    def lookahead(self):
+        return self.tokenized[self.current]
+
+    def parse_one_of(self, *options):
+        found = self.lookahead()
+        if found not in options:
+            raise ValueError("Parsing failed, expected one of `%s` but found `%s` in %s" % (options, found, repr(self.tokenized)))
+        return self.parse_token()
+
+    def parse_token(self, tok = None):
+        found = self.lookahead()
+        if tok is not None and found != tok:
+            raise ValueError("Parsing failed, expected `%s` but found `%s` in %s" % (tok, found, repr(self.tokenized)))
+        self.current += 1
+        return found
+
+    def eof(self):
+        return self.current == self.len
+
+    def parse_eof(self):
+        if not self.eof():
+            raise ValueError("Parsing failed, expected EOF, but %s has not been parsed in %s" % (self.tokenized[self.current:], self.tokenized))
+
+    def parse_if(self, tok):
+        if not self.eof() and self.lookahead() == tok:
+            self.parse_token()
+            return True
+        return False
+
+    def parse_annotations(self):
+        ret = []
+        while self.lookahead() == "@":
+            ret.append(self.parse_annotation())
+        return ret
+
+    def parse_annotation(self):
+        ret = self.parse_token("@") + self.parse_token()
+        self.parse_matching_paren("(", ")")
+        return ret
+
+    def parse_matching_paren(self, open, close):
+        start = self.current
+        if not self.parse_if(open):
+            return
+        length = len(self.tokenized)
+        count = 1
+        while count > 0:
+            if self.current == length:
+                raise ValueError("Unexpected EOF looking for closing paren: `%s`" % (self.tokenized[start:],))
+            t = self.parse_token()
+            if t == open:
+                count += 1
+            elif t == close:
+                count -= 1
+        return self.tokenized[start:self.current]
+
+    def parse_modifiers(self):
+        ret = []
+        while self.lookahead() in V2LineParser.MODIFIERS:
+            ret.append(self.parse_token())
+        return ret
+
+    def parse_kotlin_nullability(self):
+        t = self.lookahead()
+        if t == "?" or t == "!":
+            return self.parse_token()
+        return None
+
+    def parse_type(self):
+        type = self.parse_token()
+        if type in V2LineParser.JAVA_LANG_TYPES:
+            type = "java.lang." + type
+        self.parse_matching_paren("<", ">")
+        while True:
+            t = self.lookahead()
+            if t == "[]":
+                type += self.parse_token()
+            elif self.parse_kotlin_nullability() is not None:
+                pass  # discard nullability for now
+            else:
+                break
+        return type
+
+    def parse_arg_type(self):
+        type = self.parse_type()
+        if self.parse_if("..."):
+            type += "..."
+        self.parse_kotlin_nullability() # discard nullability for now
+        return type
+
+    def parse_name(self):
+        return self.parse_token()
+
+    def parse_args(self):
+        args = []
+        if self.lookahead() == ")":
+            return args
+
+        while True:
+            args.append(self.parse_arg())
+            if self.lookahead() == ")":
+                return args
+            self.parse_token(",")
+
+    def parse_arg(self):
+        self.parse_annotations()
+        type = self.parse_arg_type()
+        l = self.lookahead()
+        if l != "," and l != ")":
+            self.parse_token()  # kotlin argument name
+            if self.parse_if('='): # kotlin default value
+                (self.parse_matching_paren('(', ')') or
+                 self.parse_matching_paren('{', '}') or
+                 self.parse_token() and self.parse_matching_paren('(', ')'))
+        return type
+
+    def parse_throws(self):
+        ret = []
+        if self.parse_if("throws"):
+            ret.append(self.parse_type())
+            while self.parse_if(","):
+                ret.append(self.parse_type())
+        return ret
+
+    def parse_extends(self):
+        if self.parse_if("extends"):
+            return self.parse_space_delimited_type_list()
+        return []
+
+    def parse_implements(self):
+        if self.parse_if("implements"):
+            return self.parse_space_delimited_type_list()
+        return []
+
+    def parse_space_delimited_type_list(self, terminals = ["implements", "{"]):
+        types = []
+        while True:
+            types.append(self.parse_type())
+            if self.lookahead() in terminals:
+                return types
+
+    def parse_annotation_default(self):
+        if self.parse_if("default"):
+            self.parse_value()
+
+    def parse_value(self):
+        if self.lookahead() == "{":
+            return " ".join(self.parse_matching_paren("{", "}"))
+        elif self.lookahead() == "(":
+            return " ".join(self.parse_matching_paren("(", ")"))
+        else:
+            return self.parse_token()
+
+    def parse_value_stripped(self):
+        value = self.parse_value()
+        if value[0] == '"':
+            return value[1:-1]
+        return value
+
 
 def _parse_stream(f, clazz_cb=None, base_f=None, out_classes_with_base=None,
                   in_classes_with_base=[]):
@@ -252,6 +576,7 @@
     pkg = None
     clazz = None
     blame = None
+    sig_format = 1
 
     re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$")
     for raw in f:
@@ -264,16 +589,18 @@
         else:
             blame = None
 
-        if raw.startswith("package"):
+        if line == 1 and raw == "// Signature format: 2.0":
+            sig_format = 2
+        elif raw.startswith("package"):
             pkg = Package(line, raw, blame)
         elif raw.startswith("  ") and raw.endswith("{"):
-            clazz = Class(pkg, line, raw, blame)
+            clazz = Class(pkg, line, raw, blame, sig_format=sig_format)
         elif raw.startswith("    ctor"):
-            clazz.ctors.append(Method(clazz, line, raw, blame))
+            clazz.ctors.append(Method(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("    method"):
-            clazz.methods.append(Method(clazz, line, raw, blame))
+            clazz.methods.append(Method(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("    field"):
-            clazz.fields.append(Field(clazz, line, raw, blame))
+            clazz.fields.append(Field(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("  }") and clazz:
             yield clazz
 
@@ -367,7 +694,7 @@
     """Records an API failure to be processed later."""
     global failures
 
-    sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg)
+    sig = "%s-%s-%s" % (clazz.fullname, detail.ident if detail else None, msg)
     sig = sig.replace(" deprecated ", " ")
 
     failures[sig] = Failure(sig, clazz, detail, error, rule, msg)
@@ -408,7 +735,7 @@
 
 def verify_enums(clazz):
     """Enums are bad, mmkay?"""
-    if "extends java.lang.Enum" in clazz.raw:
+    if clazz.extends == "java.lang.Enum" or "enum" in clazz.split:
         error(clazz, None, "F5", "Enums are not allowed")
 
 
@@ -467,7 +794,7 @@
         interface OnFooListener { void onFoo() }"""
 
     if clazz.name.endswith("Listener"):
-        if " abstract class " in clazz.raw:
+        if "abstract" in clazz.split and "class" in clazz.split:
             error(clazz, None, "L1", "Listeners should be an interface, or otherwise renamed Callback")
 
         for m in clazz.methods:
@@ -546,16 +873,16 @@
     eq = False
     hc = False
     for m in clazz.methods:
-        if " static " in m.raw: continue
-        if "boolean equals(java.lang.Object)" in m.raw: eq = True
-        if "int hashCode()" in m.raw: hc = True
+        if "static" in m.split: continue
+        if m.sig_matches("boolean", "equals", ["java.lang.Object"]): eq = True
+        if m.sig_matches("int", "hashCode", []): hc = True
     if eq != hc:
         error(clazz, None, "M8", "Must override both equals and hashCode; missing one")
 
 
 def verify_parcelable(clazz):
     """Verify that Parcelable objects aren't hiding required bits."""
-    if "implements android.os.Parcelable" in clazz.raw:
+    if clazz.implements == "android.os.Parcelable":
         creator = [ i for i in clazz.fields if i.name == "CREATOR" ]
         write = [ i for i in clazz.methods if i.name == "writeToParcel" ]
         describe = [ i for i in clazz.methods if i.name == "describeContents" ]
@@ -563,8 +890,7 @@
         if len(creator) == 0 or len(write) == 0 or len(describe) == 0:
             error(clazz, None, "FW3", "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one")
 
-        if ((" final class " not in clazz.raw) and
-            (" final deprecated class " not in clazz.raw)):
+        if "final" not in clazz.split:
             error(clazz, None, "FW8", "Parcelable classes must be final")
 
         for c in clazz.ctors:
@@ -684,7 +1010,7 @@
     """Verify that helper classes are named consistently with what they extend.
     All developer extendable methods should be named onFoo()."""
     test_methods = False
-    if "extends android.app.Service" in clazz.raw:
+    if clazz.extends == "android.app.Service":
         test_methods = True
         if not clazz.name.endswith("Service"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooService")
@@ -696,7 +1022,7 @@
                 if f.value != clazz.fullname:
                     error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname))
 
-    if "extends android.content.ContentProvider" in clazz.raw:
+    if clazz.extends == "android.content.ContentProvider":
         test_methods = True
         if not clazz.name.endswith("Provider"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooProvider")
@@ -708,12 +1034,12 @@
                 if f.value != clazz.fullname:
                     error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname))
 
-    if "extends android.content.BroadcastReceiver" in clazz.raw:
+    if clazz.extends == "android.content.BroadcastReceiver":
         test_methods = True
         if not clazz.name.endswith("Receiver"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooReceiver")
 
-    if "extends android.app.Activity" in clazz.raw:
+    if clazz.extends == "android.app.Activity":
         test_methods = True
         if not clazz.name.endswith("Activity"):
             error(clazz, None, "CL4", "Inconsistent class name; should be FooActivity")
@@ -731,7 +1057,7 @@
 def verify_builder(clazz):
     """Verify builder classes.
     Methods should return the builder to enable chaining."""
-    if " extends " in clazz.raw: return
+    if clazz.extends: return
     if not clazz.name.endswith("Builder"): return
 
     if clazz.name != "Builder":
@@ -759,7 +1085,7 @@
 
 def verify_aidl(clazz):
     """Catch people exposing raw AIDL."""
-    if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw:
+    if clazz.extends == "android.os.Binder" or clazz.implements == "android.os.IInterface":
         error(clazz, None, None, "Raw AIDL interfaces must not be exposed")
 
 
@@ -768,48 +1094,66 @@
     if clazz.pkg.name.startswith("com.android"):
         error(clazz, None, None, "Internal classes must not be exposed")
 
+def layering_build_ranking(ranking_list):
+    r = {}
+    for rank, ps in enumerate(ranking_list):
+        if not isinstance(ps, list):
+            ps = [ps]
+        for p in ps:
+            rs = r
+            for n in p.split('.'):
+                if n not in rs:
+                    rs[n] = {}
+                rs = rs[n]
+            rs['-rank'] = rank
+    return r
+
+LAYERING_PACKAGE_RANKING = layering_build_ranking([
+    ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
+    "android.app",
+    "android.widget",
+    "android.view",
+    "android.animation",
+    "android.provider",
+    ["android.content","android.graphics.drawable"],
+    "android.database",
+    "android.text",
+    "android.graphics",
+    "android.os",
+    "android.util"
+])
 
 def verify_layering(clazz):
     """Catch package layering violations.
     For example, something in android.os depending on android.app."""
-    ranking = [
-        ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"],
-        "android.app",
-        "android.widget",
-        "android.view",
-        "android.animation",
-        "android.provider",
-        ["android.content","android.graphics.drawable"],
-        "android.database",
-        "android.text",
-        "android.graphics",
-        "android.os",
-        "android.util"
-    ]
 
     def rank(p):
-        for i in range(len(ranking)):
-            if isinstance(ranking[i], list):
-                for j in ranking[i]:
-                    if p.startswith(j): return i
+        r = None
+        l = LAYERING_PACKAGE_RANKING
+        for n in p.split('.'):
+            if n in l:
+                l = l[n]
+                if '-rank' in l:
+                    r = l['-rank']
             else:
-                if p.startswith(ranking[i]): return i
+                break
+        return r
 
     cr = rank(clazz.pkg.name)
     if cr is None: return
 
     for f in clazz.fields:
         ir = rank(f.typ)
-        if ir and ir < cr:
+        if ir is not None and ir < cr:
             warn(clazz, f, "FW6", "Field type violates package layering")
 
-    for m in clazz.methods:
+    for m in itertools.chain(clazz.methods, clazz.ctors):
         ir = rank(m.typ)
-        if ir and ir < cr:
+        if ir is not None and ir < cr:
             warn(clazz, m, "FW6", "Method return type violates package layering")
         for arg in m.args:
             ir = rank(arg)
-            if ir and ir < cr:
+            if ir is not None and ir < cr:
                 warn(clazz, m, "FW6", "Method argument type violates package layering")
 
 
@@ -900,21 +1244,18 @@
             if len(m.args) == 0 and t in ["java.lang.IllegalArgumentException", "java.lang.NullPointerException"]:
                 warn(clazz, m, "S1", "Methods taking no arguments should throw IllegalStateException")
 
+GOOGLE_IGNORECASE = re.compile("google", re.IGNORECASE)
 
 def verify_google(clazz):
     """Verifies that APIs never reference Google."""
 
-    if re.search("google", clazz.raw, re.IGNORECASE):
+    if GOOGLE_IGNORECASE.search(clazz.raw) is not None:
         error(clazz, None, None, "Must never reference Google")
 
-    test = []
-    test.extend(clazz.ctors)
-    test.extend(clazz.fields)
-    test.extend(clazz.methods)
-
-    for t in test:
-        if re.search("google", t.raw, re.IGNORECASE):
-            error(clazz, t, None, "Must never reference Google")
+    for test in clazz.ctors, clazz.fields, clazz.methods:
+        for t in test:
+            if GOOGLE_IGNORECASE.search(t.raw) is not None:
+                error(clazz, t, None, "Must never reference Google")
 
 
 def verify_bitset(clazz):
@@ -1168,7 +1509,7 @@
     """Verifies that abstract inner classes are static."""
 
     if re.match(".+?\.[A-Z][^\.]+\.[A-Z]", clazz.fullname):
-        if " abstract " in clazz.raw and " static " not in clazz.raw:
+        if "abstract" in clazz.split and "static" not in clazz.split:
             warn(clazz, None, None, "Abstract inner classes should be static to improve testability")
 
 
@@ -1263,8 +1604,8 @@
 
 def verify_closable(clazz):
     """Verifies that classes are AutoClosable."""
-    if "implements java.lang.AutoCloseable" in clazz.raw: return
-    if "implements java.io.Closeable" in clazz.raw: return
+    if clazz.implements == "java.lang.AutoCloseable": return
+    if clazz.implements == "java.io.Closeable": return
 
     for m in clazz.methods:
         if len(m.args) > 0: continue
@@ -1350,6 +1691,9 @@
 def verify_collections_over_arrays(clazz):
     """Warn that [] should be Collections."""
 
+    if "@interface" in clazz.split:
+        return
+
     safe = ["java.lang.String[]","byte[]","short[]","int[]","long[]","float[]","double[]","boolean[]","char[]"]
     for m in clazz.methods:
         if m.typ.endswith("[]") and m.typ not in safe:
@@ -1683,11 +2027,11 @@
             del cur[prev_clazz.fullname]
 
     for clazz in cur.values():
-        if " deprecated " in clazz.raw and not clazz.fullname in prev:
+        if "deprecated" in clazz.split and not clazz.fullname in prev:
             error(clazz, None, None, "Found API deprecation at birth")
 
         for i in clazz.ctors + clazz.methods + clazz.fields:
-            if " deprecated " in i.raw:
+            if "deprecated" in i.split:
                 error(clazz, i, None, "Found API deprecation at birth")
 
     print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py
index ece69a9..081e98d 100644
--- a/tools/apilint/apilint_test.py
+++ b/tools/apilint/apilint_test.py
@@ -143,5 +143,148 @@
                                       out_classes_with_base=classes_with_base)
         self.assertEquals(map(lambda x: x.fullname, classes_with_base), ["android.app.WallpaperColors"])
 
+class V2TokenizerTests(unittest.TestCase):
+    def _test(self, raw, expected):
+        self.assertEquals(apilint.V2Tokenizer(raw).tokenize(), expected)
+
+    def test_simple(self):
+        self._test("  method public some.Type someName(some.Argument arg, int arg);",
+                   ['method', 'public', 'some.Type', 'someName', '(', 'some.Argument',
+                    'arg', ',', 'int', 'arg', ')', ';'])
+        self._test("class Some.Class extends SomeOther {",
+                   ['class', 'Some.Class', 'extends', 'SomeOther', '{'])
+
+    def test_varargs(self):
+        self._test("name(String...)",
+                   ['name', '(', 'String', '...', ')'])
+
+    def test_kotlin(self):
+        self._test("String? name(String!...)",
+                   ['String', '?', 'name', '(', 'String', '!',  '...', ')'])
+
+    def test_annotation(self):
+        self._test("method @Nullable public void name();",
+                   ['method', '@', 'Nullable', 'public', 'void', 'name', '(', ')', ';'])
+
+    def test_annotation_args(self):
+        self._test("@Some(val=1, other=2) class Class {",
+                   ['@', 'Some', '(', 'val', '=', '1', ',', 'other', '=', '2', ')',
+                    'class', 'Class', '{'])
+    def test_comment(self):
+        self._test("some //comment", ['some'])
+
+    def test_strings(self):
+        self._test(r'"" "foo" "\"" "\\"', ['""', '"foo"', r'"\""', r'"\\"'])
+
+    def test_at_interface(self):
+        self._test("public @interface Annotation {",
+                   ['public', '@interface', 'Annotation', '{'])
+
+    def test_array_type(self):
+        self._test("int[][]", ['int', '[]', '[]'])
+
+    def test_generics(self):
+        self._test("<>foobar<A extends Object>",
+                   ['<', '>', 'foobar', '<', 'A', 'extends', 'Object', '>'])
+
+class V2ParserTests(unittest.TestCase):
+    def _cls(self, raw):
+        pkg = apilint.Package(999, "package pkg {", None)
+        return apilint.Class(pkg, 1, raw, '', sig_format=2)
+
+    def _method(self, raw, cls=None):
+        if not cls:
+            cls = self._cls("class Class {")
+        return apilint.Method(cls, 1, raw, '', sig_format=2)
+
+    def _field(self, raw):
+        cls = self._cls("class Class {")
+        return apilint.Field(cls, 1, raw, '', sig_format=2)
+
+    def test_class(self):
+        cls = self._cls("@Deprecated @IntRange(from=1, to=2) public static abstract class Some.Name extends Super<Class> implements Interface<Class> {")
+        self.assertTrue('deprecated' in cls.split)
+        self.assertTrue('static' in cls.split)
+        self.assertTrue('abstract' in cls.split)
+        self.assertTrue('class' in cls.split)
+        self.assertEquals('Super', cls.extends)
+        self.assertEquals('Interface', cls.implements)
+        self.assertEquals('pkg.Some.Name', cls.fullname)
+
+    def test_interface(self):
+        cls = self._cls("@Deprecated @IntRange(from=1, to=2) public interface Some.Name extends Interface<Class> {")
+        self.assertTrue('deprecated' in cls.split)
+        self.assertTrue('interface' in cls.split)
+        self.assertEquals('Interface', cls.extends)
+        self.assertEquals('Interface', cls.implements)
+        self.assertEquals('pkg.Some.Name', cls.fullname)
+
+    def test_at_interface(self):
+        cls = self._cls("@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface SuppressLint {")
+        self.assertTrue('@interface' in cls.split)
+        self.assertEquals('pkg.SuppressLint', cls.fullname)
+
+    def test_parse_method(self):
+        m = self._method("method @Deprecated public static <T> Class<T>[][] name("
+                         + "Class<T[]>[][], Class<T[][][]>[][]...) throws Exception, T;")
+        self.assertTrue('static' in m.split)
+        self.assertTrue('public' in m.split)
+        self.assertTrue('method' in m.split)
+        self.assertTrue('deprecated' in m.split)
+        self.assertEquals('java.lang.Class[][]', m.typ)
+        self.assertEquals('name', m.name)
+        self.assertEquals(['java.lang.Class[][]', 'java.lang.Class[][]...'], m.args)
+        self.assertEquals(['java.lang.Exception', 'T'], m.throws)
+
+    def test_ctor(self):
+        m = self._method("ctor @Deprecated <T> ClassName();")
+        self.assertTrue('ctor' in m.split)
+        self.assertTrue('deprecated' in m.split)
+        self.assertEquals('ctor', m.typ)
+        self.assertEquals('ClassName', m.name)
+
+    def test_parse_annotation_method(self):
+        cls = self._cls("@interface Annotation {")
+        self._method('method abstract String category() default "";', cls=cls)
+        self._method('method abstract boolean deepExport() default false;', cls=cls)
+        self._method('method abstract ViewDebug.FlagToString[] flagMapping() default {};', cls=cls)
+
+    def test_parse_string_field(self):
+        f = self._field('field @Deprecated public final String SOME_NAME = "value";')
+        self.assertTrue('field' in f.split)
+        self.assertTrue('deprecated' in f.split)
+        self.assertTrue('final' in f.split)
+        self.assertEquals('java.lang.String', f.typ)
+        self.assertEquals('SOME_NAME', f.name)
+        self.assertEquals('value', f.value)
+
+    def test_parse_field(self):
+        f = self._field('field public Object SOME_NAME;')
+        self.assertTrue('field' in f.split)
+        self.assertEquals('java.lang.Object', f.typ)
+        self.assertEquals('SOME_NAME', f.name)
+        self.assertEquals(None, f.value)
+
+    def test_parse_int_field(self):
+        f = self._field('field public int NAME = 123;')
+        self.assertTrue('field' in f.split)
+        self.assertEquals('int', f.typ)
+        self.assertEquals('NAME', f.name)
+        self.assertEquals('123', f.value)
+
+    def test_parse_quotient_field(self):
+        f = self._field('field public int NAME = (0.0/0.0);')
+        self.assertTrue('field' in f.split)
+        self.assertEquals('int', f.typ)
+        self.assertEquals('NAME', f.name)
+        self.assertEquals('( 0.0 / 0.0 )', f.value)
+
+    def test_kotlin_types(self):
+        self._field('field public List<Integer[]?[]!>?[]![]? NAME;')
+        self._method("method <T?> Class<T!>?[]![][]? name(Type!, Type argname,"
+                         + "Class<T?>[][]?[]!...!) throws Exception, T;")
+        self._method("method <T> T name(T a = 1, T b = A(1), Lambda f = { false }, N? n = null, "
+                         + """double c = (1/0), float d = 1.0f, String s = "heyo", char c = 'a');""")
+
 if __name__ == "__main__":
-    unittest.main()
\ No newline at end of file
+    unittest.main()
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1700006..fef0ed7 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -16,7 +16,6 @@
 
 package android.net.wifi;
 
-
 import android.content.pm.ParceledListSlice;
 
 import android.net.wifi.hotspot2.OsuProvider;
@@ -63,9 +62,9 @@
 
     ParceledListSlice getPrivilegedConfiguredNetworks();
 
-    List<WifiConfiguration> getAllMatchingWifiConfigs(in List<ScanResult> scanResult);
+    Map getAllMatchingFqdnsForScanResults(in List<ScanResult> scanResult);
 
-    List<OsuProvider> getMatchingOsuProviders(in List<ScanResult> scanResult);
+    Map getMatchingOsuProviders(in List<ScanResult> scanResult);
 
     Map getMatchingPasspointConfigsForOsuProviders(in List<OsuProvider> osuProviders);
 
@@ -77,6 +76,8 @@
 
     List<PasspointConfiguration> getPasspointConfigurations();
 
+    List<WifiConfiguration> getWifiConfigsForPasspointProfiles(in List<String> fqdnList);
+
     void queryPasspointIcon(long bssid, String fileName);
 
     int matchProviderWithCurrentNetwork(String fqdn);
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 7cdd16a..af5ad51 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -114,6 +114,11 @@
     private boolean mTrusted;
 
     /**
+     * OSU (Online Sign Up) AP for Passpoint R2.
+     */
+    private boolean mOsuAp;
+
+    /**
      * Running total count of lost (not ACKed) transmitted unicast data packets.
      * @hide
      */
@@ -190,6 +195,7 @@
         setFrequency(-1);
         setMeteredHint(false);
         setEphemeral(false);
+        setOsuAp(false);
         txBad = 0;
         txSuccess = 0;
         rxSuccess = 0;
@@ -219,6 +225,7 @@
             mMeteredHint = source.mMeteredHint;
             mEphemeral = source.mEphemeral;
             mTrusted = source.mTrusted;
+            mOsuAp = source.mOsuAp;
             txBad = source.txBad;
             txRetries = source.txRetries;
             txSuccess = source.txSuccess;
@@ -411,6 +418,15 @@
         return mTrusted;
     }
 
+    /** {@hide} */
+    public void setOsuAp(boolean osuAp) {
+        mOsuAp = osuAp;
+    }
+
+    /** {@hide} */
+    public boolean isOsuAp() {
+        return mOsuAp;
+    }
 
     /** @hide */
     @UnsupportedAppUsage
@@ -565,6 +581,7 @@
         dest.writeLong(rxSuccess);
         dest.writeDouble(rxSuccessRate);
         mSupplicantState.writeToParcel(dest, flags);
+        dest.writeInt(mOsuAp ? 1 : 0);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -600,6 +617,7 @@
                 info.rxSuccess = in.readLong();
                 info.rxSuccessRate = in.readDouble();
                 info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
+                info.mOsuAp = in.readInt() != 0;
                 return info;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index e67e8ea..ad2ed81 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -44,6 +44,7 @@
 import android.os.RemoteException;
 import android.os.WorkSource;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -1191,25 +1192,39 @@
     }
 
     /**
-     * Returns all matching WifiConfigurations for a given list of ScanResult.
+     * Returns a list of all matching WifiConfigurations for a given list of ScanResult.
      *
      * An empty list will be returned when no configurations are installed or if no configurations
      * match the ScanResult.
-
+     *
      * @param scanResults a list of scanResult that represents the BSSID
-     * @return A list of {@link WifiConfiguration} that can have duplicate entries.
+     * @return List that consists of {@link WifiConfiguration} and corresponding scanResults.
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public List<WifiConfiguration> getAllMatchingWifiConfigs(
+    public List<Pair<WifiConfiguration, List<ScanResult>>> getAllMatchingWifiConfigs(
             @NonNull List<ScanResult> scanResults) {
+        List<Pair<WifiConfiguration, List<ScanResult>>> configs = new ArrayList<>();
         try {
-            return mService.getAllMatchingWifiConfigs(scanResults);
+            Map<String, List<ScanResult>> results = mService.getAllMatchingFqdnsForScanResults(
+                    scanResults);
+            if (results.isEmpty()) {
+                return configs;
+            }
+            List<WifiConfiguration> wifiConfigurations =
+                    mService.getWifiConfigsForPasspointProfiles(new ArrayList<>(results.keySet()));
+            for (WifiConfiguration configuration : wifiConfigurations) {
+                List<ScanResult> scanResultList = results.get(configuration.FQDN);
+                if (scanResultList != null) {
+                    configs.add(Pair.create(configuration, scanResultList));
+                }
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+
+        return configs;
     }
 
     /**
@@ -1219,12 +1234,13 @@
      * An empty list will be returned if no match is found.
      *
      * @param scanResults a list of ScanResult
-     * @return A list of {@link OsuProvider} that does not contain duplicate entries.
+     * @return Map that consists {@link OsuProvider} and a list of matching {@link ScanResult}
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
+    public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
+            List<ScanResult> scanResults) {
         try {
             return mService.getMatchingOsuProviders(scanResults);
         } catch (RemoteException e) {
diff --git a/wifi/java/android/net/wifi/aware/PeerHandle.java b/wifi/java/android/net/wifi/aware/PeerHandle.java
index 8ae4b5a..1603d00 100644
--- a/wifi/java/android/net/wifi/aware/PeerHandle.java
+++ b/wifi/java/android/net/wifi/aware/PeerHandle.java
@@ -16,6 +16,9 @@
 
 package android.net.wifi.aware;
 
+import android.os.Parcel;
+import android.os.Parcelable;
+
 /**
  * Opaque object used to represent a Wi-Fi Aware peer. Obtained from discovery sessions in
  * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)} or
@@ -33,7 +36,7 @@
  * {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])}, or match filter,
  * {@link PublishConfig.Builder#setMatchFilter(java.util.List)}.
  */
-public class PeerHandle {
+public final class PeerHandle implements Parcelable {
     /** @hide */
     public PeerHandle(int peerId) {
         this.peerId = peerId;
@@ -59,4 +62,29 @@
     public int hashCode() {
         return peerId;
     }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(peerId);
+    }
+
+    public static final Creator<PeerHandle> CREATOR = new Creator<PeerHandle>() {
+        @Override
+        public PeerHandle[] newArray(int size) {
+            return new PeerHandle[size];
+        }
+
+        @Override
+        public PeerHandle createFromParcel(Parcel in) {
+            int peerHandle = in.readInt();
+
+            return new PeerHandle(peerHandle);
+        }
+    };
+
 }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index 01feb1e..72e57a1 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -17,15 +17,15 @@
 package android.net.wifi.p2p;
 
 import android.annotation.UnsupportedAppUsage;
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.regex.Pattern;
+import java.util.List;
 import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * A class representing a Wi-Fi P2p group. A p2p group consists of a single group
@@ -67,6 +67,9 @@
     /** The network id in the wpa_supplicant */
     private int mNetId;
 
+    /** The frequency used by this group */
+    private int mFrequency;
+
     /** P2P group started string pattern */
     private static final Pattern groupStartedPattern = Pattern.compile(
         "ssid=\"(.+)\" " +
@@ -116,8 +119,9 @@
             }
 
             mNetworkName = match.group(1);
-            //freq and psk are unused right now
-            //int freq = Integer.parseInt(match.group(2));
+            // It throws NumberFormatException if the string cannot be parsed as an integer.
+            mFrequency = Integer.parseInt(match.group(2));
+            // psk is unused right now
             //String psk = match.group(3);
             mPassphrase = match.group(4);
             mOwner = new WifiP2pDevice(match.group(5));
@@ -269,6 +273,16 @@
         this.mNetId = netId;
     }
 
+    /** Get the operating frequency of the p2p group */
+    public int getFrequency() {
+        return mFrequency;
+    }
+
+    /** @hide */
+    public void setFrequency(int freq) {
+        this.mFrequency = freq;
+    }
+
     public String toString() {
         StringBuffer sbuf = new StringBuffer();
         sbuf.append("network: ").append(mNetworkName);
@@ -279,6 +293,7 @@
         }
         sbuf.append("\n interface: ").append(mInterface);
         sbuf.append("\n networkId: ").append(mNetId);
+        sbuf.append("\n frequency: ").append(mFrequency);
         return sbuf.toString();
     }
 
@@ -297,6 +312,7 @@
             mPassphrase = source.getPassphrase();
             mInterface = source.getInterface();
             mNetId = source.getNetworkId();
+            mFrequency = source.getFrequency();
         }
     }
 
@@ -312,6 +328,7 @@
         dest.writeString(mPassphrase);
         dest.writeString(mInterface);
         dest.writeInt(mNetId);
+        dest.writeInt(mFrequency);
     }
 
     /** Implement the Parcelable interface */
@@ -329,6 +346,7 @@
                 group.setPassphrase(in.readString());
                 group.setInterface(in.readString());
                 group.setNetworkId(in.readInt());
+                group.setFrequency(in.readInt());
                 return group;
             }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 068b959..1bed914 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -1281,7 +1281,16 @@
         c.mAsyncChannel.sendMessage(REMOVE_GROUP, 0, c.putListener(listener));
     }
 
-    /** @hide */
+    /**
+     * Force p2p to enter or exit listen state
+     *
+     * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}
+     * @param enable enables or disables listening
+     * @param listener for callbacks on success or failure. Can be null.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void listen(Channel c, boolean enable, ActionListener listener) {
         checkChannel(c);
         c.mAsyncChannel.sendMessage(enable ? START_LISTEN : STOP_LISTEN,
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index e94b9e6..c505a58 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -21,6 +21,7 @@
 import android.content.pm.ParceledListSlice;
 import android.net.DhcpInfo;
 import android.net.Network;
+import android.net.wifi.IDppCallback;
 import android.net.wifi.INetworkRequestMatchCallback;
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.ITrafficStateCallback;
@@ -35,6 +36,7 @@
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.IBinder;
 import android.os.Messenger;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.WorkSource;
 
@@ -106,7 +108,8 @@
     }
 
     @Override
-    public List<WifiConfiguration> getAllMatchingWifiConfigs(List<ScanResult> scanResults) {
+    public Map<String, List<ScanResult>> getAllMatchingFqdnsForScanResults(
+            List<ScanResult> scanResults) {
         throw new UnsupportedOperationException();
     }
 
@@ -123,7 +126,8 @@
     }
 
     @Override
-    public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
+    public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
+            List<ScanResult> scanResults) {
         throw new UnsupportedOperationException();
     }
 
@@ -155,6 +159,11 @@
     }
 
     @Override
+    public List<WifiConfiguration> getWifiConfigsForPasspointProfiles(List<String> fqdnList) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void queryPasspointIcon(long bssid, String fileName) {
         throw new UnsupportedOperationException();
     }
@@ -464,4 +473,21 @@
     public String[] getFactoryMacAddresses() {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public void startDppAsConfiguratorInitiator(IBinder binder, String enrolleeUri,
+            int selectedNetworkId, int netRole, IDppCallback callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void startDppAsEnrolleeInitiator(IBinder binder, String configuratorUri,
+            IDppCallback callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void stopDppSession() throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index fb0af5f..677bf37 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -47,6 +47,7 @@
         writeWifiInfo.txBad = TEST_TX_BAD;
         writeWifiInfo.rxSuccess = TEST_RX_SUCCESS;
         writeWifiInfo.setTrusted(true);
+        writeWifiInfo.setOsuAp(true);
 
         Parcel parcel = Parcel.obtain();
         writeWifiInfo.writeToParcel(parcel, 0);
@@ -60,5 +61,6 @@
         assertEquals(TEST_TX_BAD, readWifiInfo.txBad);
         assertEquals(TEST_RX_SUCCESS, readWifiInfo.rxSuccess);
         assertTrue(readWifiInfo.isTrusted());
+        assertTrue(readWifiInfo.isOsuAp());
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index c43948b..4fbef5a 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -77,7 +77,9 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Unit tests for {@link android.net.wifi.WifiManager}.
@@ -1274,14 +1276,20 @@
     }
 
     /**
-     * Check the call to getAllMatchingWifiConfigs calls getAllMatchingWifiConfigs of WifiService
-     * with the provided a list of ScanResult.
+     * Check the call to getAllMatchingWifiConfigs calls getAllMatchingFqdnsForScanResults and
+     * getWifiConfigsForPasspointProfiles of WifiService in order.
      */
     @Test
     public void testGetAllMatchingWifiConfigs() throws Exception {
+        Map<String, List<ScanResult>> fqdns = new HashMap<>();
+        fqdns.put("www.test.com", new ArrayList<>());
+        when(mWifiService.getAllMatchingFqdnsForScanResults(any(List.class))).thenReturn(fqdns);
+        InOrder inOrder = inOrder(mWifiService);
+
         mWifiManager.getAllMatchingWifiConfigs(new ArrayList<>());
 
-        verify(mWifiService).getAllMatchingWifiConfigs(any(List.class));
+        inOrder.verify(mWifiService).getAllMatchingFqdnsForScanResults(any(List.class));
+        inOrder.verify(mWifiService).getWifiConfigsForPasspointProfiles(any(List.class));
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index e882b6b..ed38c76 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -1382,4 +1382,24 @@
         assertEquals(cap.getPeerIpv6Addr().toString(), "/fe80::1322:33ff:fe44:5566%5");
         assertEquals(cap.hashCode(), rereadCap.hashCode());
     }
+
+    // PeerHandle tests
+
+    @Test
+    public void testPeerHandleParcel() {
+        final PeerHandle peerHandle = new PeerHandle(5);
+
+        Parcel parcelW = Parcel.obtain();
+        peerHandle.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        PeerHandle rereadPeerHandle = PeerHandle.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(peerHandle, rereadPeerHandle);
+        assertEquals(peerHandle.hashCode(), rereadPeerHandle.hashCode());
+    }
 }