Merge "Cleanup ITelephony in TelephonyPermissions with new System API. This method is used by Telephony Module for permission checking."
diff --git a/Android.bp b/Android.bp
index e50161c..04b4e6e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -205,10 +205,23 @@
         "wifi/java/**/*.java",
         "wifi/java/**/*.aidl",
     ],
+    exclude_srcs: [
+        ":framework-wifi-non-updatable-sources"
+    ],
     path: "wifi/java",
 }
 
 filegroup {
+    name: "framework-wifi-non-updatable-sources",
+    srcs: [
+        // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
+        // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
+        // to a separate package.
+        "wifi/java/android/net/wifi/WifiNetworkScoreCache.java"
+    ],
+}
+
+filegroup {
     name: "framework-non-updatable-sources",
     srcs: [
         // Java/AIDL sources under frameworks/base
@@ -233,6 +246,7 @@
         ":framework-telephony-common-sources",
         ":framework-telephony-sources",
         ":framework-wifi-sources",
+        ":framework-wifi-non-updatable-sources",
         ":PacProcessor-aidl-sources",
         ":ProxyHandler-aidl-sources",
 
@@ -379,7 +393,9 @@
 
     sdk_version: "core_platform",
     libs: [
+        "app-compat-annotations",
         "ext",
+        "unsupportedappusage",
         "updatable_media_stubs",
     ],
 
@@ -427,7 +443,6 @@
     name: "framework-minus-apex",
     defaults: ["framework-defaults"],
     srcs: [":framework-non-updatable-sources"],
-    libs: ["app-compat-annotations"],
     installable: true,
     javac_shard_size: 150,
     required: [
@@ -445,6 +460,7 @@
     ],
     // For backwards compatibility.
     stem: "framework",
+    apex_available: ["//apex_available:platform"],
 }
 
 // This "framework" module is NOT installed to the device. It's
@@ -465,6 +481,7 @@
         // TODO(jiyong): add stubs for APEXes here
     ],
     sdk_version: "core_platform",
+    apex_available: ["//apex_available:platform"],
 }
 
 java_library {
@@ -472,15 +489,18 @@
     defaults: ["framework-defaults"],
     srcs: [":framework-all-sources"],
     installable: false,
-    libs: ["app-compat-annotations"],
-    static_libs: ["exoplayer2-core"]
+    static_libs: ["exoplayer2-core"],
+    apex_available: ["//apex_available:platform"],
 }
 
 java_library {
     name: "framework-annotation-proc",
     defaults: ["framework-aidl-export-defaults"],
     srcs: [":framework-all-sources"],
-    libs: ["app-compat-annotations"],
+    libs: [
+        "app-compat-annotations",
+        "unsupportedappusage",
+    ],
     installable: false,
     plugins: [
         "unsupportedappusage-annotation-processor",
@@ -1684,7 +1704,8 @@
     name: "framework-wifi-service-shared-srcs",
     srcs: [
         ":framework-annotations",
-	"core/java/android/os/HandlerExecutor.java",
+        "core/java/android/net/InterfaceConfiguration.java",
+        "core/java/android/os/HandlerExecutor.java",
         "core/java/android/util/BackupUtils.java",
         "core/java/android/util/LocalLog.java",
         "core/java/android/util/Rational.java",
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 525fbae..435384d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -372,9 +372,10 @@
                     continue;
                 }
 
-                // TODO lastEvaluatedPriority should be evaluateJobPriorityLocked. (double check it)
-                if (minPriorityForPreemption > nextPending.lastEvaluatedPriority) {
-                    minPriorityForPreemption = nextPending.lastEvaluatedPriority;
+                if (minPriorityForPreemption > jobPriority) {
+                    // Step down the preemption threshold - wind up replacing
+                    // the lowest-priority running job
+                    minPriorityForPreemption = jobPriority;
                     selectedContextId = j;
                     // In this case, we're just going to preempt a low priority job, we're not
                     // actually starting a job, so don't set startingJob.
diff --git a/apex/sdkext/derive_sdk/derive_sdk.cpp b/apex/sdkext/derive_sdk/derive_sdk.cpp
index 0aacebe..7536def 100644
--- a/apex/sdkext/derive_sdk/derive_sdk.cpp
+++ b/apex/sdkext/derive_sdk/derive_sdk.cpp
@@ -68,7 +68,7 @@
     auto itr = std::min_element(versions.begin(), versions.end());
     std::string prop_value = itr == versions.end() ? "0" : std::to_string(*itr);
 
-    if (!android::base::SetProperty("persist.com.android.sdkext.sdk_info", prop_value)) {
+    if (!android::base::SetProperty("ro.build.version.extensions.r", prop_value)) {
         LOG(ERROR) << "failed to set sdk_info prop";
         return EXIT_FAILURE;
     }
diff --git a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
index 331ef21..d3b9397 100644
--- a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
+++ b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
@@ -38,7 +38,7 @@
 
     private static final int R_EXTENSION_INT;
     static {
-        R_EXTENSION_INT = SystemProperties.getInt("persist.com.android.sdkext.sdk_info", 0);
+        R_EXTENSION_INT = SystemProperties.getInt("ro.build.version.extensions.r", 0);
     }
 
     /**
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index d76a40e..8327f31 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -14,20 +14,19 @@
 
 apex {
     name: "com.android.os.statsd",
-
+    defaults: ["com.android.os.statsd-defaults"],
     manifest: "apex_manifest.json",
 
-    // optional. if unspecified, a default one is auto-generated
-    //androidManifest: "AndroidManifest.xml",
 
+}
+
+apex_defaults {
     // libc.so and libcutils.so are included in the apex
     // native_shared_libs: ["libc", "libcutils"],
     // binaries: ["vold"],
     // java_libs: ["core-all"],
     // prebuilts: ["my_prebuilt"],
-
-    compile_multilib: "both",
-
+    name: "com.android.os.statsd-defaults",
     key: "com.android.os.statsd.key",
     certificate: ":com.android.os.statsd.certificate",
 }
diff --git a/apex/statsd/aidl/android/os/IStatsManager.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl
similarity index 99%
rename from apex/statsd/aidl/android/os/IStatsManager.aidl
rename to apex/statsd/aidl/android/os/IStatsd.aidl
index cc62f07..cffc6ce 100644
--- a/apex/statsd/aidl/android/os/IStatsManager.aidl
+++ b/apex/statsd/aidl/android/os/IStatsd.aidl
@@ -24,7 +24,7 @@
   * Binder interface to communicate with the statistics management service.
   * {@hide}
   */
-interface IStatsManager {
+interface IStatsd {
     /**
      * Tell the stats daemon that the android system server is up and running.
      */
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index 6fb3bc4..bc7716e 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -79,7 +79,7 @@
 import android.os.IBinder;
 import android.os.IPullAtomCallback;
 import android.os.IStatsCompanionService;
-import android.os.IStatsManager;
+import android.os.IStatsd;
 import android.os.IStoraged;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
@@ -268,7 +268,7 @@
     private final AlarmManager mAlarmManager;
     private final INetworkStatsService mNetworkStatsService;
     @GuardedBy("sStatsdLock")
-    private static IStatsManager sStatsd;
+    private static IStatsd sStatsd;
     private static final Object sStatsdLock = new Object();
 
     private final OnAlarmListener mAnomalyAlarmListener = new AnomalyAlarmListener();
@@ -2743,8 +2743,8 @@
      * Note: This should only be called from sayHiToStatsd. All other clients should use the cached
      * sStatsd with a null check.
      */
-    private static IStatsManager fetchStatsdService() {
-        return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
+    private static IStatsd fetchStatsdService() {
+        return IStatsd.Stub.asInterface(ServiceManager.getService("stats"));
     }
 
     public static final class Lifecycle extends SystemService {
diff --git a/apex/statsd/testing/Android.bp b/apex/statsd/testing/Android.bp
new file mode 100644
index 0000000..22e7301
--- /dev/null
+++ b/apex/statsd/testing/Android.bp
@@ -0,0 +1,25 @@
+// 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.
+
+apex {
+    name: "test_com.android.os.statsd",
+    visibility: [
+        "//system/apex/tests",
+    ],
+    defaults: ["com.android.os.statsd-defaults"],
+    manifest: "test_manifest.json",
+    file_contexts: ":com.android.os.statsd-file_contexts",
+    // Test APEX, should never be installed
+    installable: false,
+}
diff --git a/apex/statsd/testing/test_manifest.json b/apex/statsd/testing/test_manifest.json
new file mode 100644
index 0000000..57343d3
--- /dev/null
+++ b/apex/statsd/testing/test_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.os.statsd",
+  "version": 2147483647
+}
diff --git a/api/current.txt b/api/current.txt
index c7a210e..d22e599 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6739,6 +6739,7 @@
     method @Nullable public java.util.Set<java.lang.String> getCrossProfileCalendarPackages(@NonNull android.content.ComponentName);
     method public boolean getCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName);
     method public boolean getCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName);
+    method @NonNull public java.util.Set<java.lang.String> getCrossProfilePackages(@NonNull android.content.ComponentName);
     method @NonNull public java.util.List<java.lang.String> getCrossProfileWidgetProviders(@NonNull android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
     method @Nullable public java.util.List<java.lang.String> getDelegatePackages(@NonNull android.content.ComponentName, @NonNull String);
@@ -6856,6 +6857,7 @@
     method public void setCrossProfileCalendarPackages(@NonNull android.content.ComponentName, @Nullable java.util.Set<java.lang.String>);
     method public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean);
     method public void setCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName, boolean);
+    method public void setCrossProfilePackages(@NonNull android.content.ComponentName, @NonNull java.util.Set<java.lang.String>);
     method public void setDefaultSmsApplication(@NonNull android.content.ComponentName, @NonNull String);
     method public void setDelegatedScopes(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.lang.String>);
     method public void setDeviceOwnerLockScreenInfo(@NonNull android.content.ComponentName, CharSequence);
@@ -25397,6 +25399,8 @@
     field public static final String KEY_OPERATING_RATE = "operating-rate";
     field public static final String KEY_OUTPUT_REORDER_DEPTH = "output-reorder-depth";
     field public static final String KEY_PCM_ENCODING = "pcm-encoding";
+    field public static final String KEY_PIXEL_ASPECT_RATIO_HEIGHT = "sar-height";
+    field public static final String KEY_PIXEL_ASPECT_RATIO_WIDTH = "sar-width";
     field public static final String KEY_PREPEND_HEADER_TO_SYNC_FRAMES = "prepend-sps-pps-to-idr-frames";
     field public static final String KEY_PRIORITY = "priority";
     field public static final String KEY_PROFILE = "profile";
@@ -25646,7 +25650,7 @@
     method public void onTracksFound(int);
   }
 
-  public static interface MediaParser.SeekMap {
+  public static final class MediaParser.SeekMap {
     method public long getDurationUs();
     method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long);
     method public boolean isSeekable();
@@ -29211,6 +29215,7 @@
 
   public class NetworkRequest implements android.os.Parcelable {
     method public int describeContents();
+    method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
     method public boolean hasCapability(int);
     method public boolean hasTransport(int);
     method public void writeToParcel(android.os.Parcel, int);
@@ -38344,6 +38349,8 @@
 
   public static final class ContactsContract.RawContacts implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.SyncColumns {
     method public static android.net.Uri getContactLookupUri(android.content.ContentResolver, android.net.Uri);
+    method @Nullable public static String getLocalAccountName(@NonNull android.content.Context);
+    method @Nullable public static String getLocalAccountType(@NonNull android.content.Context);
     method public static android.content.EntityIterator newEntityIterator(android.database.Cursor);
     field public static final int AGGREGATION_MODE_DEFAULT = 0; // 0x0
     field public static final int AGGREGATION_MODE_DISABLED = 3; // 0x3
@@ -45595,6 +45602,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot();
     method public int getActiveModemCount();
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+    method @NonNull public static int[] getAllNetworkTypes();
     method public int getCallState();
     method public int getCardIdForDefaultEuicc();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
@@ -45967,6 +45975,7 @@
     field public static final int TYPE_MCX = 1024; // 0x400
     field public static final int TYPE_MMS = 2; // 0x2
     field public static final int TYPE_SUPL = 4; // 0x4
+    field public static final int TYPE_XCAP = 2048; // 0x800
   }
 
   public static class ApnSetting.Builder {
@@ -48715,6 +48724,13 @@
     ctor public Base64OutputStream(java.io.OutputStream, int);
   }
 
+  public final class CloseGuard {
+    ctor public CloseGuard();
+    method public void close();
+    method public void open(@NonNull String);
+    method public void warnIfOpen();
+  }
+
   @Deprecated public final class Config {
     field @Deprecated public static final boolean DEBUG = false;
     field @Deprecated public static final boolean LOGD = true;
diff --git a/api/system-current.txt b/api/system-current.txt
index 26e1e64..39c0882 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1423,15 +1423,24 @@
 
   public final class BluetoothDevice implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess();
+    method public boolean cancelPairing();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getBatteryLevel();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getMessageAccessPermission();
     method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getPhonebookAccessPermission();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getSimAccessPermission();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isBondingInitiatedLocally();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMessageAccessPermission(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, @NonNull byte[]);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPin(@Nullable String);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSilenceMode(boolean);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSimAccessPermission(int);
     field public static final int ACCESS_ALLOWED = 1; // 0x1
     field public static final int ACCESS_REJECTED = 2; // 0x2
     field public static final int ACCESS_UNKNOWN = 0; // 0x0
@@ -1686,9 +1695,25 @@
     field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
     field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
     field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
+    field @Deprecated public static final String EXTRA_SIM_LOCKED_REASON = "reason";
+    field @Deprecated public static final String EXTRA_SIM_STATE = "ss";
     field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
     field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
     field public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
+    field @Deprecated public static final String SIM_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
+    field @Deprecated public static final String SIM_LOCKED_NETWORK = "NETWORK";
+    field @Deprecated public static final String SIM_LOCKED_ON_PIN = "PIN";
+    field @Deprecated public static final String SIM_LOCKED_ON_PUK = "PUK";
+    field @Deprecated public static final String SIM_STATE_ABSENT = "ABSENT";
+    field @Deprecated public static final String SIM_STATE_CARD_IO_ERROR = "CARD_IO_ERROR";
+    field @Deprecated public static final String SIM_STATE_CARD_RESTRICTED = "CARD_RESTRICTED";
+    field @Deprecated public static final String SIM_STATE_IMSI = "IMSI";
+    field @Deprecated public static final String SIM_STATE_LOADED = "LOADED";
+    field @Deprecated public static final String SIM_STATE_LOCKED = "LOCKED";
+    field @Deprecated public static final String SIM_STATE_NOT_READY = "NOT_READY";
+    field @Deprecated public static final String SIM_STATE_PRESENT = "PRESENT";
+    field @Deprecated public static final String SIM_STATE_READY = "READY";
+    field @Deprecated public static final String SIM_STATE_UNKNOWN = "UNKNOWN";
   }
 
   public class IntentFilter implements android.os.Parcelable {
@@ -3394,6 +3419,14 @@
     field public static final int STATUS_OK = 0; // 0x0
   }
 
+  public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModelParamRange> CREATOR;
+    field public final int end;
+    field public final int start;
+  }
+
   public static final class SoundTrigger.ModuleProperties implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -3863,6 +3896,7 @@
     field public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 64; // 0x40
     field public static final int FLAG_BYPASS_MUTE = 128; // 0x80
     field public static final int FLAG_HW_HOTWORD = 32; // 0x20
+    field @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public static final int USAGE_CALL_ASSISTANT = 17; // 0x11
   }
 
   public static class AudioAttributes.Builder {
@@ -4182,8 +4216,11 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.media.soundtrigger.SoundTriggerDetector createSoundTriggerDetector(java.util.UUID, @NonNull android.media.soundtrigger.SoundTriggerDetector.Callback, @Nullable android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void deleteModel(java.util.UUID);
     method public int getDetectionServiceOperationsTimeout();
-    method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.media.soundtrigger.SoundTriggerManager.Model getModel(java.util.UUID);
+    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.media.soundtrigger.SoundTriggerManager.Model getModel(java.util.UUID);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModuleProperties getModuleProperties();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int getParameter(@NonNull java.util.UUID, int) throws java.lang.IllegalArgumentException, java.lang.UnsupportedOperationException;
+    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModelParamRange queryParameter(@Nullable java.util.UUID, int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int setParameter(@Nullable java.util.UUID, int, int) throws java.lang.IllegalArgumentException, java.lang.UnsupportedOperationException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void updateModel(android.media.soundtrigger.SoundTriggerManager.Model);
   }
 
@@ -4816,6 +4853,9 @@
   }
 
   public abstract class ChildSessionParams {
+    method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getLocalTrafficSelectors();
+    method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getRemoteTrafficSelectors();
+    method @NonNull public java.util.List<android.net.ipsec.ike.ChildSaProposal> getSaProposals();
   }
 
   public class IkeFqdnIdentification extends android.net.ipsec.ike.IkeIdentification {
@@ -4883,6 +4923,13 @@
   }
 
   public final class IkeSessionParams {
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getLocalAuthConfig();
+    method @NonNull public android.net.ipsec.ike.IkeIdentification getLocalIdentification();
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getRemoteAuthConfig();
+    method @NonNull public android.net.ipsec.ike.IkeIdentification getRemoteIdentification();
+    method @NonNull public java.util.List<android.net.ipsec.ike.IkeSaProposal> getSaProposals();
+    method @NonNull public java.net.InetAddress getServerAddress();
+    method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket getUdpEncapsulationSocket();
   }
 
   public static final class IkeSessionParams.Builder {
@@ -4899,6 +4946,27 @@
     method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
   }
 
+  public abstract static class IkeSessionParams.IkeAuthConfig {
+  }
+
+  public static class IkeSessionParams.IkeAuthDigitalSignLocalConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
+    method @NonNull public java.security.cert.X509Certificate getClientEndCertificate();
+    method @NonNull public java.util.List<java.security.cert.X509Certificate> getIntermediateCertificates();
+    method @NonNull public java.security.PrivateKey getPrivateKey();
+  }
+
+  public static class IkeSessionParams.IkeAuthDigitalSignRemoteConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
+    method @NonNull public java.security.cert.X509Certificate getRemoteCaCert();
+  }
+
+  public static class IkeSessionParams.IkeAuthEapConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
+    method @NonNull public android.net.eap.EapSessionConfig getEapConfig();
+  }
+
+  public static class IkeSessionParams.IkeAuthPskConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
+    method @NonNull public byte[] getPsk();
+  }
+
   public final class IkeTrafficSelector {
     ctor public IkeTrafficSelector(int, int, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress);
     field public final int endPort;
@@ -4945,6 +5013,7 @@
   }
 
   public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
+    method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest> getConfigurationRequests();
   }
 
   public static final class TunnelModeChildSessionParams.Builder {
@@ -4962,6 +5031,39 @@
     method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams build();
   }
 
+  public static interface TunnelModeChildSessionParams.ConfigRequest {
+  }
+
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+    method @Nullable public java.net.Inet4Address getAddress();
+  }
+
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+    method @Nullable public java.net.Inet4Address getAddress();
+  }
+
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+    method @Nullable public java.net.Inet4Address getAddress();
+  }
+
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+  }
+
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Subnet extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+  }
+
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+    method @Nullable public java.net.Inet6Address getAddress();
+    method public int getPrefixLength();
+  }
+
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+    method @Nullable public java.net.Inet6Address getAddress();
+  }
+
+  public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Subnet extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+  }
+
 }
 
 package android.net.ipsec.ike.exceptions {
@@ -6804,7 +6906,7 @@
     method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
     method @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
-    method @BinderThread public void onUpdateUserSensitive();
+    method @BinderThread public void onUpdateUserSensitivePermissionFlags();
     field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
   }
 
@@ -6978,7 +7080,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(@NonNull String, @NonNull String, @Nullable String);
     method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
-    method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperties(@NonNull android.provider.DeviceConfig.Properties);
+    method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperties(@NonNull android.provider.DeviceConfig.Properties) throws android.provider.DeviceConfig.BadConfigException;
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
     field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
     field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot";
@@ -7010,6 +7112,10 @@
     field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
   }
 
+  public static class DeviceConfig.BadConfigException extends java.lang.Exception {
+    ctor public DeviceConfig.BadConfigException();
+  }
+
   public static interface DeviceConfig.OnPropertiesChangedListener {
     method public void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties);
   }
@@ -8014,16 +8120,6 @@
 
 }
 
-package android.service.sms {
-
-  public abstract class FinancialSmsService extends android.app.Service {
-    method public android.os.IBinder onBind(android.content.Intent);
-    method @Nullable public abstract android.database.CursorWindow onGetSmsMessages(@NonNull android.os.Bundle);
-    field public static final String ACTION_FINANCIAL_SERVICE_INTENT = "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
-  }
-
-}
-
 package android.service.storage {
 
   public abstract class ExternalStorageService extends android.app.Service {
@@ -8487,14 +8583,6 @@
     field public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
   }
 
-  public static final class CarrierConfigManager.Wifi {
-    field public static final String KEY_CARRIER_CONNECTION_MANAGER_PACKAGE_STRING = "wifi.carrier_connection_manager_package_string";
-    field public static final String KEY_CARRIER_PROFILES_VERSION_INT = "wifi.carrier_profiles_version_int";
-    field public static final String KEY_NETWORK_PROFILES_STRING_ARRAY = "wifi.network_profiles_string_array";
-    field public static final String KEY_PASSPOINT_PROFILES_STRING_ARRAY = "wifi.passpoint_profiles_string_array";
-    field public static final String KEY_PREFIX = "wifi.";
-  }
-
   public final class CarrierRestrictionRules implements android.os.Parcelable {
     method @NonNull public java.util.List<java.lang.Boolean> areCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
     method public int describeContents();
@@ -8866,6 +8954,7 @@
     field public static final int UE_SECURITY_CAPABILITIES_MISMATCH = 2185; // 0x889
     field public static final int UMTS_HANDOVER_TO_IWLAN = 2199; // 0x897
     field public static final int UMTS_REACTIVATION_REQ = 39; // 0x27
+    field public static final int UNACCEPTABLE_NETWORK_PARAMETER = 65538; // 0x10002
     field public static final int UNACCEPTABLE_NON_EPS_AUTHENTICATION = 2187; // 0x88b
     field public static final int UNKNOWN = 65536; // 0x10000
     field public static final int UNKNOWN_INFO_ELEMENT = 99; // 0x63
@@ -9537,6 +9626,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
     method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
     method public boolean isDataConnectivityPossible();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
@@ -9575,6 +9665,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int);
     method @Deprecated public void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoiceActivationState(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void shutdownAllRadios();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPin(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPuk(String, String);
diff --git a/api/test-current.txt b/api/test-current.txt
index 3e14469..efb8538 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1151,6 +1151,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
+    field public static final String FUSED_PROVIDER = "fused";
   }
 
   public final class LocationRequest implements android.os.Parcelable {
@@ -4437,7 +4438,7 @@
 
   public final class AccessibilityManager {
     method public void addAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, @Nullable android.os.Handler);
-    method @Nullable @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public String getAccessibilityShortcutService();
+    method @NonNull @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int);
     method @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public void performAccessibilityShortcut();
     method public void removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
   }
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 17427a2..484f823 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -250,6 +250,7 @@
         "tests/external/GpuStatsPuller_test.cpp",
         "tests/external/IncidentReportArgs_test.cpp",
         "tests/external/puller_util_test.cpp",
+        "tests/external/StatsCallbackPuller_test.cpp",
         "tests/external/StatsPuller_test.cpp",
         "tests/external/SurfaceflingerStatsPuller_test.cpp",
         "tests/FieldValue_test.cpp",
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 3c5ad42..4d38ba0 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -272,7 +272,7 @@
             }
             return NO_ERROR;
         }
-        default: { return BnStatsManager::onTransact(code, data, reply, flags); }
+        default: { return BnStatsd::onTransact(code, data, reply, flags); }
     }
 }
 
@@ -862,13 +862,13 @@
     int64_t trainVersion = strtoll(args[2].c_str(), nullptr, 10);
     int options = 0;
     if (args[3] == "1") {
-        options = options | IStatsManager::FLAG_REQUIRE_STAGING;
+        options = options | IStatsd::FLAG_REQUIRE_STAGING;
     }
     if (args[4] == "1") {
-        options = options | IStatsManager::FLAG_ROLLBACK_ENABLED;
+        options = options | IStatsd::FLAG_ROLLBACK_ENABLED;
     }
     if (args[5] == "1") {
-        options = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
+        options = options | IStatsd::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
     }
     int32_t state = atoi(args[6].c_str());
     vector<int64_t> experimentIds;
@@ -1406,9 +1406,9 @@
     StorageManager::writeTrainInfo(trainVersionCode, trainNameUtf8, state, experimentIds);
 
     userid_t userId = multiuser_get_user_id(uid);
-    bool requiresStaging = options & IStatsManager::FLAG_REQUIRE_STAGING;
-    bool rollbackEnabled = options & IStatsManager::FLAG_ROLLBACK_ENABLED;
-    bool requiresLowLatencyMonitor = options & IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
+    bool requiresStaging = options & IStatsd::FLAG_REQUIRE_STAGING;
+    bool rollbackEnabled = options & IStatsd::FLAG_ROLLBACK_ENABLED;
+    bool requiresLowLatencyMonitor = options & IStatsd::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
     LogEvent event(trainNameUtf8, trainVersionCode, requiresStaging, rollbackEnabled,
                    requiresLowLatencyMonitor, state, experimentIdsProtoBuffer, userId);
     mProcessor->OnLogEvent(&event);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 50b1014..9abf415 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -29,9 +29,9 @@
 
 #include <android/frameworks/stats/1.0/IStats.h>
 #include <android/frameworks/stats/1.0/types.h>
-#include <android/os/BnStatsManager.h>
+#include <android/os/BnStatsd.h>
 #include <android/os/IStatsCompanionService.h>
-#include <android/os/IStatsManager.h>
+#include <android/os/IStatsd.h>
 #include <binder/IResultReceiver.h>
 #include <binder/ParcelFileDescriptor.h>
 #include <utils/Looper.h>
@@ -52,7 +52,7 @@
 
 using android::hardware::Return;
 
-class StatsService : public BnStatsManager,
+class StatsService : public BnStatsd,
                      public IStats,
                      public IBinder::DeathRecipient {
 public:
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 77ff707..a20436d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -113,8 +113,8 @@
         WakeupAlarmOccurred wakeup_alarm_occurred = 35;
         KernelWakeupReported kernel_wakeup_reported = 36;
         WifiLockStateChanged wifi_lock_state_changed = 37 [(log_from_module) = "wifi"];
-        WifiSignalStrengthChanged wifi_signal_strength_changed = 38;
-        WifiScanStateChanged wifi_scan_state_changed = 39;
+        WifiSignalStrengthChanged wifi_signal_strength_changed = 38 [(log_from_module) = "wifi"];
+        WifiScanStateChanged wifi_scan_state_changed = 39 [(log_from_module) = "wifi"];
         PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
         SettingChanged setting_changed = 41;
         ActivityForegroundStateChanged activity_foreground_state_changed = 42;
@@ -128,7 +128,7 @@
         AppStartFullyDrawn app_start_fully_drawn = 50;
         LmkKillOccurred lmk_kill_occurred = 51;
         PictureInPictureStateChanged picture_in_picture_state_changed = 52;
-        WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53;
+        WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53 [(log_from_module) = "wifi"];
         LmkStateChanged lmk_state_changed = 54;
         AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
         ShutdownSequenceReported shutdown_sequence_reported = 56;
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index f5b1e7f..0e6b677 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -35,8 +35,9 @@
 namespace os {
 namespace statsd {
 
-StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback)
-    : StatsPuller(tagId), mCallback(callback) {
+StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback,
+                                         int64_t timeoutNs)
+    : StatsPuller(tagId), mCallback(callback), mTimeoutNs(timeoutNs) {
     VLOG("StatsCallbackPuller created for tag %d", tagId);
 }
 
@@ -64,10 +65,9 @@
                 {
                     lock_guard<mutex> lk(*cv_mutex);
                     for (const StatsEventParcel& parcel: output) {
-                        shared_ptr<LogEvent> event =
-                              make_shared<LogEvent>(const_cast<uint8_t*>(parcel.buffer.data()),
-                                                    parcel.buffer.size(),
-                                                    /*uid=*/ -1);
+                        shared_ptr<LogEvent> event = make_shared<LogEvent>(
+                                const_cast<uint8_t*>(parcel.buffer.data()), parcel.buffer.size(),
+                                /*uid=*/-1, /*useNewSchema=*/true);
                         sharedData->push_back(event);
                     }
                     *pullSuccess = success;
@@ -76,7 +76,8 @@
                 cv->notify_one();
             });
 
-    // Initiate the pull.
+    // Initiate the pull. This is a oneway call to a different process, except
+    // in unit tests. In process calls are not oneway.
     Status status = mCallback->onPullAtom(mTagId, resultReceiver);
     if (!status.isOk()) {
         return false;
@@ -84,10 +85,8 @@
 
     {
         unique_lock<mutex> unique_lk(*cv_mutex);
-        int64_t pullTimeoutNs =
-                StatsPullerManager::kAllPullAtomInfo.at({.atomTag = mTagId}).pullTimeoutNs;
         // Wait until the pull finishes, or until the pull timeout.
-        cv->wait_for(unique_lk, chrono::nanoseconds(pullTimeoutNs),
+        cv->wait_for(unique_lk, chrono::nanoseconds(mTimeoutNs),
                      [pullFinish] { return *pullFinish; });
         if (!*pullFinish) {
             // Note: The parent stats puller will also note that there was a timeout and that the
@@ -96,7 +95,7 @@
             return true;
         } else {
             // Only copy the data if we did not timeout and the pull was successful.
-            if (pullSuccess) {
+            if (*pullSuccess) {
                 *data = std::move(*sharedData);
             }
             VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId);
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h
index ce506c7..d943f9d 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.h
+++ b/cmds/statsd/src/external/StatsCallbackPuller.h
@@ -27,11 +27,17 @@
 
 class StatsCallbackPuller : public StatsPuller {
 public:
-    explicit StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback);
+    explicit StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback,
+                                 int64_t timeoutNs);
 
 private:
     bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
     const sp<IPullAtomCallback> mCallback;
+    const int64_t mTimeoutNs;
+
+    FRIEND_TEST(StatsCallbackPullerTest, PullFail);
+    FRIEND_TEST(StatsCallbackPullerTest, PullSuccess);
+    FRIEND_TEST(StatsCallbackPullerTest, PullTimeout);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 615af89..9ee627e 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -500,10 +500,11 @@
     VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
     // TODO: linkToDeath with the callback so that we can remove it and delete the puller.
     StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
-    kAllPullAtomInfo[{.atomTag = atomTag}] = {.additiveFields = additiveFields,
-                                              .coolDownNs = coolDownNs,
-                                              .puller = new StatsCallbackPuller(atomTag, callback),
-                                              .pullTimeoutNs = timeoutNs,
+    kAllPullAtomInfo[{.atomTag = atomTag}] = {
+            .additiveFields = additiveFields,
+            .coolDownNs = coolDownNs,
+            .puller = new StatsCallbackPuller(atomTag, callback, timeoutNs),
+            .pullTimeoutNs = timeoutNs,
     };
 }
 
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 67022a0..36f4623 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -52,6 +52,17 @@
 #endif
 }
 
+LogEvent::LogEvent(uint8_t* msg, uint32_t len, uint32_t uid, bool useNewSchema)
+    : mBuf(msg), mRemainingLen(len), mLogdTimestampNs(time(nullptr)), mLogUid(uid) {
+    if (useNewSchema) {
+        initNew();
+    } else {
+        mContext = create_android_log_parser((char*)msg, len);
+        init(mContext);
+        if (mContext) android_log_destroy(&mContext);  // set mContext to NULL
+    }
+}
+
 LogEvent::LogEvent(const LogEvent& event) {
     mTagId = event.mTagId;
     mLogUid = event.mLogUid;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 1ff95f7..596d623 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -75,6 +75,11 @@
     explicit LogEvent(uint8_t* msg, uint32_t len, uint32_t uid);
 
     /**
+     * Temp constructor to use for pulled atoms until we flip the socket schema.
+     */
+    explicit LogEvent(uint8_t* msg, uint32_t len, uint32_t uid, bool useNewSchema);
+
+    /**
      * Creates LogEvent from StatsLogEventWrapper.
      */
     static void createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
index 2fa28c9..80d3983 100644
--- a/cmds/statsd/src/state/StateManager.cpp
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -29,18 +29,15 @@
 }
 
 void StateManager::onLogEvent(const LogEvent& event) {
-    std::lock_guard<std::mutex> lock(mMutex);
     if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) {
         mStateTrackers[event.GetTagId()]->onLogEvent(event);
     }
 }
 
 bool StateManager::registerListener(int32_t atomId, wp<StateListener> listener) {
-    std::lock_guard<std::mutex> lock(mMutex);
-
-    // Check if state tracker already exists
+    // Check if state tracker already exists.
     if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
-        // Create a new state tracker iff atom is a state atom
+        // Create a new state tracker iff atom is a state atom.
         auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId);
         if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
             mStateTrackers[atomId] = new StateTracker(atomId, it->second);
@@ -79,8 +76,6 @@
 
 bool StateManager::getStateValue(int32_t atomId, const HashableDimensionKey& key,
                                  FieldValue* output) const {
-    std::lock_guard<std::mutex> lock(mMutex);
-
     auto it = mStateTrackers.find(atomId);
     if (it != mStateTrackers.end()) {
         return it->second->getStateValue(key, output);
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
index 272724c..a6053e6 100644
--- a/cmds/statsd/src/state/StateManager.h
+++ b/cmds/statsd/src/state/StateManager.h
@@ -27,6 +27,10 @@
 namespace os {
 namespace statsd {
 
+/**
+ * This class is NOT thread safe.
+ * It should only be used while StatsLogProcessor's lock is held.
+ */
 class StateManager : public virtual RefBase {
 public:
     StateManager(){};
@@ -56,13 +60,10 @@
                        FieldValue* output) const;
 
     inline int getStateTrackersCount() const {
-        std::lock_guard<std::mutex> lock(mMutex);
         return mStateTrackers.size();
     }
 
     inline int getListenersCount(int32_t atomId) const {
-        std::lock_guard<std::mutex> lock(mMutex);
-
         auto it = mStateTrackers.find(atomId);
         if (it != mStateTrackers.end()) {
             return it->second->getListenersCount();
@@ -71,10 +72,10 @@
     }
 
 private:
-  mutable std::mutex mMutex;
+    mutable std::mutex mMutex;
 
-  // Maps state atom ids to StateTrackers
-  std::unordered_map<int32_t, sp<StateTracker>> mStateTrackers;
+    // Maps state atom ids to StateTrackers
+    std::unordered_map<int32_t, sp<StateTracker>> mStateTrackers;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
new file mode 100644
index 0000000..2b0590d
--- /dev/null
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -0,0 +1,210 @@
+// 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.
+
+#include "src/external/StatsCallbackPuller.h"
+
+#include <android/os/BnPullAtomCallback.h>
+#include <android/os/IPullAtomResultReceiver.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+
+#include <chrono>
+#include <thread>
+#include <vector>
+
+#include "../metrics/metrics_test_helper.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using std::this_thread::sleep_for;
+using testing::Contains;
+
+namespace {
+int pullTagId = -12;
+bool pullSuccess;
+vector<int64_t> values;
+int64_t pullDelayNs;
+int64_t pullTimeoutNs;
+int64_t pullCoolDownNs;
+std::thread pullThread;
+
+stats_event* createSimpleEvent(int64_t value) {
+    stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, pullTagId);
+    stats_event_write_int64(event, value);
+    stats_event_build(event);
+    return event;
+}
+
+void executePull(const sp<IPullAtomResultReceiver>& resultReceiver) {
+    // Convert stats_events into StatsEventParcels.
+    std::vector<android::util::StatsEventParcel> parcels;
+    for (int i = 0; i < values.size(); i++) {
+        stats_event* event = createSimpleEvent(values[i]);
+        size_t size;
+        uint8_t* buffer = stats_event_get_buffer(event, &size);
+
+        android::util::StatsEventParcel p;
+        // vector.assign() creates a copy, but this is inevitable unless
+        // stats_event.h/c uses a vector as opposed to a buffer.
+        p.buffer.assign(buffer, buffer + size);
+        parcels.push_back(std::move(p));
+        stats_event_release(event);
+    }
+
+    sleep_for(std::chrono::nanoseconds(pullDelayNs));
+    resultReceiver->pullFinished(pullTagId, pullSuccess, parcels);
+}
+
+class FakePullAtomCallback : public BnPullAtomCallback {
+public:
+    binder::Status onPullAtom(int atomTag,
+                              const sp<IPullAtomResultReceiver>& resultReceiver) override {
+        // Force pull to happen in separate thread to simulate binder.
+        pullThread = std::thread(executePull, resultReceiver);
+        return binder::Status::ok();
+    }
+};
+
+class StatsCallbackPullerTest : public ::testing::Test {
+public:
+    StatsCallbackPullerTest() {
+    }
+
+    void SetUp() override {
+        pullSuccess = false;
+        pullDelayNs = 0;
+        values.clear();
+        pullTimeoutNs = 10000000000LL;  // 10 seconds.
+        pullCoolDownNs = 1000000000;    // 1 second.
+    }
+
+    void TearDown() override {
+        if (pullThread.joinable()) {
+            pullThread.join();
+        }
+        values.clear();
+    }
+};
+}  // Anonymous namespace.
+
+TEST_F(StatsCallbackPullerTest, PullSuccess) {
+    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    int64_t value = 43;
+    pullSuccess = true;
+    values.push_back(value);
+
+    StatsCallbackPuller puller(pullTagId, cb, pullTimeoutNs);
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    int64_t startTimeNs = getElapsedRealtimeNs();
+    EXPECT_TRUE(puller.PullInternal(&dataHolder));
+    int64_t endTimeNs = getElapsedRealtimeNs();
+
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_LT(startTimeNs, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_GT(endTimeNs, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(value, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+TEST_F(StatsCallbackPullerTest, PullFail) {
+    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    pullSuccess = false;
+    int64_t value = 1234;
+    values.push_back(value);
+
+    StatsCallbackPuller puller(pullTagId, cb, pullTimeoutNs);
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.PullInternal(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsCallbackPullerTest, PullTimeout) {
+    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    pullSuccess = true;
+    pullDelayNs = 500000000;  // 500ms.
+    pullTimeoutNs = 10000;    // 10 microseconds.
+    int64_t value = 4321;
+    values.push_back(value);
+
+    StatsCallbackPuller puller(pullTagId, cb, pullTimeoutNs);
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    int64_t startTimeNs = getElapsedRealtimeNs();
+    // Returns true to let StatsPuller code evaluate the timeout.
+    EXPECT_TRUE(puller.PullInternal(&dataHolder));
+    int64_t endTimeNs = getElapsedRealtimeNs();
+    int64_t actualPullDurationNs = endTimeNs - startTimeNs;
+
+    // Pull should take at least the timeout amount of time, but should stop early because the delay
+    // is bigger.
+    EXPECT_LT(pullTimeoutNs, actualPullDurationNs);
+    EXPECT_GT(pullDelayNs, actualPullDurationNs);
+    EXPECT_EQ(0, dataHolder.size());
+
+    // Let the pull return and make sure that the dataHolder is not modified.
+    pullThread.join();
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+// Register a puller and ensure that the timeout logic works.
+TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) {
+    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    pullSuccess = true;
+    pullDelayNs = 500000000;  // 500 ms.
+    pullTimeoutNs = 10000;    // 10 microsseconds.
+    int64_t value = 4321;
+    values.push_back(value);
+
+    StatsPullerManager pullerManager;
+    pullerManager.RegisterPullAtomCallback(/*uid=*/-1, pullTagId, pullCoolDownNs, pullTimeoutNs,
+                                           vector<int32_t>(), cb);
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    int64_t startTimeNs = getElapsedRealtimeNs();
+    // Returns false, since StatsPuller code will evaluate the timeout.
+    EXPECT_FALSE(pullerManager.Pull(pullTagId, &dataHolder));
+    int64_t endTimeNs = getElapsedRealtimeNs();
+    int64_t actualPullDurationNs = endTimeNs - startTimeNs;
+
+    // Pull should take at least the timeout amount of time, but should stop early because the delay
+    // is bigger.
+    EXPECT_LT(pullTimeoutNs, actualPullDurationNs);
+    EXPECT_GT(pullDelayNs, actualPullDurationNs);
+    EXPECT_EQ(0, dataHolder.size());
+
+    // Let the pull return and make sure that the dataHolder is not modified.
+    pullThread.join();
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp
index 76e2097..c40719a 100644
--- a/cmds/statsd/tests/external/StatsPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsPuller_test.cpp
@@ -35,6 +35,7 @@
 using std::this_thread::sleep_for;
 using testing::Contains;
 
+namespace {
 // cooldown time 1sec.
 int pullTagId = 10014;
 
@@ -76,7 +77,9 @@
     }
 };
 
-TEST_F(StatsPullerTest, PullSucces) {
+}  // Anonymous namespace.
+
+TEST_F(StatsPullerTest, PullSuccess) {
     pullData.push_back(createSimpleEvent(1111L, 33));
 
     pullSuccess = true;
diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp
index 1173d57..3a26063 100644
--- a/cmds/uiautomator/library/Android.bp
+++ b/cmds/uiautomator/library/Android.bp
@@ -22,6 +22,7 @@
         "android.test.runner",
         "junit",
         "android.test.base",
+        "unsupportedappusage",
     ],
     custom_template: "droiddoc-templates-sdk",
     installable: false,
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index f4cadfd..d79740b 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -141,6 +141,16 @@
     }
 
     /**
+     * The {@link ComponentName} of the accessibility shortcut target.
+     *
+     * @return The component name
+     */
+    @NonNull
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    /**
      * The localized summary of the accessibility shortcut target.
      *
      * @return The localized summary if available, and {@code null} if a summary
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 49a8e2f3..93b6454 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -7395,6 +7395,24 @@
                 super.remove(path);
             }
         }
+
+        @Override
+        public void rename(String oldPath, String newPath) throws ErrnoException {
+            try {
+                super.rename(oldPath, newPath);
+            } catch (ErrnoException e) {
+                if (e.errno == OsConstants.EXDEV && oldPath.startsWith("/storage/")) {
+                    Log.v(TAG, "Recovering failed rename " + oldPath + " to " + newPath);
+                    try {
+                        Files.move(new File(oldPath).toPath(), new File(newPath).toPath());
+                    } catch (IOException e2) {
+                        throw e;
+                    }
+                } else {
+                    throw e;
+                }
+            }
+        }
     }
 
     public static void main(String[] args) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 5032f77..f2fa4fd 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2008,7 +2008,7 @@
             false, // WRITE_MEDIA_VIDEO
             false, // READ_MEDIA_IMAGES
             false, // WRITE_MEDIA_IMAGES
-            false, // LEGACY_STORAGE
+            true,  // LEGACY_STORAGE
             false, // ACCESS_ACCESSIBILITY
             false, // READ_DEVICE_IDENTIFIERS
             false, // ACCESS_MEDIA_LOCATION
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index d3d7c68..254657e 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -84,6 +84,19 @@
             long appVersionCode, in ParcelFileDescriptor newState,
             int token, IBackupManager callbackBinder);
 
+     /**
+     * Restore an entire data snapshot to the application and pass the list of excluded keys to the
+     * backup agent.
+     *
+     * @param excludedKeys List of keys to be excluded from the restore. It will be passed to the
+     *        backup agent to make it aware of what data has been removed (in case it has any
+     *        application-level implications) as well as the data that should be removed by the
+     *        agent itself.
+     */
+    void doRestoreWithExcludedKeys(in ParcelFileDescriptor data,
+            long appVersionCode, in ParcelFileDescriptor newState,
+            int token, IBackupManager callbackBinder, in List<String> excludedKeys);
+
     /**
      * Perform a "full" backup to the given file descriptor.  The output file is presumed
      * to be a socket or other non-seekable, write-only data sink.  When this method is
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 588acee..690e956 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -493,6 +493,7 @@
     public Activity startActivitySync(@NonNull Intent intent, @Nullable Bundle options) {
         validateNotAppThread();
 
+        final Activity activity;
         synchronized (mSync) {
             intent = new Intent(intent);
 
@@ -527,16 +528,18 @@
                 } catch (InterruptedException e) {
                 }
             } while (mWaitingActivities.contains(aw));
-
-            waitForEnterAnimationComplete(aw.activity);
-
-            // Apply an empty transaction to ensure SF has a chance to update before
-            // the Activity is ready (b/138263890).
-            try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
-                t.apply(true);
-            }
-            return aw.activity;
+            activity = aw.activity;
         }
+
+        // Do not call this method within mSync, lest it could block the main thread.
+        waitForEnterAnimationComplete(activity);
+
+        // Apply an empty transaction to ensure SF has a chance to update before
+        // the Activity is ready (b/138263890).
+        try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
+            t.apply(true);
+        }
+        return activity;
     }
 
     /**
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 90cd51f..f6e9569 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -27,8 +27,8 @@
 import android.os.IPullAtomCallback;
 import android.os.IPullAtomResultReceiver;
 import android.os.IStatsCompanionService;
-import android.os.IStatsManager;
 import android.os.IStatsPullerCallback;
+import android.os.IStatsd;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.AndroidException;
@@ -56,7 +56,7 @@
     private final Context mContext;
 
     @GuardedBy("sLock")
-    private IStatsManager mService;
+    private IStatsd mService;
 
     @GuardedBy("sLock")
     private IStatsCompanionService mStatsCompanion;
@@ -129,7 +129,7 @@
     public void addConfig(long configKey, byte[] config) throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 // can throw IllegalArgumentException
                 service.addConfiguration(configKey, config, mContext.getOpPackageName());
             } catch (RemoteException e) {
@@ -166,7 +166,7 @@
     public void removeConfig(long configKey) throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 service.removeConfiguration(configKey, mContext.getOpPackageName());
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to connect to statsd when removing configuration");
@@ -227,7 +227,7 @@
             throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 if (pendingIntent != null) {
                     // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
                     IBinder intentSender = pendingIntent.getTarget().asBinder();
@@ -281,7 +281,7 @@
             throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 if (pendingIntent == null) {
                     service.removeDataFetchOperation(configKey, mContext.getOpPackageName());
                 } else {
@@ -319,7 +319,7 @@
             throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 if (pendingIntent == null) {
                     service.removeActiveConfigsChangedOperation(mContext.getOpPackageName());
                     return new long[0];
@@ -367,7 +367,7 @@
     public byte[] getReports(long configKey) throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 return service.getData(configKey, mContext.getOpPackageName());
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to connect to statsd when getting data");
@@ -404,7 +404,7 @@
     public byte[] getStatsMetadata() throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 return service.getMetadata(mContext.getOpPackageName());
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to connect to statsd when getting metadata");
@@ -439,7 +439,7 @@
             throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 if (service == null) {
                     if (DEBUG) {
                         Slog.d(TAG, "Failed to find statsd when getting experiment IDs");
@@ -476,7 +476,7 @@
             throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 if (callback == null) {
                     service.unregisterPullerCallback(atomTag, mContext.getOpPackageName());
                 } else {
@@ -660,11 +660,11 @@
     }
 
     @GuardedBy("sLock")
-    private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException {
+    private IStatsd getIStatsdLocked() throws StatsUnavailableException {
         if (mService != null) {
             return mService;
         }
-        mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
+        mService = IStatsd.Stub.asInterface(ServiceManager.getService("stats"));
         if (mService == null) {
             throw new StatsUnavailableException("could not be found");
         }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 915e457..63eb7ce 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11085,6 +11085,53 @@
     }
 
     /**
+     * Sets the set of package names that are allowed to request user consent for cross-profile
+     * communication.
+     *
+     * <p>Assumes that the caller is a profile owner and is the given {@code admin}.
+     *
+     * <p>Previous calls are overridden by each subsequent call to this method.
+     *
+     * @param admin the {@link DeviceAdminReceiver} this request is associated with
+     * @param packageNames the new cross-profile package names
+     */
+    public void setCrossProfilePackages(
+            @NonNull ComponentName admin, @NonNull Set<String> packageNames) {
+        throwIfParentInstance("setCrossProfilePackages");
+        if (mService != null) {
+            try {
+                mService.setCrossProfilePackages(admin, new ArrayList<>(packageNames));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the set of package names that the admin has previously set as allowed to request user
+     * consent for cross-profile communication, via {@link
+     * #setCrossProfilePackages(ComponentName, Set)}.
+     *
+     * <p>Assumes that the caller is a profile owner and is the given {@code admin}.
+     *
+     * @param admin the {@link DeviceAdminReceiver} this request is associated with
+     * @return the set of package names the admin has previously set as allowed to request user
+     * consent for cross-profile communication, via {@link
+     * #setCrossProfilePackages(ComponentName, Set)}
+     */
+    public @NonNull Set<String> getCrossProfilePackages(@NonNull ComponentName admin) {
+        throwIfParentInstance("getCrossProfilePackages");
+        if (mService != null) {
+            try {
+                return new ArraySet<>(mService.getCrossProfilePackages(admin));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return Collections.emptySet();
+    }
+
+    /**
      * Returns whether the device is being used as a managed kiosk. These requirements are as
      * follows:
      * <ul>
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 34246fa..ff1ecd5 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -438,6 +438,9 @@
     boolean isPackageAllowedToAccessCalendarForUser(String packageName, int userHandle);
     List<String> getCrossProfileCalendarPackagesForUser(int userHandle);
 
+    void setCrossProfilePackages(in ComponentName admin, in List<String> packageNames);
+    List<String> getCrossProfilePackages(in ComponentName admin);
+
     boolean isManagedKiosk();
     boolean isUnattendedManagedKiosk();
 
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 24580b4..20aa064 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -45,7 +45,10 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -327,6 +330,28 @@
     }
 
     /**
+     * New version of {@link #onRestore(BackupDataInput, long, android.os.ParcelFileDescriptor)}
+     * that has a list of keys to be excluded from the restore. Key/value pairs for which the key
+     * is present in {@code excludedKeys} have already been excluded from the restore data by the
+     * system. The list is passed to the agent to make it aware of what data has been removed (in
+     * case it has any application-level consequences) as well as the data that should be removed
+     * by the agent itself.
+     *
+     * The default implementation calls {@link #onRestore(BackupDataInput, long,
+     * android.os.ParcelFileDescriptor)}.
+     *
+     * @param excludedKeys A list of keys to be excluded from restore.
+     *
+     * @hide
+     */
+    public void onRestore(BackupDataInput data, long appVersionCode,
+            ParcelFileDescriptor newState,
+            Set<String> excludedKeys)
+            throws IOException {
+        onRestore(data, appVersionCode, newState);
+    }
+
+    /**
      * The application is having its entire file system contents backed up.  {@code data}
      * points to the backup destination, and the app has the opportunity to choose which
      * files are to be stored.  To commit a file as part of the backup, call the
@@ -1016,8 +1041,22 @@
 
         @Override
         public void doRestore(ParcelFileDescriptor data, long appVersionCode,
-                ParcelFileDescriptor newState,
-                int token, IBackupManager callbackBinder) throws RemoteException {
+                ParcelFileDescriptor newState, int token, IBackupManager callbackBinder)
+                throws RemoteException {
+            doRestoreInternal(data, appVersionCode, newState, token, callbackBinder,
+                    /* excludedKeys */ null);
+        }
+
+        @Override
+        public void doRestoreWithExcludedKeys(ParcelFileDescriptor data, long appVersionCode,
+                ParcelFileDescriptor newState, int token, IBackupManager callbackBinder,
+                List<String> excludedKeys) throws RemoteException {
+            doRestoreInternal(data, appVersionCode, newState, token, callbackBinder, excludedKeys);
+        }
+
+        private void doRestoreInternal(ParcelFileDescriptor data, long appVersionCode,
+                ParcelFileDescriptor newState, int token, IBackupManager callbackBinder,
+                List<String> excludedKeys) throws RemoteException {
             // Ensure that we're running with the app's normal permission level
             long ident = Binder.clearCallingIdentity();
 
@@ -1029,7 +1068,9 @@
 
             BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
             try {
-                BackupAgent.this.onRestore(input, appVersionCode, newState);
+                BackupAgent.this.onRestore(input, appVersionCode, newState,
+                        excludedKeys != null ? new HashSet<>(excludedKeys)
+                                : Collections.emptySet());
             } catch (IOException ex) {
                 Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
                 throw new RuntimeException(ex);
diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java
index 471606da..55f92be 100644
--- a/core/java/android/app/timedetector/ManualTimeSuggestion.java
+++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java
@@ -56,6 +56,7 @@
 
     public ManualTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
         mUtcTime = Objects.requireNonNull(utcTime);
+        Objects.requireNonNull(utcTime.getValue());
     }
 
     private static ManualTimeSuggestion createFromParcel(Parcel in) {
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index dd02af7..4a89a12 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -166,7 +166,12 @@
         }
 
         /** Returns the builder for call chaining. */
-        public Builder setUtcTime(TimestampedValue<Long> utcTime) {
+        public Builder setUtcTime(@Nullable TimestampedValue<Long> utcTime) {
+            if (utcTime != null) {
+                // utcTime can be null, but the value it holds cannot.
+                Objects.requireNonNull(utcTime.getValue());
+            }
+
             mUtcTime = utcTime;
             return this;
         }
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 49187dc..323c7d1 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.Manifest;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -33,8 +34,12 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.UUID;
 
 /**
@@ -771,6 +776,13 @@
     @UnsupportedAppUsage
     public static final String EXTRA_SDP_SEARCH_STATUS =
             "android.bluetooth.device.extra.SDP_SEARCH_STATUS";
+
+    /** @hide */
+    @IntDef(prefix = "ACCESS_", value = {ACCESS_UNKNOWN,
+            ACCESS_ALLOWED, ACCESS_REJECTED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AccessPermission{}
+
     /**
      * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
      * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
@@ -1096,15 +1108,14 @@
 
     /**
      * Get the most recent identified battery level of this Bluetooth device
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      *
      * @return Battery level in percents from 0 to 100, or {@link #BATTERY_LEVEL_UNKNOWN} if
      * Bluetooth is disabled, or device is disconnected, or does not have any battery reporting
      * service, or return value is invalid
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    @UnsupportedAppUsage
     public int getBatteryLevel() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1187,8 +1198,15 @@
         return false;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * Gets whether bonding was initiated locally
+     *
+     * @return true if bonding is initiated locally, false otherwise
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public boolean isBondingInitiatedLocally() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1480,15 +1498,20 @@
         return false;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
-    public boolean setPasskey(int passkey) {
-        //TODO(BT)
-        /*
-        try {
-            return sService.setPasskey(this, true, 4, passkey);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}*/
-        return false;
+    /**
+     * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
+     *
+     * @return true pin has been set false for error
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public boolean setPin(@Nullable String pin) {
+        byte[] pinBytes = convertPinToBytes(pin);
+        if (pinBytes == null) {
+            return false;
+        }
+        return setPin(pinBytes);
     }
 
     /**
@@ -1511,22 +1534,18 @@
         return false;
     }
 
-    /** @hide */
-    public boolean setRemoteOutOfBandData() {
-        // TODO(BT)
-        /*
-        try {
-          return sService.setRemoteOutOfBandData(this);
-      } catch (RemoteException e) {Log.e(TAG, "", e);}*/
-        return false;
-    }
-
-    /** @hide */
-    @UnsupportedAppUsage
-    public boolean cancelPairingUserInput() {
+    /**
+     * Cancels pairing to this device
+     *
+     * @return true if pairing cancelled successfully, false otherwise
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean cancelPairing() {
         final IBluetooth service = sService;
         if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot create pairing user input");
+            Log.e(TAG, "BT not enabled. Cannot cancel pairing");
             return false;
         }
         try {
@@ -1537,17 +1556,6 @@
         return false;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
-    public boolean isBluetoothDock() {
-        // TODO(BT)
-        /*
-        try {
-            return sService.isBluetoothDock(this);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}*/
-        return false;
-    }
-
     boolean isBluetoothEnabled() {
         boolean ret = false;
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1558,13 +1566,14 @@
     }
 
     /**
-     * Requires {@link android.Manifest.permission#BLUETOOTH}.
+     * Gets whether the phonebook access is allowed for this bluetooth device
      *
      * @return Whether the phonebook access is allowed to this device. Can be {@link
      * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public int getPhonebookAccessPermission() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1667,14 +1676,14 @@
     }
 
     /**
-     * Requires {@link android.Manifest.permission#BLUETOOTH}.
+     * Gets whether message access is allowed to this bluetooth device
      *
-     * @return Whether the message access is allowed to this device. Can be {@link #ACCESS_UNKNOWN},
-     * {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
+     * @return Whether the message access is allowed to this device.
      * @hide
      */
-    @UnsupportedAppUsage
-    public int getMessageAccessPermission() {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public @AccessPermission int getMessageAccessPermission() {
         final IBluetooth service = sService;
         if (service == null) {
             return ACCESS_UNKNOWN;
@@ -1689,15 +1698,18 @@
 
     /**
      * Sets whether the message access is allowed to this device.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
      *
-     * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link
-     * #ACCESS_REJECTED}.
+     * @param value is the value we are setting the message access permission to
      * @return Whether the value has been successfully set.
      * @hide
      */
-    @UnsupportedAppUsage
-    public boolean setMessageAccessPermission(int value) {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setMessageAccessPermission(@AccessPermission int value) {
+        // Validates param value is one of the accepted constants
+        if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) {
+            throw new IllegalArgumentException(value + "is not a valid AccessPermission value");
+        }
         final IBluetooth service = sService;
         if (service == null) {
             return false;
@@ -1711,13 +1723,14 @@
     }
 
     /**
-     * Requires {@link android.Manifest.permission#BLUETOOTH}.
+     * Gets whether sim access is allowed for this bluetooth device
      *
-     * @return Whether the Sim access is allowed to this device. Can be {@link #ACCESS_UNKNOWN},
-     * {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
+     * @return Whether the Sim access is allowed to this device.
      * @hide
      */
-    public int getSimAccessPermission() {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public @AccessPermission int getSimAccessPermission() {
         final IBluetooth service = sService;
         if (service == null) {
             return ACCESS_UNKNOWN;
@@ -1732,14 +1745,14 @@
 
     /**
      * Sets whether the Sim access is allowed to this device.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
      *
      * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link
      * #ACCESS_REJECTED}.
      * @return Whether the value has been successfully set.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setSimAccessPermission(int value) {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1970,7 +1983,7 @@
      * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin.
      * @hide
      */
-    @UnsupportedAppUsage
+    @VisibleForTesting
     public static byte[] convertPinToBytes(String pin) {
         if (pin == null) {
             return null;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d370a38..7b580c3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4973,7 +4973,7 @@
      * {@link android.content.pm.DataLoaderManager}.
      * @hide
      */
-    public static final String DATA_LOADER_MANAGER_SERVICE = "dataloadermanager";
+    public static final String DATA_LOADER_MANAGER_SERVICE = "dataloader_manager";
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve an
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7815a33..ca2de6a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4000,9 +4000,15 @@
      * Broadcast Action: The sim card state has changed.
      * For more details see TelephonyIntents.ACTION_SIM_STATE_CHANGED. This is here
      * because TelephonyIntents is an internal class.
-     * @hide
+     * The intent will have following extras.</p>
+     * <p>
+     * @see #EXTRA_SIM_STATE
+     * @see #EXTRA_SIM_LOCKED_REASON
+     *
      * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or
      * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     *
+     * @hide
      */
     @Deprecated
     @SystemApi
@@ -4010,6 +4016,170 @@
     public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
 
     /**
+     * The extra used with {@link #ACTION_SIM_STATE_CHANGED} for broadcasting SIM STATE.
+     * This will have one of the following intent values.
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_NOT_READY
+     * @see #SIM_STATE_ABSENT
+     * @see #SIM_STATE_PRESENT
+     * @see #SIM_STATE_CARD_IO_ERROR
+     * @see #SIM_STATE_CARD_RESTRICTED
+     * @see #SIM_STATE_LOCKED
+     * @see #SIM_STATE_READY
+     * @see #SIM_STATE_IMSI
+     * @see #SIM_STATE_LOADED
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_SIM_STATE = "ss";
+
+    /**
+     * The intent value UNKNOWN represents the SIM state unknown
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_STATE_UNKNOWN = "UNKNOWN";
+
+    /**
+     * The intent value NOT_READY means that the SIM is not ready eg. radio is off or powering on
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_STATE_NOT_READY = "NOT_READY";
+
+    /**
+     * The intent value ABSENT means the SIM card is missing
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_STATE_ABSENT = "ABSENT";
+
+    /**
+     * The intent value PRESENT means the device has a SIM card inserted
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_STATE_PRESENT = "PRESENT";
+
+    /**
+     * The intent value CARD_IO_ERROR means for three consecutive times there was SIM IO error
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    static public final String SIM_STATE_CARD_IO_ERROR = "CARD_IO_ERROR";
+
+    /**
+     * The intent value CARD_RESTRICTED means card is present but not usable due to carrier
+     * restrictions
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    static public final String SIM_STATE_CARD_RESTRICTED = "CARD_RESTRICTED";
+
+    /**
+     * The intent value LOCKED means the SIM is locked by PIN or by network
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_STATE_LOCKED = "LOCKED";
+
+    /**
+     * The intent value READY means the SIM is ready to be accessed
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_STATE_READY = "READY";
+
+    /**
+     * The intent value IMSI means the SIM IMSI is ready in property
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_STATE_IMSI = "IMSI";
+
+    /**
+     * The intent value LOADED means all SIM records, including IMSI, are loaded
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_STATE_LOADED = "LOADED";
+
+    /**
+     * The extra used with {@link #ACTION_SIM_STATE_CHANGED} for broadcasting SIM STATE.
+     * This extra will have one of the following intent values.
+     * <p>
+     * @see #SIM_LOCKED_ON_PIN
+     * @see #SIM_LOCKED_ON_PUK
+     * @see #SIM_LOCKED_NETWORK
+     * @see #SIM_ABSENT_ON_PERM_DISABLED
+     *
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String EXTRA_SIM_LOCKED_REASON = "reason";
+
+    /**
+     * The intent value PIN means the SIM is locked on PIN1
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_LOCKED_ON_PIN = "PIN";
+
+    /**
+     * The intent value PUK means the SIM is locked on PUK1
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     */
+    /* PUK means ICC is locked on PUK1 */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_LOCKED_ON_PUK = "PUK";
+
+    /**
+     * The intent value NETWORK means the SIM is locked on NETWORK PERSONALIZATION
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_LOCKED_NETWORK = "NETWORK";
+
+    /**
+     * The intent value PERM_DISABLED means SIM is permanently disabled due to puk fails
+     * @hide
+     * @deprecated Use {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+     */
+    @Deprecated
+    @SystemApi
+    public static final String SIM_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
+
+    /**
      * Broadcast Action: indicate that the phone service state has changed.
      * The intent will have the following extra values:</p>
      * <p>
diff --git a/core/java/android/content/pm/ParceledListSlice.aidl b/core/java/android/content/pm/ParceledListSlice.aidl
index c02cc6a..5031fba 100644
--- a/core/java/android/content/pm/ParceledListSlice.aidl
+++ b/core/java/android/content/pm/ParceledListSlice.aidl
@@ -16,4 +16,4 @@
 
 package android.content.pm;
 
-parcelable ParceledListSlice;
+parcelable ParceledListSlice<T>;
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
index 35df474..515185e 100644
--- a/core/java/android/content/pm/parsing/AndroidPackage.java
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -229,11 +229,6 @@
 
     String getOverlayTargetName();
 
-    /**
-     * Map of overlayable name to actor name.
-     */
-    Map<String, String> getOverlayables();
-
     // TODO(b/135203078): Does this and getAppInfoPackageName have to be separate methods?
     //  The refactor makes them the same value with no known consequences, so should be redundant.
     String getPackageName();
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index ffddbb6..edbf73a0 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -52,7 +52,6 @@
 import android.content.pm.split.DefaultSplitAssetLoader;
 import android.content.pm.split.SplitAssetDependencyLoader;
 import android.content.pm.split.SplitAssetLoader;
-import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -93,7 +92,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 /** @hide */
@@ -289,23 +287,8 @@
                                 + result.getErrorMessage());
             }
 
-            ParsingPackage pkg = result.getResultAndNull();
-            ApkAssets apkAssets = assets.getApkAssets()[0];
-            if (apkAssets.definesOverlayable()) {
-                SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
-                int size = packageNames.size();
-                for (int index = 0; index < size; index++) {
-                    String packageName = packageNames.get(index);
-                    Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
-                    if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
-                        for (String overlayable : overlayableToActor.keySet()) {
-                            pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
-                        }
-                    }
-                }
-            }
-
-            return pkg.setVolumeUuid(volumeUuid)
+            return result.getResultAndNull()
+                    .setVolumeUuid(volumeUuid)
                     .setApplicationVolumeUuid(volumeUuid)
                     .setSigningDetails(SigningDetails.UNKNOWN);
         } catch (PackageParserException e) {
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 0e736d5..377279e 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -18,8 +18,6 @@
 
 import static android.os.Build.VERSION_CODES.DONUT;
 
-import static java.util.Collections.emptyMap;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Intent;
@@ -57,13 +55,11 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.CollectionUtils;
 import com.android.server.SystemConfig;
 
 import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -130,7 +126,6 @@
     private String overlayCategory;
     private int overlayPriority;
     private boolean overlayIsStatic;
-    private Map<String, String> overlayables = emptyMap();
 
     private String staticSharedLibName;
     private long staticSharedLibVersion;
@@ -480,7 +475,7 @@
 
     @Override
     public Map<String, ArraySet<PublicKey>> getKeySetMapping() {
-        return keySetMapping == null ? emptyMap() : keySetMapping;
+        return keySetMapping == null ? Collections.emptyMap() : keySetMapping;
     }
 
     @Override
@@ -778,13 +773,6 @@
     }
 
     @Override
-    public ParsingPackage addOverlayable(String overlayableName, String actorName) {
-        this.overlayables = CollectionUtils.add(this.overlayables,
-                TextUtils.safeIntern(overlayableName), TextUtils.safeIntern(actorName));
-        return this;
-    }
-
-    @Override
     public PackageImpl addAdoptPermission(String adoptPermission) {
         this.adoptPermissions = ArrayUtils.add(this.adoptPermissions, adoptPermission);
         return this;
@@ -2137,11 +2125,6 @@
     }
 
     @Override
-    public Map<String, String> getOverlayables() {
-        return overlayables;
-    }
-
-    @Override
     public boolean isOverlayIsStatic() {
         return overlayIsStatic;
     }
@@ -2308,7 +2291,7 @@
         appInfo.metaData = appMetaData;
         appInfo.minAspectRatio = minAspectRatio;
         appInfo.minSdkVersion = minSdkVersion;
-        appInfo.name = className;
+        appInfo.name = name;
         if (appInfo.name != null) {
             appInfo.name = appInfo.name.trim();
         }
@@ -2974,7 +2957,6 @@
         dest.writeString(this.overlayCategory);
         dest.writeInt(this.overlayPriority);
         dest.writeBoolean(this.overlayIsStatic);
-        dest.writeMap(this.overlayables);
         dest.writeString(this.staticSharedLibName);
         dest.writeLong(this.staticSharedLibVersion);
         dest.writeStringList(this.libraryNames);
@@ -3118,8 +3100,6 @@
         this.overlayCategory = in.readString();
         this.overlayPriority = in.readInt();
         this.overlayIsStatic = in.readBoolean();
-        this.overlayables = new HashMap<>();
-        in.readMap(overlayables, boot);
         this.staticSharedLibName = TextUtils.safeIntern(in.readString());
         this.staticSharedLibVersion = in.readLong();
         this.libraryNames = in.createStringArrayList();
diff --git a/core/java/android/content/pm/parsing/PackageInfoUtils.java b/core/java/android/content/pm/parsing/PackageInfoUtils.java
index 28f0bc4..af9ba8d 100644
--- a/core/java/android/content/pm/parsing/PackageInfoUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoUtils.java
@@ -436,7 +436,7 @@
         ii.functionalTest = i.functionalTest;
 
         ii.sourceDir = pkg.getBaseCodePath();
-        ii.publicSourceDir = pkg.getCodePath();
+        ii.publicSourceDir = pkg.getBaseCodePath();
         ii.splitNames = pkg.getSplitNames();
         ii.splitSourceDirs = pkg.getSplitCodePaths();
         ii.splitPublicSourceDirs = pkg.getSplitCodePaths();
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index aff1b2e..43c1f6e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -62,8 +62,6 @@
 
     ParsingPackage addOriginalPackage(String originalPackage);
 
-    ParsingPackage addOverlayable(String overlayableName, String actorName);
-
     ParsingPackage addPermission(ParsedPermission permission);
 
     ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup);
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 090629f..fa5ad39 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -452,6 +452,9 @@
         if ((diff & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
             list.add("CONFIG_SMALLEST_SCREEN_SIZE");
         }
+        if ((diff & ActivityInfo.CONFIG_DENSITY) != 0) {
+            list.add("CONFIG_DENSITY");
+        }
         if ((diff & ActivityInfo.CONFIG_LAYOUT_DIRECTION) != 0) {
             list.add("CONFIG_LAYOUT_DIRECTION");
         }
@@ -461,6 +464,9 @@
         if ((diff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
             list.add("CONFIG_ASSETS_PATHS");
         }
+        if ((diff & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0) {
+            list.add("CONFIG_WINDOW_CONFIGURATION");
+        }
         StringBuilder builder = new StringBuilder("{");
         for (int i = 0, n = list.size(); i < n; i++) {
             builder.append(list.get(i));
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 2698c2d..c698267 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1859,6 +1859,7 @@
             mResId = other.mResId == null ? null : other.mResId.clone();
             mForce = other.mForce == null ? null : other.mForce.clone();
             mCount = other.mCount;
+            mHashCode = other.mHashCode;
         }
 
         @Override
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 84489cf..e4a4a1a 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -344,7 +344,7 @@
         try {
             return mAssets.openNonAssetFd(tempValue.assetCookie, tempValue.string.toString());
         } catch (Exception e) {
-            throw new NotFoundException("File " + tempValue.string.toString() + " from drawable "
+            throw new NotFoundException("File " + tempValue.string.toString() + " from "
                     + "resource ID #0x" + Integer.toHexString(id), e);
         }
     }
@@ -359,7 +359,7 @@
             // Note: value.string might be null
             NotFoundException rnf = new NotFoundException("File "
                     + (value.string == null ? "(null)" : value.string.toString())
-                    + " from drawable resource ID #0x" + Integer.toHexString(id));
+                    + " from resource ID #0x" + Integer.toHexString(id));
             rnf.initCause(e);
             throw rnf;
         }
diff --git a/core/java/android/hardware/soundtrigger/ModelParams.aidl b/core/java/android/hardware/soundtrigger/ModelParams.aidl
new file mode 100644
index 0000000..d90dc81
--- /dev/null
+++ b/core/java/android/hardware/soundtrigger/ModelParams.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.soundtrigger;
+
+/**
+ * Model specific parameters to be used with parameter set and get APIs
+ * {@hide}
+ */
+@Backing(type="int")
+enum ModelParams {
+  /**
+   * Placeholder for invalid model parameter used for returning error or
+   * passing an invalid value.
+   */
+  INVALID = -1,
+  /**
+   * Controls the sensitivity threshold adjustment factor for a given model.
+   * Negative value corresponds to less sensitive model (high threshold) and
+   * a positive value corresponds to a more sensitive model (low threshold).
+   * Default value is 0.
+   */
+  THRESHOLD_FACTOR = 0,
+}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.aidl b/core/java/android/hardware/soundtrigger/SoundTrigger.aidl
index 325a9ad..94c4216 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.aidl
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.aidl
@@ -24,5 +24,6 @@
 parcelable SoundTrigger.KeyphraseRecognitionExtra;
 parcelable SoundTrigger.KeyphraseSoundModel;
 parcelable SoundTrigger.GenericSoundModel;
+parcelable SoundTrigger.ModelParamRange;
 parcelable SoundTrigger.ModuleProperties;
 parcelable SoundTrigger.RecognitionConfig;
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index b1134e1..86f3eec 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -567,6 +567,65 @@
         }
     }
 
+    /*****************************************************************************
+     * A ModelParamRange is a representation of supported parameter range for a
+     * given loaded model.
+     ****************************************************************************/
+    public static final class ModelParamRange implements Parcelable {
+
+        /**
+         * start of supported range inclusive
+         */
+        public final int start;
+
+        /**
+         * end of supported range inclusive
+         */
+        public final int end;
+
+        ModelParamRange(int start, int end) {
+            this.start = start;
+            this.end = end;
+        }
+
+        private ModelParamRange(@NonNull Parcel in) {
+            this.start = in.readInt();
+            this.end = in.readInt();
+        }
+
+        @NonNull
+        public static final Creator<ModelParamRange> CREATOR = new Creator<ModelParamRange>() {
+            @Override
+            @NonNull
+            public ModelParamRange createFromParcel(@NonNull Parcel in) {
+                return new ModelParamRange(in);
+            }
+
+            @Override
+            @NonNull
+            public ModelParamRange[] newArray(int size) {
+                return new ModelParamRange[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(start);
+            dest.writeInt(end);
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "ModelParamRange [start=" + start + ", end=" + end + "]";
+        }
+    }
+
     /**
      *  Modes for key phrase recognition
      */
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 9113548..b16ef5c 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -16,7 +16,9 @@
 
 package android.hardware.soundtrigger;
 
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
+import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -150,6 +152,57 @@
      */
     public native int getModelState(int soundModelHandle);
 
+    /**
+     * Set a model specific {@link ModelParams} with the given value. This
+     * parameter will keep its value for the duration the model is loaded regardless of starting and
+     * stopping recognition. Once the model is unloaded, the value will be lost.
+     * {@link SoundTriggerModule#isParameterSupported} should be checked first before calling this
+     * method.
+     *
+     * @param soundModelHandle handle of model to apply parameter
+     * @param modelParam   {@link ModelParams}
+     * @param value        Value to set
+     * @return - {@link SoundTrigger#STATUS_OK} in case of success
+     *         - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+     *         - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
+     *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
+     *           if API is not supported by HAL
+     */
+    public native int setParameter(int soundModelHandle,
+            @ModelParams int modelParam, int value);
+
+    /**
+     * Get a model specific {@link ModelParams}. This parameter will keep its value
+     * for the duration the model is loaded regardless of starting and stopping recognition.
+     * Once the model is unloaded, the value will be lost. If the value is not set, a default
+     * value is returned. See {@link ModelParams} for parameter default values.
+     * {@link SoundTriggerModule#isParameterSupported} should be checked first before
+     * calling this method. Otherwise, an exception can be thrown.
+     *
+     * @param soundModelHandle handle of model to get parameter
+     * @param modelParam   {@link ModelParams}
+     * @return value of parameter
+     * @throws UnsupportedOperationException if hal or model do not support this API.
+     *         {@link SoundTriggerModule#isParameterSupported} should be checked first.
+     * @throws IllegalArgumentException if invalid model handle or parameter is passed.
+     *         {@link SoundTriggerModule#isParameterSupported} should be checked first.
+     */
+    public native int getParameter(int soundModelHandle,
+            @ModelParams int modelParam)
+            throws UnsupportedOperationException, IllegalArgumentException;
+
+    /**
+     * Determine if parameter control is supported for the given model handle.
+     * This method should be checked prior to calling {@link SoundTriggerModule#setParameter} or
+     * {@link SoundTriggerModule#getParameter}.
+     *
+     * @param soundModelHandle handle of model to get parameter
+     * @param modelParam {@link ModelParams}
+     * @return supported range of parameter, null if not supported
+     */
+    @Nullable
+    public native ModelParamRange queryParameter(int soundModelHandle, @ModelParams int modelParam);
+
     private class NativeEventHandlerDelegate {
         private final Handler mHandler;
 
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index adc497a..2992127 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
@@ -461,6 +462,14 @@
         return networkCapabilities.hasTransport(transportType);
     }
 
+    /**
+     * @see Builder#setNetworkSpecifier(NetworkSpecifier)
+     */
+    @Nullable
+    public NetworkSpecifier getNetworkSpecifier() {
+        return networkCapabilities.getNetworkSpecifier();
+    }
+
     public String toString() {
         return "NetworkRequest [ " + type + " id=" + requestId +
                 (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 9fed269..4754444 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2422,7 +2422,7 @@
 
     public static final int DATA_CONNECTION_OUT_OF_SERVICE = 0;
     public static final int DATA_CONNECTION_EMERGENCY_SERVICE =
-            TelephonyManager.MAX_NETWORK_TYPE + 1;
+            TelephonyManager.getAllNetworkTypes().length + 1;
     public static final int DATA_CONNECTION_OTHER = DATA_CONNECTION_EMERGENCY_SERVICE + 1;
 
 
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 6408f61..ebb2071 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -519,11 +519,12 @@
      * @param invokeWith null-ok the command to invoke with.
      * @param packageName null-ok the name of the package this process belongs to.
      * @param isTopApp whether the process starts for high priority application.
-     *
+     * @param disabledCompatChanges null-ok list of disabled compat changes for the process being
+     *                             started.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
      * @throws RuntimeException on fatal start failure
-     * 
+     *
      * {@hide}
      */
     public static ProcessStartResult start(@NonNull final String processClass,
@@ -539,11 +540,12 @@
                                            @Nullable String invokeWith,
                                            @Nullable String packageName,
                                            boolean isTopApp,
+                                           @Nullable long[] disabledCompatChanges,
                                            @Nullable String[] zygoteArgs) {
         return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
-                    /*useUsapPool=*/ true, isTopApp, zygoteArgs);
+                    /*useUsapPool=*/ true, isTopApp, disabledCompatChanges, zygoteArgs);
     }
 
     /** @hide */
@@ -559,11 +561,12 @@
                                                   @Nullable String appDataDir,
                                                   @Nullable String invokeWith,
                                                   @Nullable String packageName,
+                                                  @Nullable long[] disabledCompatChanges,
                                                   @Nullable String[] zygoteArgs) {
         return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
-                    /*useUsapPool=*/ false, /*isTopApp=*/ false, zygoteArgs);
+                    /*useUsapPool=*/ false, /*isTopApp=*/ false, disabledCompatChanges, zygoteArgs);
     }
 
     /**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 907eae8..d17a5e0 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -306,6 +306,8 @@
      * @param appDataDir null-ok the data directory of the app.
      * @param invokeWith null-ok the command to invoke with.
      * @param packageName null-ok the name of the package this process belongs to.
+     * @param disabledCompatChanges null-ok list of disabled compat changes for the process being
+     *                             started.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      * @param isTopApp Whether the process starts for high priority application.
      *
@@ -325,6 +327,7 @@
                                                   @Nullable String packageName,
                                                   boolean useUsapPool,
                                                   boolean isTopApp,
+                                                  @Nullable long[] disabledCompatChanges,
                                                   @Nullable String[] zygoteArgs) {
         // TODO (chriswailes): Is there a better place to check this value?
         if (fetchUsapPoolEnabledPropWithMinInterval()) {
@@ -335,7 +338,7 @@
             return startViaZygote(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
-                    packageName, useUsapPool, isTopApp, zygoteArgs);
+                    packageName, useUsapPool, isTopApp, disabledCompatChanges, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -535,6 +538,7 @@
      * that has its state cloned from this zygote process.
      * @param packageName null-ok the name of the package this process belongs to.
      * @param isTopApp Whether the process starts for high priority application.
+     * @param disabledCompatChanges a list of disabled compat changes for the process being started.
      * @param extraArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
      * @throws ZygoteStartFailedEx if process start failed for any reason
@@ -554,6 +558,7 @@
                                                       @Nullable String packageName,
                                                       boolean useUsapPool,
                                                       boolean isTopApp,
+                                                      @Nullable long[] disabledCompatChanges,
                                                       @Nullable String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
         ArrayList<String> argsForZygote = new ArrayList<>();
@@ -584,10 +589,10 @@
 
         // --setgroups is a comma-separated list
         if (gids != null && gids.length > 0) {
-            StringBuilder sb = new StringBuilder();
+            final StringBuilder sb = new StringBuilder();
             sb.append("--setgroups=");
 
-            int sz = gids.length;
+            final int sz = gids.length;
             for (int i = 0; i < sz; i++) {
                 if (i != 0) {
                     sb.append(',');
@@ -631,6 +636,21 @@
             argsForZygote.add(Zygote.START_AS_TOP_APP_ARG);
         }
 
+        if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("--disabled-compat-changes=");
+
+            int sz = disabledCompatChanges.length;
+            for (int i = 0; i < sz; i++) {
+                if (i != 0) {
+                    sb.append(',');
+                }
+                sb.append(disabledCompatChanges[i]);
+            }
+
+            argsForZygote.add(sb.toString());
+        }
+
         argsForZygote.add(processClass);
 
         if (extraArgs != null) {
@@ -1166,7 +1186,8 @@
                     gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
                     abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
                     true /* startChildZygote */, null /* packageName */,
-                    false /* useUsapPool */, false /* isTopApp */, extraArgs);
+                    false /* useUsapPool */, false /* isTopApp */,
+                    null /* disabledCompatChanges */, extraArgs);
         } catch (ZygoteStartFailedEx ex) {
             throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
         }
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 14c299d..5cac5f5 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -109,4 +109,13 @@
      */
     public abstract void onAppOpsChanged(int code, int uid,
             @Nullable String packageName, int mode);
+
+    /**
+     * Asks the StorageManager to reset all state for the provided user; this will result
+     * in the unmounting for all volumes of the user, and, if the user is still running, the
+     * volumes will be re-mounted as well.
+     *
+     * @param userId the userId for which to reset storage
+     */
+    public abstract void resetUser(int userId);
 }
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 8f765fa..f914663 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -221,7 +221,7 @@
      * permission controller package.
      */
     @BinderThread
-    public void onUpdateUserSensitive() {
+    public void onUpdateUserSensitivePermissionFlags() {
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
 
@@ -449,7 +449,7 @@
             public void updateUserSensitive(AndroidFuture callback) {
                 Preconditions.checkNotNull(callback, "callback cannot be null");
 
-                onUpdateUserSensitive();
+                onUpdateUserSensitivePermissionFlags();
                 callback.complete(null);
             }
         };
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index f10e184..8bf723f 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -17,6 +17,8 @@
 package android.provider;
 
 import android.accounts.Account;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
@@ -2902,6 +2904,40 @@
         }
 
         /**
+         * The default value used for {@link #ACCOUNT_NAME} of raw contacts when they are inserted
+         * without a value for this column.
+         *
+         * <p>This account is used to identify contacts that are only stored locally in the
+         * contacts database instead of being associated with an {@link Account} managed by an
+         * installed application.
+         *
+         * <p>When this returns null then {@link #getLocalAccountType} will also return null and
+         * when it is non-null {@link #getLocalAccountType} will also return a non-null value.
+         */
+        @Nullable
+        public static String getLocalAccountName(@NonNull Context context) {
+            return TextUtils.nullIfEmpty(context.getString(
+                    com.android.internal.R.string.config_rawContactsLocalAccountName));
+        }
+
+        /**
+         * The default value used for {@link #ACCOUNT_TYPE} of raw contacts when they are inserted
+         * without a value for this column.
+         *
+         * <p>This account is used to identify contacts that are only stored locally in the
+         * contacts database instead of being associated with an {@link Account} managed by an
+         * installed application.
+         *
+         * <p>When this returns null then {@link #getLocalAccountName} will also return null and
+         * when it is non-null {@link #getLocalAccountName} will also return a non-null value.
+         */
+        @Nullable
+        public static String getLocalAccountType(@NonNull Context context) {
+            return TextUtils.nullIfEmpty(context.getString(
+                    com.android.internal.R.string.config_rawContactsLocalAccountType));
+        }
+
+        /**
          * A sub-directory of a single raw contact that contains all of its
          * {@link ContactsContract.Data} rows. To access this directory
          * append {@link Data#CONTENT_DIRECTORY} to the raw contact URI.
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index abf34ca..f8825ed 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -357,6 +357,13 @@
     @TestApi
     public static final String NAMESPACE_PERMISSIONS = "permissions";
 
+    /**
+     * Namespace for all widget related features.
+     *
+     * @hide
+     */
+    public static final String NAMESPACE_WIDGET = "widget";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
@@ -571,11 +578,13 @@
      * none or all of this update is picked up, but never only part of it.
      *
      * @param properties the complete set of properties to set for a specific namespace.
+     * @throws BadConfigException if the provided properties are banned by RescueParty.
+     * @return True if the values were set, false otherwise.
      * @hide
      */
     @SystemApi
     @RequiresPermission(WRITE_DEVICE_CONFIG)
-    public static boolean setProperties(@NonNull Properties properties) {
+    public static boolean setProperties(@NonNull Properties properties) throws BadConfigException {
         ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
         return Settings.Config.setStrings(contentResolver, properties.getNamespace(),
                 properties.mMap);
@@ -792,6 +801,15 @@
     }
 
     /**
+     * Thrown by {@link #setProperties(Properties)} when a configuration is rejected. This
+     * happens if RescueParty has identified a bad configuration and reset the namespace.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static class BadConfigException extends Exception {}
+
+    /**
      * A mapping of properties to values, as well as a single namespace which they all belong to.
      *
      * @hide
@@ -927,4 +945,5 @@
             }
         }
     }
+
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ad8d553..165c284 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -648,6 +648,22 @@
             "android.settings.NIGHT_DISPLAY_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of Dark theme.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DARK_THEME_SETTINGS =
+            "android.settings.DARK_THEME_SETTINGS";
+
+    /**
      * Activity Action: Show settings to allow configuration of locale.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 70c8e5d..f6da01b 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.ChangeId;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -4032,6 +4033,16 @@
         @Retention(RetentionPolicy.SOURCE)
         public @interface Skip464XlatStatus {}
 
+        /**
+         * Compat framework change ID for the APN db read permission change.
+         *
+         * In API level 30 and beyond, accessing the APN database will require the
+         * {@link android.Manifest.permission#WRITE_APN_SETTINGS} permission. This change ID tracks
+         * apps that are affected because they don't hold this permission.
+         * @hide
+         */
+        @ChangeId
+        public static final long APN_READING_PERMISSION_CHANGE_ID = 124107808L;
     }
 
     /**
diff --git a/core/java/android/service/controls/BooleanAction.java b/core/java/android/service/controls/BooleanAction.java
index 8508c63..877f82e 100644
--- a/core/java/android/service/controls/BooleanAction.java
+++ b/core/java/android/service/controls/BooleanAction.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
 import android.os.Parcel;
 
 /**
@@ -26,6 +27,8 @@
  */
 public final class BooleanAction extends ControlAction {
 
+    private static final String KEY_NEW_STATE = "key_new_state";
+
     private final boolean mNewState;
 
     /**
@@ -49,9 +52,9 @@
         mNewState = newValue;
     }
 
-    BooleanAction(Parcel in) {
-        super(in);
-        mNewState = in.readByte() == 1;
+    BooleanAction(Bundle b) {
+        super(b);
+        mNewState = b.getBoolean(KEY_NEW_STATE);
     }
 
     /**
@@ -72,17 +75,17 @@
         return ControlAction.TYPE_BOOLEAN;
     }
 
-
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeByte(mNewState ? (byte) 1 : (byte) 0);
+    protected Bundle getDataBundle() {
+        Bundle b =  super.getDataBundle();
+        b.putBoolean(KEY_NEW_STATE, mNewState);
+        return b;
     }
 
     public static final @NonNull Creator<BooleanAction> CREATOR = new Creator<BooleanAction>() {
         @Override
         public BooleanAction createFromParcel(Parcel source) {
-            return new BooleanAction(source);
+            return new BooleanAction(source.readBundle());
         }
 
         @Override
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
index a69408c..2c17e89 100644
--- a/core/java/android/service/controls/Control.java
+++ b/core/java/android/service/controls/Control.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.PendingIntent;
 import android.content.Intent;
 import android.content.res.ColorStateList;
 import android.graphics.drawable.Icon;
@@ -55,7 +56,7 @@
     private final @NonNull Icon mIcon;
     private final @NonNull CharSequence mTitle;
     private final @Nullable ColorStateList mTintColor;
-    private final @NonNull Intent mAppIntent;
+    private final @NonNull PendingIntent mAppIntent;
     private final @ControlTemplate.TemplateType int mPrimaryType;
 
     /**
@@ -64,14 +65,15 @@
      * @param title the user facing name of this control (e.g. "Bedroom thermostat").
      * @param tintColor the color to tint parts of the element UI. If {@code null} is passed, the
      *                  system accent color will be used.
-     * @param appIntent an intent linking to a page to interact with the corresponding device.
+     * @param appIntent a {@link PendingIntent} linking to a page to interact with the
+     *                  corresponding device.
      * @param primaryType the primary template for this type.
      */
     public Control(@NonNull String controlId,
             @NonNull Icon icon,
             @NonNull CharSequence title,
             @Nullable ColorStateList tintColor,
-            @NonNull Intent appIntent,
+            @NonNull PendingIntent appIntent,
             int primaryType) {
         Preconditions.checkNotNull(controlId);
         Preconditions.checkNotNull(icon);
@@ -94,7 +96,7 @@
         } else {
             mTintColor = null;
         }
-        mAppIntent = Intent.CREATOR.createFromParcel(in);
+        mAppIntent = PendingIntent.CREATOR.createFromParcel(in);
         mPrimaryType = in.readInt();
     }
 
@@ -119,7 +121,7 @@
     }
 
     @NonNull
-    public Intent getAppIntent() {
+    public PendingIntent getAppIntent() {
         return mAppIntent;
     }
 
@@ -175,17 +177,17 @@
         private Icon mIcon;
         private CharSequence mTitle = "";
         private ColorStateList mTintColor;
-        private @Nullable Intent mAppIntent;
+        private @Nullable PendingIntent mAppIntent;
         private @ControlTemplate.TemplateType int mPrimaryType = ControlTemplate.TYPE_NONE;
 
         /**
          * @param controlId the identifier for the {@link Control}.
          * @param icon the icon for the {@link Control}.
-         * @param appIntent the intent linking to the device Activity.
+         * @param appIntent the pending intent linking to the device Activity.
          */
         public Builder(@NonNull String controlId,
                 @NonNull Icon icon,
-                @NonNull Intent appIntent) {
+                @NonNull PendingIntent appIntent) {
             Preconditions.checkNotNull(controlId);
             Preconditions.checkNotNull(icon);
             Preconditions.checkNotNull(appIntent);
@@ -256,7 +258,7 @@
          * @return {@code this}
          */
         @NonNull
-        public Builder setAppIntent(@NonNull Intent appIntent) {
+        public Builder setAppIntent(@NonNull PendingIntent appIntent) {
             Preconditions.checkNotNull(appIntent);
             mAppIntent = appIntent;
             return this;
diff --git a/core/java/android/service/controls/ControlAction.java b/core/java/android/service/controls/ControlAction.java
index 8b75955..0a7c97c 100644
--- a/core/java/android/service/controls/ControlAction.java
+++ b/core/java/android/service/controls/ControlAction.java
@@ -16,9 +16,11 @@
 
 package android.service.controls;
 
+import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -36,25 +38,38 @@
  */
 public abstract class ControlAction implements Parcelable {
 
+    private static final String KEY_TEMPLATE_ID = "key_template_id";
+    private static final String KEY_CHALLENGE_VALUE = "key_challenge_value";
+
+    public static final ControlAction UNKNOWN_ACTION = new ControlAction() {
+
+        @Override
+        public int getActionType() {
+            return TYPE_UNKNOWN;
+        }
+    };
+
     /**
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
+            TYPE_UNKNOWN,
             TYPE_BOOLEAN,
             TYPE_FLOAT
     })
     public @interface ActionType {};
 
+    public static final @ActionType int TYPE_UNKNOWN = 0;
     /**
      * The identifier of {@link BooleanAction}.
      */
-    public static final @ActionType int TYPE_BOOLEAN = 0;
+    public static final @ActionType int TYPE_BOOLEAN = 1;
 
     /**
      * The identifier of {@link FloatAction}.
      */
-    public static final @ActionType int TYPE_FLOAT = 1;
+    public static final @ActionType int TYPE_FLOAT = 2;
 
     /**
      * @hide
@@ -120,13 +135,9 @@
     /**
      * @hide
      */
-    ControlAction(Parcel in) {
-        mTemplateId = in.readString();
-        if (in.readByte() == 1) {
-            mChallengeValue = in.readString();
-        } else {
-            mChallengeValue = null;
-        }
+    ControlAction(Bundle b) {
+        mTemplateId = b.getString(KEY_TEMPLATE_ID);
+        mChallengeValue = b.getString(KEY_CHALLENGE_VALUE);
     }
 
     /**
@@ -151,15 +162,24 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public final void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(getActionType());
-        dest.writeString(mTemplateId);
-        if (mChallengeValue != null) {
-            dest.writeByte((byte) 1);
-            dest.writeString(mChallengeValue);
-        } else {
-            dest.writeByte((byte) 0);
-        }
+        dest.writeBundle(getDataBundle());
+    }
+
+    /**
+     * Obtain a {@link Bundle} describing this object populated with data.
+     *
+     * Implementations in subclasses should populate the {@link Bundle} returned by
+     * {@link ControlAction}.
+     * @return a {@link Bundle} containing the data that represents this object.
+     */
+    @CallSuper
+    protected Bundle getDataBundle() {
+        Bundle b = new Bundle();
+        b.putString(KEY_TEMPLATE_ID, mTemplateId);
+        b.putString(KEY_CHALLENGE_VALUE, mChallengeValue);
+        return b;
     }
 
     public static final @NonNull Creator<ControlAction> CREATOR = new Creator<ControlAction>() {
@@ -175,6 +195,7 @@
         }
     };
 
+
     private static ControlAction createActionFromType(@ActionType int type, Parcel source) {
         switch(type) {
             case TYPE_BOOLEAN:
@@ -182,7 +203,8 @@
             case TYPE_FLOAT:
                 return FloatAction.CREATOR.createFromParcel(source);
             default:
-                return null;
+                source.readBundle();
+                return UNKNOWN_ACTION;
         }
     }
 
diff --git a/core/java/android/service/controls/ControlButton.java b/core/java/android/service/controls/ControlButton.java
index fed3115..969c0a7 100644
--- a/core/java/android/service/controls/ControlButton.java
+++ b/core/java/android/service/controls/ControlButton.java
@@ -29,29 +29,29 @@
  */
 public class ControlButton implements Parcelable {
 
-    private final boolean mActive;
+    private final boolean mChecked;
     private final @NonNull Icon mIcon;
     private final @NonNull CharSequence mContentDescription;
 
     /**
-     * @param active true if the button should be rendered as active.
+     * @param checked true if the button should be rendered as active.
      * @param icon icon to display in the button.
      * @param contentDescription content description for the button.
      */
-    public ControlButton(boolean active, @NonNull Icon icon,
+    public ControlButton(boolean checked, @NonNull Icon icon,
             @NonNull CharSequence contentDescription) {
         Preconditions.checkNotNull(icon);
         Preconditions.checkNotNull(contentDescription);
-        mActive = active;
+        mChecked = checked;
         mIcon = icon;
         mContentDescription = contentDescription;
     }
 
     /**
-     * Whether the button should be rendered in its active state.
+     * Whether the button should be rendered in a checked state.
      */
-    public boolean isActive() {
-        return mActive;
+    public boolean isChecked() {
+        return mChecked;
     }
 
     /**
@@ -78,13 +78,13 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByte(mActive ? (byte) 1 : (byte) 0);
+        dest.writeByte(mChecked ? (byte) 1 : (byte) 0);
         mIcon.writeToParcel(dest, flags);
         dest.writeCharSequence(mContentDescription);
     }
 
     ControlButton(Parcel in) {
-        mActive = in.readByte() != 0;
+        mChecked = in.readByte() != 0;
         mIcon = Icon.CREATOR.createFromParcel(in);
         mContentDescription = in.readCharSequence();
     }
diff --git a/core/java/android/service/controls/ControlState.java b/core/java/android/service/controls/ControlState.java
index 804aef7..f2410a8 100644
--- a/core/java/android/service/controls/ControlState.java
+++ b/core/java/android/service/controls/ControlState.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.PendingIntent;
 import android.content.res.ColorStateList;
 import android.graphics.drawable.Icon;
 import android.os.Parcel;
@@ -33,11 +34,11 @@
  * Current state for a {@link Control}.
  *
  * Collects information to render the current state of a {@link Control} as well as possible action
- * that can be performed on it. Some of the information may temporarily override the defaults
- * provided by the corresponding {@link Control}, while this state is being displayed.
- *
- * Additionally, this can be used to modify information related to the corresponding
- * {@link Control}.
+ * that can be performed on it.
+ * <p>
+ * Additionally, this object is used to modify elements from the {@link Control} such as icons,
+ * colors, names and intents. This information will last until it is again modified by a
+ * {@link ControlState}.
  * @hide
  */
 public final class ControlState implements Parcelable {
@@ -74,58 +75,81 @@
      */
     public static final int STATUS_DISABLED = 3;
 
-    private final @NonNull Control mControl;
+    private final @NonNull String mControlId;
     private final @Status int mStatus;
     private final @NonNull ControlTemplate mControlTemplate;
     private final @NonNull CharSequence mStatusText;
-    private final @Nullable Icon mOverrideIcon;
-    private final @Nullable ColorStateList mOverrideTint;
+    private final @Nullable CharSequence mTitle;
+    private final @Nullable PendingIntent mAppIntent;
+    private final @Nullable Icon mIcon;
+    private final @Nullable ColorStateList mTint;
 
     /**
-     * @param control the {@link Control} this state should be applied to. Can be used to
-     *                       update information about the {@link Control}
+     * @param controlId the identifier of the {@link Control} this object refers to.
      * @param status the current status of the {@link Control}.
-     * @param controlTemplate the template to be used to render the {@link Control}.
-     * @param statusText the text describing the current status.
-     * @param overrideIcon the icon to temporarily override the one provided in
-     *                     {@link Control#getIcon()}. Pass {@code null} to use the icon in
-     *                     {@link Control#getIcon()}.
-     * @param overrideTint the colors to temporarily override those provided in
-     *                            {@link Control#getTint()}. Pass {@code null} to use the colors in
-     *                            {@link Control#getTint()}.
+     * @param controlTemplate the template to be used to render the {@link Control}. This can be
+     *                        of a different
+     *                        {@link android.service.controls.ControlTemplate.TemplateType} than the
+     *                        one defined in {@link Control#getPrimaryType}
+     * @param statusText the user facing text describing the current status.
+     * @param title the title to replace the one set in the {@link Control} or set in the
+     *              last {@link ControlState}. Pass {@code null} to use the last value set for this
+     *              {@link Control}
+     * @param appIntent the {@link PendingIntent} to replace the one set in the {@link Control} or
+     *                  set in the last {@link ControlState}. Pass {@code null} to use the last
+     *                  value set for this {@link Control}.
+     * @param icon the icon to replace the one set in the {@link Control} or set in the last
+     *             {@link ControlState}. Pass {@code null} to use the last value set for this
+     *             {@link Control}.
+     * @param tint the colors to replace those set in the {@link Control} or set in the last
+     *             {@link ControlState}. Pass {@code null} to use the last value set for this
+     *             {@link Control}.
      */
-    public ControlState(@NonNull Control control,
+    public ControlState(@NonNull String controlId,
             int status,
             @NonNull ControlTemplate controlTemplate,
             @NonNull CharSequence statusText,
-            @Nullable Icon overrideIcon,
-            @Nullable ColorStateList overrideTint) {
-        Preconditions.checkNotNull(control);
+            @Nullable CharSequence title,
+            @Nullable PendingIntent appIntent,
+            @Nullable Icon icon,
+            @Nullable ColorStateList tint) {
+        Preconditions.checkNotNull(controlId);
         Preconditions.checkNotNull(controlTemplate);
         Preconditions.checkNotNull(statusText);
-
-        mControl = control;
+        mControlId = controlId;
         mStatus = status;
         mControlTemplate = controlTemplate;
-        mOverrideIcon = overrideIcon;
         mStatusText = statusText;
-        mOverrideTint = overrideTint;
+        mTitle = title;
+        mAppIntent = appIntent;
+        mIcon = icon;
+        mTint = tint;
     }
 
     ControlState(Parcel in) {
-        mControl = Control.CREATOR.createFromParcel(in);
+        mControlId = in.readString();
         mStatus = in.readInt();
         mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
         mStatusText = in.readCharSequence();
         if (in.readByte() == 1) {
-            mOverrideIcon = Icon.CREATOR.createFromParcel(in);
+            mTitle = in.readCharSequence();
         } else {
-            mOverrideIcon = null;
+            mTitle = null;
         }
         if (in.readByte() == 1) {
-            mOverrideTint = ColorStateList.CREATOR.createFromParcel(in);
+            mAppIntent = PendingIntent.CREATOR.createFromParcel(in);
         } else {
-            mOverrideTint = null;
+            mAppIntent = null;
+        }
+        if (in.readByte() == 1) {
+            mIcon = Icon.CREATOR.createFromParcel(in);
+        } else {
+            mIcon = null;
+        }
+        if (in.readByte() == 1) {
+            mTint = ColorStateList.CREATOR.createFromParcel(in);
+        } else {
+            mTint = null;
         }
     }
 
@@ -134,6 +158,21 @@
         return 0;
     }
 
+    @NonNull
+    public String getControlId() {
+        return mControlId;
+    }
+
+    @Nullable
+    public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    @Nullable
+    public PendingIntent getAppIntent() {
+        return mAppIntent;
+    }
+
     @Status
     public int getStatus() {
         return mStatus;
@@ -145,8 +184,8 @@
     }
 
     @Nullable
-    public Icon getOverrideIcon() {
-        return mOverrideIcon;
+    public Icon getIcon() {
+        return mIcon;
     }
 
     @NonNull
@@ -155,35 +194,35 @@
     }
 
     @Nullable
-    public ColorStateList getOverrideTint() {
-        return mOverrideTint;
-    }
-
-    @NonNull
-    public Control getControl() {
-        return mControl;
-    }
-
-    @NonNull
-    public String getControlId() {
-        return mControl.getControlId();
+    public ColorStateList getTint() {
+        return mTint;
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        mControl.writeToParcel(dest, flags);
+        dest.writeString(mControlId);
         dest.writeInt(mStatus);
         mControlTemplate.writeToParcel(dest, flags);
         dest.writeCharSequence(mStatusText);
-        if (mOverrideIcon != null) {
+        if (mTitle != null) {
             dest.writeByte((byte) 1);
-            mOverrideIcon.writeToParcel(dest, flags);
+            dest.writeCharSequence(mTitle);
         } else {
             dest.writeByte((byte) 0);
         }
-        if (mOverrideTint != null) {
+        if (mAppIntent != null) {
             dest.writeByte((byte) 1);
-            mOverrideTint.writeToParcel(dest, flags);
+            mAppIntent.writeToParcel(dest, flags);
+        }
+        if (mIcon != null) {
+            dest.writeByte((byte) 1);
+            mIcon.writeToParcel(dest, flags);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+        if (mTint != null) {
+            dest.writeByte((byte) 1);
+            mTint.writeToParcel(dest, flags);
         } else {
             dest.writeByte((byte) 0);
         }
@@ -213,19 +252,22 @@
      * </ul>
      */
     public static class Builder {
-        private @NonNull Control mControl;
+        private @NonNull String mControlId;
         private @Status int mStatus = STATUS_OK;
         private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
         private @NonNull CharSequence mStatusText = "";
-        private @Nullable Icon mOverrideIcon;
-        private @Nullable ColorStateList mOverrideTint;
+        private @Nullable CharSequence mTitle;
+        private @Nullable PendingIntent mAppIntent;
+        private @Nullable Icon mIcon;
+        private @Nullable ColorStateList mTint;
 
         /**
-         * @param control the {@link Control} that the resulting {@link ControlState} refers to.
+         * @param controlId the identifier of the {@link Control} that the resulting
+         *                  {@link ControlState} refers to.
          */
-        public Builder(@NonNull Control control) {
-            Preconditions.checkNotNull(control);
-            mControl = control;
+        public Builder(@NonNull String controlId) {
+            Preconditions.checkNotNull(controlId);
+            mControlId = controlId;
         }
 
         /**
@@ -234,21 +276,24 @@
          */
         public Builder(@NonNull ControlState controlState) {
             Preconditions.checkNotNull(controlState);
-            mControl = controlState.mControl;
+            mControlId = controlState.mControlId;
+            mStatus = controlState.mStatus;
             mControlTemplate = controlState.mControlTemplate;
-            mOverrideIcon = controlState.mOverrideIcon;
             mStatusText = controlState.mStatusText;
-            mOverrideTint = controlState.mOverrideTint;
+            mTitle = controlState.mTitle;
+            mAppIntent = controlState.mAppIntent;
+            mIcon = controlState.mIcon;
+            mTint = controlState.mTint;
         }
 
 
         /**
-         * @param control the updated {@link Control} information.
+         * @param controlId the identifier of the {@link Control} for the resulting object.
          * @return {@code this}
          */
         @NonNull
-        public Builder setControl(@NonNull Control control) {
-            mControl = control;
+        public Builder setControlId(@NonNull String controlId) {
+            mControlId = controlId;
             return this;
         }
 
@@ -285,24 +330,50 @@
         }
 
         /**
-         * @param overrideIcon the icon to override the one defined in the corresponding
-         *                            {@code Control}. Pass {@code null} to remove the override.
+         * @param title the title to replace the one defined in the corresponding {@link Control} or
+         *              set by the last {@link ControlState}. Pass {@code null} to keep the last
+         *              value.
          * @return {@code this}
          */
         @NonNull
-        public Builder setOverrideIcon(@Nullable Icon overrideIcon) {
-            mOverrideIcon = overrideIcon;
+        public Builder setTitle(@Nullable CharSequence title) {
+            mTitle = title;
             return this;
         }
 
         /**
-         * @param overrideTint the colors to override the ones defined in the corresponding
-         *                            {@code Control}. Pass {@code null} to remove the override.
+         * @param appIntent the Pending Intent to replace the one defined in the corresponding
+         *                  {@link Control} or set by the last {@link ControlState}. Pass
+         *                  {@code null} to keep the last value.
          * @return {@code this}
          */
         @NonNull
-        public Builder setOverrideTint(@Nullable ColorStateList overrideTint) {
-            mOverrideTint = overrideTint;
+        public Builder setAppIntent(@Nullable PendingIntent appIntent) {
+            mAppIntent = appIntent;
+            return this;
+        }
+
+        /**
+         * @param icon the title to replace the one defined in the corresponding {@link Control} or
+         *             set by the last {@link ControlState}. Pass {@code null} to keep the last
+         *             value.
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setIcon(@Nullable Icon icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        /**
+         * @param tint the title to replace the one defined in the corresponding {@link Control} or
+         *             set by the last {@link ControlState}. Pass {@code null} to keep the last
+         *             value.
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setTint(@Nullable ColorStateList tint) {
+            mTint = tint;
             return this;
         }
 
@@ -310,8 +381,8 @@
          * @return a new {@link ControlState}
          */
         public ControlState build() {
-            return new ControlState(mControl, mStatus, mControlTemplate, mStatusText,
-                    mOverrideIcon, mOverrideTint);
+            return new ControlState(mControlId, mStatus, mControlTemplate, mStatusText,
+                    mTitle, mAppIntent, mIcon, mTint);
         }
     }
 }
diff --git a/core/java/android/service/controls/ControlTemplate.java b/core/java/android/service/controls/ControlTemplate.java
index e559862..8bcabd6 100644
--- a/core/java/android/service/controls/ControlTemplate.java
+++ b/core/java/android/service/controls/ControlTemplate.java
@@ -16,8 +16,10 @@
 
 package android.service.controls;
 
+import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -40,6 +42,8 @@
  */
 public abstract class ControlTemplate implements Parcelable {
 
+    private static final String KEY_TEMPLATE_ID = "key_template_id";
+
     /**
      * Singleton representing a {@link Control} with no input.
      */
@@ -114,17 +118,28 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public final void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(getTemplateType());
-        dest.writeString(mTemplateId);
+        dest.writeBundle(getDataBundle());
+    }
+
+    /**
+     * Obtain a {@link Bundle} describing this object populated with data.
+     * @return a {@link Bundle} containing the data that represents this object.
+     */
+    @CallSuper
+    protected Bundle getDataBundle() {
+        Bundle b = new Bundle();
+        b.putString(KEY_TEMPLATE_ID, mTemplateId);
+        return b;
     }
 
     private ControlTemplate() {
         mTemplateId = "";
     }
 
-    ControlTemplate(Parcel in) {
-        mTemplateId = in.readString();
+    ControlTemplate(@NonNull Bundle b) {
+        mTemplateId = b.getString(KEY_TEMPLATE_ID);
     }
 
     /**
@@ -148,6 +163,7 @@
         }
     };
 
+
     private static ControlTemplate createTemplateFromType(@TemplateType int type, Parcel source) {
         switch(type) {
             case TYPE_TOGGLE:
@@ -159,9 +175,9 @@
             case TYPE_DISCRETE_TOGGLE:
                 return DiscreteToggleTemplate.CREATOR.createFromParcel(source);
             case TYPE_NONE:
-                return NO_TEMPLATE;
             default:
-                return null;
+                source.readBundle();
+                return NO_TEMPLATE;
         }
     }
 }
diff --git a/core/java/android/service/controls/DiscreteToggleTemplate.java b/core/java/android/service/controls/DiscreteToggleTemplate.java
index 5167af4..5718252 100644
--- a/core/java/android/service/controls/DiscreteToggleTemplate.java
+++ b/core/java/android/service/controls/DiscreteToggleTemplate.java
@@ -17,6 +17,7 @@
 package android.service.controls;
 
 import android.annotation.NonNull;
+import android.os.Bundle;
 import android.os.Parcel;
 
 import com.android.internal.util.Preconditions;
@@ -34,6 +35,9 @@
  */
 public class DiscreteToggleTemplate extends ControlTemplate {
 
+    private static final String KEY_NEGATIVE_BUTTON = "key_negative_button";
+    private static final String KEY_POSITIVE_BUTTON = "key_positive_button";
+
     private final @NonNull ControlButton mNegativeButton;
     private final @NonNull ControlButton mPositiveButton;
 
@@ -52,10 +56,10 @@
         mPositiveButton = positiveButton;
     }
 
-    DiscreteToggleTemplate(Parcel in) {
-        super(in);
-        this.mNegativeButton = ControlButton.CREATOR.createFromParcel(in);
-        this.mPositiveButton = ControlButton.CREATOR.createFromParcel(in);
+    DiscreteToggleTemplate(Bundle b) {
+        super(b);
+        mNegativeButton = b.getParcelable(KEY_NEGATIVE_BUTTON);
+        mPositiveButton = b.getParcelable(KEY_POSITIVE_BUTTON);
     }
 
     /**
@@ -89,17 +93,18 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        mNegativeButton.writeToParcel(dest, flags);
-        mPositiveButton.writeToParcel(dest, flags);
+    protected Bundle getDataBundle() {
+        Bundle b = super.getDataBundle();
+        b.putObject(KEY_NEGATIVE_BUTTON, mNegativeButton);
+        b.putObject(KEY_POSITIVE_BUTTON, mPositiveButton);
+        return b;
     }
 
     public static final Creator<DiscreteToggleTemplate> CREATOR =
             new Creator<DiscreteToggleTemplate>() {
                 @Override
                 public DiscreteToggleTemplate createFromParcel(Parcel source) {
-                    return new DiscreteToggleTemplate(source);
+                    return new DiscreteToggleTemplate(source.readBundle());
                 }
 
                 @Override
diff --git a/core/java/android/service/controls/FloatAction.java b/core/java/android/service/controls/FloatAction.java
index fe6db10..229435f 100644
--- a/core/java/android/service/controls/FloatAction.java
+++ b/core/java/android/service/controls/FloatAction.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
 import android.os.Parcel;
 
 /**
@@ -26,6 +27,8 @@
  */
 public final class FloatAction extends ControlAction {
 
+    private static final String KEY_NEW_VALUE = "key_new_value";
+
     private final float mNewValue;
 
     /**
@@ -50,9 +53,9 @@
         mNewValue = newValue;
     }
 
-    public FloatAction(Parcel in) {
-        super(in);
-        mNewValue = in.readFloat();
+    public FloatAction(Bundle b) {
+        super(b);
+        mNewValue = b.getFloat(KEY_NEW_VALUE);
     }
 
     /**
@@ -71,15 +74,16 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeFloat(mNewValue);
+    protected Bundle getDataBundle() {
+        Bundle b = super.getDataBundle();
+        b.putFloat(KEY_NEW_VALUE, mNewValue);
+        return b;
     }
 
     public static final @NonNull Creator<FloatAction> CREATOR = new Creator<FloatAction>() {
         @Override
         public FloatAction createFromParcel(Parcel source) {
-            return new FloatAction(source);
+            return new FloatAction(source.readBundle());
         }
 
         @Override
diff --git a/core/java/android/service/controls/RangeTemplate.java b/core/java/android/service/controls/RangeTemplate.java
index 70bf2dd..f0bce30 100644
--- a/core/java/android/service/controls/RangeTemplate.java
+++ b/core/java/android/service/controls/RangeTemplate.java
@@ -18,10 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
 import android.os.Parcel;
 
-import com.android.internal.util.Preconditions;
-
 import java.security.InvalidParameterException;
 
 /**
@@ -32,6 +31,12 @@
  */
 public final class RangeTemplate extends ControlTemplate {
 
+    private static final String KEY_MIN_VALUE = "key_min_value";
+    private static final String KEY_MAX_VALUE = "key_max_value";
+    private static final String KEY_CURRENT_VALUE = "key_current_value";
+    private static final String KEY_STEP_VALUE = "key_step_value";
+    private static final String KEY_FORMAT_STRING = "key_format_string";
+
     private final float mMinValue;
     private final float mMaxValue;
     private final float mCurrentValue;
@@ -67,7 +72,6 @@
             float stepValue,
             @Nullable CharSequence formatString) {
         super(templateId);
-        Preconditions.checkNotNull(formatString);
         mMinValue = minValue;
         mMaxValue = maxValue;
         mCurrentValue = currentValue;
@@ -87,13 +91,13 @@
      * @see RangeTemplate#RangeTemplate(String, float, float, float, float, CharSequence)
      * @hide
      */
-    RangeTemplate(Parcel in) {
-        super(in);
-        mMinValue = in.readFloat();
-        mMaxValue = in.readFloat();
-        mCurrentValue = in.readFloat();
-        mStepValue = in.readFloat();
-        mFormatString = in.readCharSequence();
+    RangeTemplate(Bundle b) {
+        super(b);
+        mMinValue = b.getFloat(KEY_MIN_VALUE);
+        mMaxValue = b.getFloat(KEY_MAX_VALUE);
+        mCurrentValue = b.getFloat(KEY_CURRENT_VALUE);
+        mStepValue = b.getFloat(KEY_STEP_VALUE);
+        mFormatString = b.getCharSequence(KEY_FORMAT_STRING, "%.1f");
         validate();
     }
 
@@ -144,13 +148,14 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeFloat(mMinValue);
-        dest.writeFloat(mMaxValue);
-        dest.writeFloat(mCurrentValue);
-        dest.writeFloat(mStepValue);
-        dest.writeCharSequence(mFormatString);
+    protected Bundle getDataBundle() {
+        Bundle b = super.getDataBundle();
+        b.putFloat(KEY_MIN_VALUE, mMinValue);
+        b.putFloat(KEY_MAX_VALUE, mMaxValue);
+        b.putFloat(KEY_CURRENT_VALUE, mCurrentValue);
+        b.putFloat(KEY_STEP_VALUE, mStepValue);
+        b.putCharSequence(KEY_FORMAT_STRING, mFormatString);
+        return b;
     }
 
     /**
@@ -179,7 +184,7 @@
     public static final Creator<RangeTemplate> CREATOR = new Creator<RangeTemplate>() {
         @Override
         public RangeTemplate createFromParcel(Parcel source) {
-            return new RangeTemplate(source);
+            return new RangeTemplate(source.readBundle());
         }
 
         @Override
diff --git a/core/java/android/service/controls/ThumbnailTemplate.java b/core/java/android/service/controls/ThumbnailTemplate.java
index 796d2de..6e729c0 100644
--- a/core/java/android/service/controls/ThumbnailTemplate.java
+++ b/core/java/android/service/controls/ThumbnailTemplate.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.graphics.drawable.Icon;
+import android.os.Bundle;
 import android.os.Parcel;
 
 import com.android.internal.util.Preconditions;
@@ -28,6 +29,9 @@
  */
 public final class ThumbnailTemplate extends ControlTemplate {
 
+    private static final String KEY_ICON = "key_icon";
+    private static final String KEY_CONTENT_DESCRIPTION = "key_content_description";
+
     private final @NonNull Icon mThumbnail;
     private final @NonNull CharSequence mContentDescription;
 
@@ -45,10 +49,10 @@
         mContentDescription = contentDescription;
     }
 
-    ThumbnailTemplate(Parcel in) {
-        super(in);
-        mThumbnail = Icon.CREATOR.createFromParcel(in);
-        mContentDescription = in.readCharSequence();
+    ThumbnailTemplate(Bundle b) {
+        super(b);
+        mThumbnail = b.getParcelable(KEY_ICON);
+        mContentDescription = b.getCharSequence(KEY_CONTENT_DESCRIPTION, "");
     }
 
     /**
@@ -74,17 +78,19 @@
     public int getTemplateType() {
         return TYPE_THUMBNAIL;
     }
+
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        mThumbnail.writeToParcel(dest, flags);
-        dest.writeCharSequence(mContentDescription);
+    protected Bundle getDataBundle() {
+        Bundle b = super.getDataBundle();
+        b.putObject(KEY_ICON, mThumbnail);
+        b.putObject(KEY_CONTENT_DESCRIPTION, mContentDescription);
+        return b;
     }
 
     public static final Creator<ThumbnailTemplate> CREATOR = new Creator<ThumbnailTemplate>() {
         @Override
         public ThumbnailTemplate createFromParcel(Parcel source) {
-            return new ThumbnailTemplate(source);
+            return new ThumbnailTemplate(source.readBundle());
         }
 
         @Override
diff --git a/core/java/android/service/controls/ToggleTemplate.java b/core/java/android/service/controls/ToggleTemplate.java
index 3766bd1..4c4fd5e 100644
--- a/core/java/android/service/controls/ToggleTemplate.java
+++ b/core/java/android/service/controls/ToggleTemplate.java
@@ -17,6 +17,7 @@
 package android.service.controls;
 
 import android.annotation.NonNull;
+import android.os.Bundle;
 import android.os.Parcel;
 
 import com.android.internal.util.Preconditions;
@@ -24,7 +25,7 @@
 /**
  * A template for a {@link Control} with a single button that can be toggled between two states.
  *
- * The states for the toggle correspond to the states in {@link ControlButton#isActive()}.
+ * The states for the toggle correspond to the states in {@link ControlButton#isChecked()}.
  * An action on this template will originate a {@link BooleanAction} to change that state.
  *
  * @see BooleanAction
@@ -32,6 +33,7 @@
  */
 public final class ToggleTemplate extends ControlTemplate {
 
+    private static final String KEY_BUTTON = "key_button";
     private final @NonNull ControlButton mButton;
 
     /**
@@ -44,9 +46,9 @@
         mButton = button;
     }
 
-    ToggleTemplate(Parcel in) {
-        super(in);
-        mButton = ControlButton.CREATOR.createFromParcel(in);
+    ToggleTemplate(Bundle b) {
+        super(b);
+        mButton = b.getParcelable(KEY_BUTTON);
     }
 
     /**
@@ -66,15 +68,16 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        mButton.writeToParcel(dest, flags);
+    protected Bundle getDataBundle() {
+        Bundle b =  super.getDataBundle();
+        b.putObject(KEY_BUTTON, mButton);
+        return b;
     }
 
     public static final Creator<ToggleTemplate> CREATOR = new Creator<ToggleTemplate>() {
         @Override
         public ToggleTemplate createFromParcel(Parcel source) {
-            return new ToggleTemplate(source);
+            return new ToggleTemplate(source.readBundle());
         }
 
         @Override
diff --git a/core/java/android/service/incremental/IncrementalDataLoaderService.java b/core/java/android/service/incremental/IncrementalDataLoaderService.java
new file mode 100644
index 0000000..c4a06c8
--- /dev/null
+++ b/core/java/android/service/incremental/IncrementalDataLoaderService.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.incremental;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.IDataLoader;
+import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.InstallationFile;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.incremental.IncrementalDataLoaderParams;
+import android.os.incremental.IncrementalDataLoaderParamsParcel;
+import android.os.incremental.IncrementalFileSystemControlParcel;
+import android.os.incremental.NamedParcelFileDescriptor;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.List;
+
+
+/**
+ * The base class for implementing data loader service to control data loaders. Expecting
+ * Incremental Service to bind to a children class of this.
+ *
+ * @hide
+ *
+ * Hide for now, should be @SystemApi
+ * TODO(b/136132412): update with latest API design
+ */
+public abstract class IncrementalDataLoaderService extends Service {
+    private static final String TAG = "IncrementalDataLoaderService";
+    private final DataLoaderBinderService mBinder = new DataLoaderBinderService();
+
+    public static final int DATA_LOADER_READY =
+            IDataLoaderStatusListener.DATA_LOADER_READY;
+    public static final int DATA_LOADER_NOT_READY =
+            IDataLoaderStatusListener.DATA_LOADER_NOT_READY;
+    public static final int DATA_LOADER_RUNNING =
+            IDataLoaderStatusListener.DATA_LOADER_RUNNING;
+    public static final int DATA_LOADER_STOPPED =
+            IDataLoaderStatusListener.DATA_LOADER_STOPPED;
+    public static final int DATA_LOADER_SLOW_CONNECTION =
+            IDataLoaderStatusListener.DATA_LOADER_SLOW_CONNECTION;
+    public static final int DATA_LOADER_NO_CONNECTION =
+            IDataLoaderStatusListener.DATA_LOADER_NO_CONNECTION;
+    public static final int DATA_LOADER_CONNECTION_OK =
+            IDataLoaderStatusListener.DATA_LOADER_CONNECTION_OK;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"DATA_LOADER_"}, value = {
+            DATA_LOADER_READY,
+            DATA_LOADER_NOT_READY,
+            DATA_LOADER_RUNNING,
+            DATA_LOADER_STOPPED,
+            DATA_LOADER_SLOW_CONNECTION,
+            DATA_LOADER_NO_CONNECTION,
+            DATA_LOADER_CONNECTION_OK
+    })
+    public @interface DataLoaderStatus {
+    }
+
+    /**
+     * Incremental FileSystem block size.
+     **/
+    public static final int BLOCK_SIZE = 4096;
+
+    /**
+     * Data compression types
+     */
+    public static final int COMPRESSION_NONE = 0;
+    public static final int COMPRESSION_LZ4 = 1;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({COMPRESSION_NONE, COMPRESSION_LZ4})
+    public @interface CompressionType {
+    }
+
+    /**
+     * Managed DataLoader interface. Each instance corresponds to a single Incremental File System
+     * instance.
+     */
+    public abstract static class DataLoader {
+        /**
+         * A virtual constructor used to do simple initialization. Not ready to serve any data yet.
+         * All heavy-lifting has to be done in onStart.
+         *
+         * @param params    Data loader configuration parameters.
+         * @param connector IncFS API wrapper.
+         * @param listener  Used for reporting internal state to IncrementalService.
+         * @return True if initialization of a Data Loader was successful. False will be reported to
+         * IncrementalService and can cause an unmount of an IFS instance.
+         */
+        public abstract boolean onCreate(@NonNull IncrementalDataLoaderParams params,
+                @NonNull FileSystemConnector connector,
+                @NonNull StatusListener listener);
+
+        /**
+         * Start the data loader. After this method returns data loader is considered to be ready to
+         * receive callbacks from IFS, supply data via connector and send status updates via
+         * callbacks.
+         *
+         * @return True if Data Loader was able to start. False will be reported to
+         * IncrementalService and can cause an unmount of an IFS instance.
+         */
+        public abstract boolean onStart();
+
+        /**
+         * Stop the data loader. Use to stop any additional threads and free up resources. Data
+         * loader is not longer responsible for supplying data. Start/Stop pair can be called
+         * multiple times e.g. if IFS detects corruption and data needs to be re-loaded.
+         */
+        public abstract void onStop();
+
+        /**
+         * Virtual destructor. Use to cleanup all internal state. After this method returns, the
+         * data loader can no longer use connector or callbacks. For any additional operations with
+         * this instance of IFS a new DataLoader will be created using createDataLoader method.
+         */
+        public abstract void onDestroy();
+
+        /**
+         * IFS reports a pending read each time the page needs to be loaded, e.g. missing.
+         *
+         * @param pendingReads array of blocks to load.
+         *
+         * TODO(b/136132412): avoid using collections
+         */
+        public abstract void onPendingReads(
+                @NonNull Collection<FileSystemConnector.PendingReadInfo> pendingReads);
+
+        /**
+         * IFS tracks all reads and reports them using onPageReads.
+         *
+         * @param reads array of blocks.
+         *
+         * TODO(b/136132412): avoid using collections
+         */
+        public abstract void onPageReads(@NonNull Collection<FileSystemConnector.ReadInfo> reads);
+
+        /**
+         * IFS informs data loader that a new file has been created.
+         * <p>
+         * This can be used to prepare the data loader before it starts loading data. For example,
+         * the data loader can keep a list of newly created files, so that it knows what files to
+         * download from the server.
+         *
+         * @param inode    The inode value of the new file.
+         * @param metadata The metadata of the new file.
+         */
+        public abstract void onFileCreated(long inode, byte[] metadata);
+    }
+
+    /**
+     * DataLoader factory method.
+     *
+     * @return An instance of a DataLoader.
+     */
+    public abstract @Nullable DataLoader onCreateDataLoader();
+
+    /**
+     * @hide
+     */
+    public final @NonNull IBinder onBind(@NonNull Intent intent) {
+        return (IBinder) mBinder;
+    }
+
+    private class DataLoaderBinderService extends IDataLoader.Stub {
+        private int mId;
+
+        @Override
+        public void create(int id, @NonNull Bundle options,
+                @NonNull IDataLoaderStatusListener listener)
+                    throws IllegalArgumentException, RuntimeException {
+            mId = id;
+            final IncrementalDataLoaderParamsParcel params =  options.getParcelable("params");
+            if (params == null) {
+                throw new IllegalArgumentException("Must specify Incremental data loader params");
+            }
+            final IncrementalFileSystemControlParcel control =
+                    options.getParcelable("control");
+            if (control == null) {
+                throw new IllegalArgumentException("Must specify Incremental control parcel");
+            }
+            mStatusListener = listener;
+            try {
+                if (!nativeCreateDataLoader(id, control, params, listener)) {
+                    Slog.e(TAG, "Failed to create native loader for " + mId);
+                }
+            } catch (Exception ex) {
+                destroy();
+                throw new RuntimeException(ex);
+            } finally {
+                // Closing FDs.
+                if (control.cmd != null) {
+                    try {
+                        control.cmd.close();
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Failed to close IncFs CMD file descriptor " + e);
+                    }
+                }
+                if (control.log != null) {
+                    try {
+                        control.log.close();
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Failed to close IncFs LOG file descriptor " + e);
+                    }
+                }
+                NamedParcelFileDescriptor[] fds = params.dynamicArgs;
+                for (NamedParcelFileDescriptor nfd : fds) {
+                    try {
+                        nfd.fd.close();
+                    } catch (IOException e) {
+                        Slog.e(TAG,
+                                "Failed to close DynamicArgs parcel file descriptor " + e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void start(List<InstallationFile> fileInfos) {
+            if (!nativeStartDataLoader(mId)) {
+                Slog.e(TAG, "Failed to start loader: loader not found for " + mId);
+            }
+        }
+
+        @Override
+        public void stop() {
+            if (!nativeStopDataLoader(mId)) {
+                Slog.w(TAG, "Failed to stop loader: loader not found for " + mId);
+            }
+        }
+
+        @Override
+        public void destroy() {
+            if (!nativeDestroyDataLoader(mId)) {
+                Slog.w(TAG, "Failed to destroy loader: loader not found for " + mId);
+            }
+        }
+
+        @Override
+        // TODO(b/136132412): remove this
+        public void onFileCreated(long inode, byte[] metadata) {
+            if (!nativeOnFileCreated(mId, inode, metadata)) {
+                Slog.w(TAG, "Failed to handle onFileCreated for storage:" + mId
+                        + " inode:" + inode);
+            }
+        }
+    }
+
+    /**
+     * IncFs API wrapper for writing pages and getting page missing info. Non-hidden methods are
+     * expected to be called by the IncrementalDataLoaderService implemented by developers.
+     *
+     * @hide
+     *
+     * TODO(b/136132412) Should be @SystemApi
+     */
+    public static final class FileSystemConnector {
+        /**
+         * Defines a block address. A block is the unit of data chunk that IncFs operates with.
+         *
+         * @hide
+         */
+        public static class BlockAddress {
+            /**
+             * Linux inode uniquely identifies file within a single IFS instance.
+             */
+            private final long mFileIno;
+            /**
+             * Index of a 4K block within a file.
+             */
+            private final int mBlockIndex;
+
+            public BlockAddress(long fileIno, int blockIndex) {
+                this.mFileIno = fileIno;
+                this.mBlockIndex = blockIndex;
+            }
+
+            public long getFileIno() {
+                return mFileIno;
+            }
+
+            public int getBlockIndex() {
+                return mBlockIndex;
+            }
+        }
+
+        /**
+         * A block is the unit of data chunk that IncFs operates with.
+         *
+         * @hide
+         */
+        public static class Block extends BlockAddress {
+            /**
+             * Data content of the block.
+             */
+            private final @NonNull byte[] mDataBytes;
+
+            public Block(long fileIno, int blockIndex, @NonNull byte[] dataBytes) {
+                super(fileIno, blockIndex);
+                this.mDataBytes = dataBytes;
+            }
+        }
+
+        /**
+         * Defines a page/block inside a file.
+         */
+        public static class DataBlock extends Block {
+            /**
+             * Compression type of the data block.
+             */
+            private final @CompressionType int mCompressionType;
+
+            public DataBlock(long fileIno, int blockIndex, @NonNull byte[] dataBytes,
+                    @CompressionType int compressionType) {
+                super(fileIno, blockIndex, dataBytes);
+                this.mCompressionType = compressionType;
+            }
+        }
+
+        /**
+         * Defines a hash block for a certain file. A hash block index is the index in an array of
+         * hashes which is the 1-d representation of the hash tree. One DataBlock might be
+         * associated with multiple HashBlocks.
+         */
+        public static class HashBlock extends Block {
+            public HashBlock(long fileIno, int blockIndex, @NonNull byte[] dataBytes) {
+                super(fileIno, blockIndex, dataBytes);
+            }
+        }
+
+        /**
+         * Information about a page that is pending to be read.
+         */
+        public static class PendingReadInfo extends BlockAddress {
+            PendingReadInfo(long fileIno, int blockIndex) {
+                super(fileIno, blockIndex);
+            }
+        }
+
+        /**
+         * Information about a page that is read.
+         */
+        public static class ReadInfo extends BlockAddress {
+            /**
+             * A monotonically increasing read timestamp.
+             */
+            private final long mTimePoint;
+            /**
+             * Number of blocks read starting from blockIndex.
+             */
+            private final int mBlockCount;
+
+            ReadInfo(long timePoint, long fileIno, int firstBlockIndex, int blockCount) {
+                super(fileIno, firstBlockIndex);
+                this.mTimePoint = timePoint;
+                this.mBlockCount = blockCount;
+            }
+
+            public long getTimePoint() {
+                return mTimePoint;
+            }
+
+            public int getBlockCount() {
+                return mBlockCount;
+            }
+        }
+
+        /**
+         * Defines the dynamic information about an IncFs file.
+         */
+        public static class FileInfo {
+            /**
+             * BitSet to show if any block is available at each block index.
+             */
+            private final @NonNull
+            byte[] mBlockBitmap;
+
+            /**
+             * @hide
+             */
+            public FileInfo(@NonNull byte[] blockBitmap) {
+                this.mBlockBitmap = blockBitmap;
+            }
+        }
+
+        /**
+         * Creates a wrapper for a native instance.
+         */
+        FileSystemConnector(long nativeInstance) {
+            mNativeInstance = nativeInstance;
+        }
+
+        /**
+         * Checks whether a range in a file if loaded.
+         *
+         * @param node  inode of the file.
+         * @param start The starting offset of the range.
+         * @param end   The ending offset of the range.
+         * @return True if the file is fully loaded.
+         */
+        public boolean isFileRangeLoaded(long node, long start, long end) {
+            return nativeIsFileRangeLoadedNode(mNativeInstance, node, start, end);
+        }
+
+        /**
+         * Gets the metadata of a file.
+         *
+         * @param node inode of the file.
+         * @return The metadata object.
+         */
+        @NonNull
+        public byte[] getFileMetadata(long node) throws IOException {
+            final byte[] metadata = nativeGetFileMetadataNode(mNativeInstance, node);
+            if (metadata == null || metadata.length == 0) {
+                throw new IOException(
+                        "IncrementalFileSystem failed to obtain metadata for node: " + node);
+            }
+            return metadata;
+        }
+
+        /**
+         * Gets the dynamic information of a file, such as page bitmaps. Can be used to get missing
+         * page indices by the FileSystemConnector.
+         *
+         * @param node inode of the file.
+         * @return Dynamic file info.
+         */
+        @NonNull
+        public FileInfo getDynamicFileInfo(long node) throws IOException {
+            final byte[] blockBitmap = nativeGetFileInfoNode(mNativeInstance, node);
+            if (blockBitmap == null || blockBitmap.length == 0) {
+                throw new IOException(
+                        "IncrementalFileSystem failed to obtain dynamic file info for node: "
+                                + node);
+            }
+            return new FileInfo(blockBitmap);
+        }
+
+        /**
+         * Writes a page's data and/or hashes.
+         *
+         * @param dataBlocks the DataBlock objects that contain data block index and data bytes.
+         * @param hashBlocks the HashBlock objects that contain hash indices and hash bytes.
+         *
+         * TODO(b/136132412): change API to avoid dynamic allocation of data block objects
+         */
+        public void writeMissingData(@NonNull DataBlock[] dataBlocks,
+                @Nullable HashBlock[] hashBlocks) throws IOException {
+            if (!nativeWriteMissingData(mNativeInstance, dataBlocks, hashBlocks)) {
+                throw new IOException("IncrementalFileSystem failed to write missing data.");
+            }
+        }
+
+        /**
+         * Writes the signer block of a file. Expecting the connector to call this when it got
+         * signing data from data loader.
+         *
+         * @param node       the file to be written to.
+         * @param signerData the raw signer data byte array.
+         */
+        public void writeSignerData(long node, @NonNull byte[] signerData)
+                throws IOException {
+            if (!nativeWriteSignerDataNode(mNativeInstance, node, signerData)) {
+                throw new IOException(
+                        "IncrementalFileSystem failed to write signer data of node " + node);
+            }
+        }
+
+        private final long mNativeInstance;
+    }
+
+    /**
+     * Wrapper for native reporting DataLoader statuses.
+     *
+     * @hide
+     *
+     * TODO(b/136132412) Should be @SystemApi
+     */
+    public static final class StatusListener {
+        /**
+         * Creates a wrapper for a native instance.
+         *
+         * @hide
+         */
+        StatusListener(long nativeInstance) {
+            mNativeInstance = nativeInstance;
+        }
+
+        /**
+         * Report the status of DataLoader. Used for system-wide notifications e.g., disabling
+         * applications which rely on this data loader to function properly.
+         *
+         * @param status status to report.
+         * @return True if status was reported successfully.
+         */
+        public boolean onStatusChanged(@DataLoaderStatus int status) {
+            return nativeReportStatus(mNativeInstance, status);
+        }
+
+        private final long mNativeInstance;
+    }
+
+    private IDataLoaderStatusListener mStatusListener = null;
+
+    /* Native methods */
+    private native boolean nativeCreateDataLoader(int storageId,
+            @NonNull IncrementalFileSystemControlParcel control,
+            @NonNull IncrementalDataLoaderParamsParcel params,
+            IDataLoaderStatusListener listener);
+
+    private native boolean nativeStartDataLoader(int storageId);
+
+    private native boolean nativeStopDataLoader(int storageId);
+
+    private native boolean nativeDestroyDataLoader(int storageId);
+
+    private static native boolean nativeOnFileCreated(int storageId,
+            long inode, byte[] metadata);
+
+    private static native boolean nativeIsFileRangeLoadedNode(
+            long nativeInstance, long node, long start, long end);
+
+    private static native boolean nativeWriteMissingData(
+            long nativeInstance, FileSystemConnector.DataBlock[] dataBlocks,
+            FileSystemConnector.HashBlock[] hashBlocks);
+
+    private static native boolean nativeWriteSignerDataNode(
+            long nativeInstance, long node, byte[] signerData);
+
+    private static native byte[] nativeGetFileMetadataNode(
+            long nativeInstance, long node);
+
+    private static native byte[] nativeGetFileInfoNode(
+            long nativeInstance, long node);
+
+    private static native boolean nativeReportStatus(long nativeInstance, int status);
+}
diff --git a/core/java/android/service/sms/FinancialSmsService.java b/core/java/android/service/sms/FinancialSmsService.java
deleted file mode 100644
index 5fb7249..0000000
--- a/core/java/android/service/sms/FinancialSmsService.java
+++ /dev/null
@@ -1,104 +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.service.sms;
-
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.app.Service;
-import android.content.Intent;
-import android.database.CursorWindow;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteCallback;
-import android.os.RemoteException;
-
-/**
- * A service to support sms messages read for financial apps.
- *
- * {@hide}
- */
-@SystemApi
-public abstract class FinancialSmsService extends Service {
-
-    private static final String TAG = "FinancialSmsService";
-
-    /**
-     * The {@link Intent} action that must be declared as handled by a service
-     * in its manifest for the system to recognize it as a quota providing
-     * service.
-     */
-    public static final String ACTION_FINANCIAL_SERVICE_INTENT =
-            "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
-
-    /** {@hide} **/
-    public static final String EXTRA_SMS_MSGS = "sms_messages";
-
-    private FinancialSmsServiceWrapper mWrapper;
-
-    private void getSmsMessages(RemoteCallback callback, Bundle params) {
-        final Bundle data = new Bundle();
-        CursorWindow smsMessages = onGetSmsMessages(params);
-        if (smsMessages != null) {
-            data.putParcelable(EXTRA_SMS_MSGS, smsMessages);
-        }
-        callback.sendResult(data);
-    }
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
-
-    /** @hide */
-    public FinancialSmsService() {
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mWrapper = new FinancialSmsServiceWrapper();
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mWrapper;
-    }
-
-    /**
-     * Get sms messages for financial apps.
-     *
-     * @param params parameters passed in by the calling app.
-     * @return the {@code CursorWindow} with all sms messages for the app to read.
-     *
-     * {@hide}
-     */
-    @Nullable
-    @SystemApi
-    public abstract CursorWindow onGetSmsMessages(@NonNull Bundle params);
-
-    private final class FinancialSmsServiceWrapper extends IFinancialSmsService.Stub {
-        @Override
-        public void getSmsMessages(RemoteCallback callback, Bundle params) throws RemoteException {
-            mHandler.sendMessage(obtainMessage(
-                    FinancialSmsService::getSmsMessages,
-                    FinancialSmsService.this,
-                    callback, params));
-        }
-    }
-
-}
diff --git a/core/java/android/service/sms/IFinancialSmsService.aidl b/core/java/android/service/sms/IFinancialSmsService.aidl
deleted file mode 100644
index caabe58..0000000
--- a/core/java/android/service/sms/IFinancialSmsService.aidl
+++ /dev/null
@@ -1,30 +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.service.sms;
-
-import android.os.Bundle;
-import android.os.RemoteCallback;
-
-/**
- * Service used by financial apps to read sms messages.
- *
- * @hide
- */
-oneway interface IFinancialSmsService
-{
-    void getSmsMessages(in RemoteCallback callback, in Bundle params);
-}
\ No newline at end of file
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 20dc2be..8dca69f 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -37,7 +37,6 @@
 import android.os.Looper;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
 import android.view.textclassifier.ConversationActions;
@@ -437,28 +436,16 @@
     /**
      * Returns the component name of the system default textclassifier service if it can be found
      * on the system. Otherwise, returns null.
-     * @hide
-     */
-    public static ComponentName getServiceComponentName(@NonNull Context context) {
-        return getServiceComponentName(context, new TextClassificationConstants(
-                () -> Settings.Global.getString(
-                        context.getContentResolver(),
-                        Settings.Global.TEXT_CLASSIFIER_CONSTANTS)));
-    }
-
-    /**
-     * Returns the component name of the system default textclassifier service if it can be found
-     * on the system. Otherwise, returns null.
-     * @param context the text classification context
-     * @param settings TextClassifier specific settings.
      *
+     * @param context the text classification context
      * @hide
      */
     @Nullable
-    public static ComponentName getServiceComponentName(@NonNull Context context,
-            @NonNull TextClassificationConstants settings) {
+    public static ComponentName getServiceComponentName(@NonNull Context context) {
+        final TextClassificationConstants settings = TextClassificationManager.getSettings(context);
         // get override TextClassifierService package name
-        String packageName = settings.getTextClassifierServiceName();
+        String packageName = settings.getTextClassifierServicePackageOverride();
+
         ComponentName serviceComponent = null;
         final boolean isOverrideService = !TextUtils.isEmpty(packageName);
         if (isOverrideService) {
@@ -468,7 +455,7 @@
         if (serviceComponent != null) {
             return serviceComponent;
         }
-        // If no TextClassifierService overrode or invalid override package name, read the first
+        // If no TextClassifierService override or invalid override package name, read the first
         // package defined in the config
         final String[] packages = context.getPackageManager().getSystemTextClassifierPackages();
         if (packages.length == 0 || TextUtils.isEmpty(packages[0])) {
diff --git a/core/java/android/telephony/CellBroadcastIntents.java b/core/java/android/telephony/CellBroadcastIntents.java
index 4474f3e..8446253 100644
--- a/core/java/android/telephony/CellBroadcastIntents.java
+++ b/core/java/android/telephony/CellBroadcastIntents.java
@@ -26,7 +26,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
-import android.util.Log;
 
 /**
  * A static helper class used to send Intents with prepopulated flags.
@@ -75,20 +74,20 @@
             @Nullable String receiverAppOp, @Nullable BroadcastReceiver resultReceiver,
             @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
             @Nullable Bundle initialExtras) {
-        Log.d(LOG_TAG, "sendOrderedBroadcastForBackgroundReceivers intent=" + intent.getAction());
         int status = context.checkCallingOrSelfPermission(
                 "android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS");
         if (status == PackageManager.PERMISSION_DENIED) {
             throw new SecurityException(
                     "Caller does not have permission to send broadcast for background receivers");
         }
-        intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        Intent backgroundIntent = new Intent(intent);
+        backgroundIntent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         if (user != null) {
-            context.createContextAsUser(user, 0).sendOrderedBroadcast(intent, receiverPermission,
-                    receiverAppOp, resultReceiver, scheduler, initialCode, initialData,
-                    initialExtras);
+            context.createContextAsUser(user, 0).sendOrderedBroadcast(backgroundIntent,
+                    receiverPermission, receiverAppOp, resultReceiver, scheduler, initialCode,
+                    initialData, initialExtras);
         } else {
-            context.sendOrderedBroadcast(intent, receiverPermission,
+            context.sendOrderedBroadcast(backgroundIntent, receiverPermission,
                     receiverAppOp, resultReceiver, scheduler, initialCode, initialData,
                     initialExtras);
         }
diff --git a/core/java/android/util/CloseGuard.java b/core/java/android/util/CloseGuard.java
new file mode 100644
index 0000000..c39a6c9
--- /dev/null
+++ b/core/java/android/util/CloseGuard.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.NonNull;
+
+/**
+ * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
+ * resources that should have been cleaned up by explicit close
+ * methods (aka "explicit termination methods" in Effective Java).
+ * <p>
+ * A simple example: <pre>   {@code
+ *   class Foo {
+ *
+ *       private final CloseGuard guard = CloseGuard.get();
+ *
+ *       ...
+ *
+ *       public Foo() {
+ *           ...;
+ *           guard.open("cleanup");
+ *       }
+ *
+ *       public void cleanup() {
+ *          guard.close();
+ *          ...;
+ *       }
+ *
+ *       protected void finalize() throws Throwable {
+ *           try {
+ *               // Note that guard could be null if the constructor threw.
+ *               if (guard != null) {
+ *                   guard.warnIfOpen();
+ *               }
+ *               cleanup();
+ *           } finally {
+ *               super.finalize();
+ *           }
+ *       }
+ *   }
+ * }</pre>
+ *
+ * In usage where the resource to be explicitly cleaned up is
+ * allocated after object construction, CloseGuard protection can
+ * be deferred. For example: <pre>   {@code
+ *   class Bar {
+ *
+ *       private final CloseGuard guard = CloseGuard.get();
+ *
+ *       ...
+ *
+ *       public Bar() {
+ *           ...;
+ *       }
+ *
+ *       public void connect() {
+ *          ...;
+ *          guard.open("cleanup");
+ *       }
+ *
+ *       public void cleanup() {
+ *          guard.close();
+ *          ...;
+ *          Reference.reachabilityFence(this);
+ *          // For full correctness in the absence of a close() call, other methods may also need
+ *          // reachabilityFence() calls.
+ *       }
+ *
+ *       protected void finalize() throws Throwable {
+ *           try {
+ *               // Note that guard could be null if the constructor threw.
+ *               if (guard != null) {
+ *                   guard.warnIfOpen();
+ *               }
+ *               cleanup();
+ *           } finally {
+ *               super.finalize();
+ *           }
+ *       }
+ *   }
+ * }</pre>
+ *
+ * When used in a constructor, calls to {@code open} should occur at
+ * the end of the constructor since an exception that would cause
+ * abrupt termination of the constructor will mean that the user will
+ * not have a reference to the object to cleanup explicitly. When used
+ * in a method, the call to {@code open} should occur just after
+ * resource acquisition.
+ */
+public final class CloseGuard {
+    private final dalvik.system.CloseGuard mImpl;
+
+    /**
+     * Constructs a new CloseGuard instance.
+     * {@link #open(String)} can be used to set up the instance to warn on failure to close.
+     */
+    public CloseGuard() {
+        mImpl = dalvik.system.CloseGuard.get();
+    }
+
+    /**
+     * Initializes the instance with a warning that the caller should have explicitly called the
+     * {@code closeMethodName} method instead of relying on finalization.
+     *
+     * @param closeMethodName non-null name of explicit termination method. Printed by warnIfOpen.
+     * @throws NullPointerException if closeMethodName is null.
+     */
+    public void open(@NonNull String closeMethodName) {
+        mImpl.open(closeMethodName);
+    }
+
+    /** Marks this CloseGuard instance as closed to avoid warnings on finalization. */
+    public void close() {
+        mImpl.close();
+    }
+
+    /**
+     * Logs a warning if the caller did not properly cleanup by calling an explicit close method
+     * before finalization.
+     */
+    public void warnIfOpen() {
+        mImpl.warnIfOpen();
+    }
+}
diff --git a/core/java/android/util/StatsEvent.aidl b/core/java/android/util/StatsEvent.aidl
deleted file mode 100644
index deac873..0000000
--- a/core/java/android/util/StatsEvent.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-parcelable StatsEvent cpp_header "android/util/StatsEvent.h";
diff --git a/core/java/android/util/StatsEvent.java b/core/java/android/util/StatsEvent.java
index 7e71640..c765945 100644
--- a/core/java/android/util/StatsEvent.java
+++ b/core/java/android/util/StatsEvent.java
@@ -31,14 +31,23 @@
  *
  * <p>Usage:</p>
  * <pre>
+ *      // Pushed event
+ *      StatsEvent statsEvent = StatsEvent.newBuilder()
+ *          .setAtomId(atomId)
+ *          .writeBoolean(false)
+ *          .writeString("annotated String field")
+ *          .addBooleanAnnotation(annotationId, true)
+ *          .usePooledBuffer()
+ *          .build();
+ *      StatsLog.write(statsEvent);
+ *
+ *      // Pulled event
  *      StatsEvent statsEvent = StatsEvent.newBuilder()
  *          .setAtomId(atomId)
  *          .writeBoolean(false)
  *          .writeString("annotated String field")
  *          .addBooleanAnnotation(annotationId, true)
  *          .build();
- *
- *      StatsLog.write(statsEvent);
  * </pre>
  * @hide
  **/
@@ -210,12 +219,15 @@
     private static final int MAX_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
 
     private final int mAtomId;
-    private final Buffer mBuffer;
+    private final byte[] mPayload;
+    private Buffer mBuffer;
     private final int mNumBytes;
 
-    private StatsEvent(final int atomId, @NonNull final Buffer buffer, final int numBytes) {
+    private StatsEvent(final int atomId, @Nullable final Buffer buffer,
+            @NonNull final byte[] payload, final int numBytes) {
         mAtomId = atomId;
         mBuffer = buffer;
+        mPayload = payload;
         mNumBytes = numBytes;
     }
 
@@ -243,7 +255,7 @@
      **/
     @NonNull
     public byte[] getBytes() {
-        return mBuffer.getBytes();
+        return mPayload;
     }
 
     /**
@@ -256,10 +268,14 @@
     }
 
     /**
-     * Recycle this StatsEvent object.
+     * Recycle resources used by this StatsEvent object.
+     * No actions should be taken on this StatsEvent after release() is called.
      **/
     public void release() {
-        mBuffer.release();
+        if (mBuffer != null) {
+            mBuffer.release();
+            mBuffer = null;
+        }
     }
 
     /**
@@ -280,7 +296,18 @@
      *         optional string field3 = 3 [(annotation1) = true];
      *     }
      *
-     *     // StatsEvent construction.
+     *     // StatsEvent construction for pushed event.
+     *     StatsEvent.newBuilder()
+     *     StatsEvent statsEvent = StatsEvent.newBuilder()
+     *         .setAtomId(atomId)
+     *         .writeInt(3) // field1
+     *         .writeLong(8L) // field2
+     *         .writeString("foo") // field 3
+     *         .addBooleanAnnotation(annotation1Id, true)
+     *         .usePooledBuffer()
+     *         .build();
+     *
+     *     // StatsEvent construction for pulled event.
      *     StatsEvent.newBuilder()
      *     StatsEvent statsEvent = StatsEvent.newBuilder()
      *         .setAtomId(atomId)
@@ -306,6 +333,7 @@
         private byte mLastType;
         private int mNumElements;
         private int mErrorMask;
+        private boolean mUsePooledBuffer = false;
 
         private Builder(final Buffer buffer) {
             mBuffer = buffer;
@@ -569,6 +597,17 @@
         }
 
         /**
+         * Indicates to reuse Buffer's byte array as the underlying payload in StatsEvent.
+         * This should be called for pushed events to reduce memory allocations and garbage
+         * collections.
+         **/
+        @NonNull
+        public Builder usePooledBuffer() {
+            mUsePooledBuffer = true;
+            return this;
+        }
+
+        /**
          * Builds a StatsEvent object with values entered in this Builder.
          **/
         @NonNull
@@ -599,7 +638,18 @@
                 size = mPos;
             }
 
-            return new StatsEvent(mAtomId, mBuffer, size);
+            if (mUsePooledBuffer) {
+                return new StatsEvent(mAtomId, mBuffer, mBuffer.getBytes(), size);
+            } else {
+                // Create a copy of the buffer with the required number of bytes.
+                final byte[] payload = new byte[size];
+                System.arraycopy(mBuffer.getBytes(), 0, payload, 0, size);
+
+                // Return Buffer instance to the pool.
+                mBuffer.release();
+
+                return new StatsEvent(mAtomId, null, payload, size);
+            }
         }
 
         private void writeTypeId(final byte typeId) {
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index 8cb5b05..952d7cb 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -24,7 +24,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
-import android.os.IStatsManager;
+import android.os.IStatsd;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
@@ -36,7 +36,7 @@
     private static final String TAG = "StatsLog";
     private static final boolean DEBUG = false;
 
-    private static IStatsManager sService;
+    private static IStatsd sService;
 
     private static Object sLogLock = new Object();
 
@@ -52,7 +52,7 @@
     public static boolean logStart(int label) {
         synchronized (sLogLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 if (service == null) {
                     if (DEBUG) {
                         Slog.d(TAG, "Failed to find statsd when logging start");
@@ -81,7 +81,7 @@
     public static boolean logStop(int label) {
         synchronized (sLogLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 if (service == null) {
                     if (DEBUG) {
                         Slog.d(TAG, "Failed to find statsd when logging stop");
@@ -109,7 +109,7 @@
     public static boolean logEvent(int label) {
         synchronized (sLogLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 if (service == null) {
                     if (DEBUG) {
                         Slog.d(TAG, "Failed to find statsd when logging event");
@@ -151,7 +151,7 @@
             @NonNull long[] experimentIds) {
         synchronized (sLogLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 if (service == null) {
                     if (DEBUG) {
                         Slog.d(TAG, "Failed to find statsd when logging event");
@@ -191,7 +191,7 @@
             long packageVersionCode, int rollbackReason, String failingPackageName) {
         synchronized (sLogLock) {
             try {
-                IStatsManager service = getIStatsManagerLocked();
+                IStatsd service = getIStatsdLocked();
                 if (service == null) {
                     if (DEBUG) {
                         Slog.d(TAG, "Failed to find statsd when logging event");
@@ -215,11 +215,11 @@
     }
 
 
-    private static IStatsManager getIStatsManagerLocked() throws RemoteException {
+    private static IStatsd getIStatsdLocked() throws RemoteException {
         if (sService != null) {
             return sService;
         }
-        sService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
+        sService = IStatsd.Stub.asInterface(ServiceManager.getService("stats"));
         return sService;
     }
 
@@ -248,12 +248,15 @@
 
     /**
      * Write an event to stats log using the raw format encapsulated in StatsEvent.
+     * After writing to stats log, release() is called on the StatsEvent object.
+     * No further action should be taken on the StatsEvent object following this call.
      *
      * @param statsEvent    The StatsEvent object containing the encoded buffer of data to write.
      * @hide
      */
     public static void write(@NonNull final StatsEvent statsEvent) {
         writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId());
+        statsEvent.release();
     }
 
     private static void enforceDumpCallingPermission(Context context) {
diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
index 443bbd8..e81e3f7 100644
--- a/core/java/android/util/apk/VerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -70,22 +70,6 @@
 
     /**
      * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link
-     * ByteBuffer} created by the {@link ByteBufferFactory}.  The output is suitable to be used as
-     * the on-disk format for fs-verity to use.
-     *
-     * @return VerityResult containing a buffer with the generated Merkle tree stored at the
-     *         front, the tree size, and the calculated root hash.
-     */
-    @NonNull
-    public static VerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
-            @NonNull ByteBufferFactory bufferFactory)
-            throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
-        return generateVerityTreeInternal(apk, bufferFactory, null /* signatureInfo */,
-                false /* skipSigningBlock */);
-    }
-
-    /**
-     * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link
      * ByteBuffer} created by the {@link ByteBufferFactory}.  The Merkle tree does not cover Signing
      * Block specificed in {@code signatureInfo}.  The output is suitable to be used as the on-disk
      * format for fs-verity to use (with elide and patch extensions).
@@ -97,21 +81,16 @@
     public static VerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
             @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory)
             throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
-        return generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
-                true /* skipSigningBlock */);
+        return generateVerityTreeInternal(apk, bufferFactory, signatureInfo);
     }
 
     @NonNull
     private static VerityResult generateVerityTreeInternal(@NonNull RandomAccessFile apk,
-            @NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo,
-            boolean skipSigningBlock)
+            @NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo)
             throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
-        long dataSize = apk.length();
-        if (skipSigningBlock) {
-            long signingBlockSize =
-                    signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
-            dataSize -= signingBlockSize;
-        }
+        long signingBlockSize =
+                signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
+        long dataSize = apk.length() - signingBlockSize;
         int[] levelOffset = calculateVerityLevelOffset(dataSize);
         int merkleTreeSize = levelOffset[levelOffset.length - 1];
 
@@ -120,10 +99,8 @@
                 + CHUNK_SIZE_BYTES);  // maximum size of apk-verity metadata
         output.order(ByteOrder.LITTLE_ENDIAN);
         ByteBuffer tree = slice(output, 0, merkleTreeSize);
-        // Only use default salt in legacy case.
-        byte[] salt = skipSigningBlock ? DEFAULT_SALT : null;
-        byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, salt, levelOffset,
-                tree, skipSigningBlock);
+        byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, DEFAULT_SALT,
+                levelOffset, tree);
         return new VerityResult(output, merkleTreeSize, apkRootHash);
     }
 
@@ -173,8 +150,7 @@
             throws IOException, SignatureNotFoundException, SecurityException, DigestException,
                    NoSuchAlgorithmException {
         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
-            VerityResult result = generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
-                    true /* skipSigningBlock */);
+            VerityResult result = generateVerityTreeInternal(apk, bufferFactory, signatureInfo);
             ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,
                     result.verityData.limit());
             generateApkVerityFooter(apk, signatureInfo, footer);
@@ -351,17 +327,12 @@
     @NonNull
     private static byte[] generateVerityTreeInternal(@NonNull RandomAccessFile apk,
             @Nullable SignatureInfo signatureInfo, @Nullable byte[] salt,
-            @NonNull int[] levelOffset, @NonNull ByteBuffer output, boolean skipSigningBlock)
+            @NonNull int[] levelOffset, @NonNull ByteBuffer output)
             throws IOException, NoSuchAlgorithmException, DigestException {
         // 1. Digest the apk to generate the leaf level hashes.
-        if (skipSigningBlock) {
-            assertSigningBlockAlignedAndHasFullPages(signatureInfo);
-            generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
-                        levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
-        } else {
-            generateFsVerityDigestAtLeafLevel(apk, slice(output,
-                        levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
-        }
+        assertSigningBlockAlignedAndHasFullPages(signatureInfo);
+        generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
+                    levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
 
         // 2. Digest the lower level hashes bottom up.
         for (int level = levelOffset.length - 3; level >= 0; level--) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index c699cdc..d79abc2 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1170,16 +1170,6 @@
     /**
      * {@hide}
      */
-    @UnsupportedAppUsage
-    protected void setNeedsMenuKey(int value) {
-        final WindowManager.LayoutParams attrs = getAttributes();
-        attrs.needsMenuKey = value;
-        dispatchWindowAttributesChanged(attrs);
-    }
-
-    /**
-     * {@hide}
-     */
     protected void dispatchWindowAttributesChanged(WindowManager.LayoutParams attrs) {
         if (mCallback != null) {
             mCallback.onWindowAttributesChanged(attrs);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index d40f832..9d5f98e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -29,7 +29,6 @@
 import static android.view.WindowLayoutParamsProto.HEIGHT;
 import static android.view.WindowLayoutParamsProto.HORIZONTAL_MARGIN;
 import static android.view.WindowLayoutParamsProto.INPUT_FEATURE_FLAGS;
-import static android.view.WindowLayoutParamsProto.NEEDS_MENU_KEY;
 import static android.view.WindowLayoutParamsProto.PREFERRED_REFRESH_RATE;
 import static android.view.WindowLayoutParamsProto.PRIVATE_FLAGS;
 import static android.view.WindowLayoutParamsProto.ROTATION_ANIMATION;
@@ -1106,6 +1105,13 @@
         public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
 
         /**
+         * Window type: Window for adding accessibility window magnification above other windows.
+         * This will place the window in the overlay windows.
+         * @hide
+         */
+        public static final int TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
@@ -1941,48 +1947,6 @@
         public int privateFlags;
 
         /**
-         * Value for {@link #needsMenuKey} for a window that has not explicitly specified if it
-         * needs {@link #NEEDS_MENU_SET_TRUE} or doesn't need {@link #NEEDS_MENU_SET_FALSE} a menu
-         * key. For this case, we should look at windows behind it to determine the appropriate
-         * value.
-         *
-         * @hide
-         */
-        public static final int NEEDS_MENU_UNSET = 0;
-
-        /**
-         * Value for {@link #needsMenuKey} for a window that has explicitly specified it needs a
-         * menu key.
-         *
-         * @hide
-         */
-        @UnsupportedAppUsage
-        public static final int NEEDS_MENU_SET_TRUE = 1;
-
-        /**
-         * Value for {@link #needsMenuKey} for a window that has explicitly specified it doesn't
-         * needs a menu key.
-         *
-         * @hide
-         */
-        @UnsupportedAppUsage
-        public static final int NEEDS_MENU_SET_FALSE = 2;
-
-        /**
-         * State variable for a window belonging to an activity that responds to
-         * {@link KeyEvent#KEYCODE_MENU} and therefore needs a Menu key. For devices where Menu is a
-         * physical button this variable is ignored, but on devices where the Menu key is drawn in
-         * software it may be hidden unless this variable is set to {@link #NEEDS_MENU_SET_TRUE}.
-         *
-         *  (Note that Action Bars, when available, are the preferred way to offer additional
-         * functions otherwise accessed via an options menu.)
-         *
-         * {@hide}
-         */
-        @UnsupportedAppUsage
-        public int needsMenuKey = NEEDS_MENU_UNSET;
-
-        /**
          * Given a particular set of window manager flags, determine whether
          * such a window may be a target for an input method when it has
          * focus.  In particular, this checks the
@@ -2784,7 +2748,6 @@
             out.writeInt(surfaceInsets.bottom);
             out.writeInt(hasManualSurfaceInsets ? 1 : 0);
             out.writeInt(preservePreviousSurfaceInsets ? 1 : 0);
-            out.writeInt(needsMenuKey);
             out.writeLong(accessibilityIdOfAnchor);
             TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
             out.writeInt(mColorMode);
@@ -2842,7 +2805,6 @@
             surfaceInsets.bottom = in.readInt();
             hasManualSurfaceInsets = in.readInt() != 0;
             preservePreviousSurfaceInsets = in.readInt() != 0;
-            needsMenuKey = in.readInt();
             accessibilityIdOfAnchor = in.readLong();
             accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
             mColorMode = in.readInt();
@@ -2884,8 +2846,6 @@
         /** {@hide} */
         public static final int PREFERRED_REFRESH_RATE_CHANGED = 1 << 21;
         /** {@hide} */
-        public static final int NEEDS_MENU_KEY_CHANGED = 1 << 22;
-        /** {@hide} */
         public static final int PREFERRED_DISPLAY_MODE_ID = 1 << 23;
         /** {@hide} */
         public static final int ACCESSIBILITY_ANCHOR_CHANGED = 1 << 24;
@@ -3059,11 +3019,6 @@
                 changes |= SURFACE_INSETS_CHANGED;
             }
 
-            if (needsMenuKey != o.needsMenuKey) {
-                needsMenuKey = o.needsMenuKey;
-                changes |= NEEDS_MENU_KEY_CHANGED;
-            }
-
             if (accessibilityIdOfAnchor != o.accessibilityIdOfAnchor) {
                 accessibilityIdOfAnchor = o.accessibilityIdOfAnchor;
                 changes |= ACCESSIBILITY_ANCHOR_CHANGED;
@@ -3217,9 +3172,6 @@
                     sb.append(" (!preservePreviousSurfaceInsets)");
                 }
             }
-            if (needsMenuKey == NEEDS_MENU_SET_TRUE) {
-                sb.append(" needsMenuKey");
-            }
             if (mColorMode != COLOR_MODE_DEFAULT) {
                 sb.append(" colorMode=").append(ActivityInfo.colorModeToString(mColorMode));
             }
@@ -3281,7 +3233,6 @@
             proto.write(HAS_SYSTEM_UI_LISTENERS, hasSystemUiListeners);
             proto.write(INPUT_FEATURE_FLAGS, inputFeatures);
             proto.write(USER_ACTIVITY_TIMEOUT, userActivityTimeout);
-            proto.write(NEEDS_MENU_KEY, needsMenuKey);
             proto.write(COLOR_MODE, mColorMode);
             proto.write(FLAGS, flags);
             proto.write(PRIVATE_FLAGS, privateFlags);
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index cc28840..843f8e3 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -117,7 +117,7 @@
      * Activity action: Launch UI to manage which accessibility service or feature is assigned
      * to the navigation bar Accessibility button.
      * <p>
-     * Input: Nothing.
+     * Input: {@link #EXTRA_SHORTCUT_TYPE} is the shortcut type.
      * </p>
      * <p>
      * Output: Nothing.
@@ -130,6 +130,42 @@
             "com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON";
 
     /**
+     * Used as an int extra field in {@link #ACTION_CHOOSE_ACCESSIBILITY_BUTTON} intent to specify
+     * the shortcut type.
+     *
+     * @hide
+     */
+    public static final String EXTRA_SHORTCUT_TYPE =
+            "com.android.internal.intent.extra.SHORTCUT_TYPE";
+
+    /**
+     * Used as an int value for {@link #EXTRA_SHORTCUT_TYPE} to represent the accessibility button
+     * shortcut type.
+     *
+     * @hide
+     */
+    public static final int ACCESSIBILITY_BUTTON = 0;
+
+    /**
+     * Used as an int value for {@link #EXTRA_SHORTCUT_TYPE} to represent hardware key shortcut,
+     * such as volume key button.
+     *
+     * @hide
+     */
+    public static final int ACCESSIBILITY_SHORTCUT_KEY = 1;
+
+    /**
+     * Annotations for the shortcut type.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            ACCESSIBILITY_BUTTON,
+            ACCESSIBILITY_SHORTCUT_KEY
+    })
+    public @interface ShortcutType {}
+
+    /**
      * Annotations for content flag of UI.
      * @hide
      */
@@ -1242,27 +1278,28 @@
     }
 
     /**
-     * Get the component name of the service currently assigned to the accessibility shortcut.
+     * Returns the list of shortcut target names currently assigned to the given shortcut.
      *
-     * @return The flattened component name
+     * @param shortcutType The shortcut type.
+     * @return The list of shortcut target names.
      * @hide
      */
     @TestApi
     @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
-    @Nullable
-    public String getAccessibilityShortcutService() {
+    @NonNull
+    public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
         final IAccessibilityManager service;
         synchronized (mLock) {
             service = getServiceLocked();
         }
         if (service != null) {
             try {
-                return service.getAccessibilityShortcutService();
+                return service.getAccessibilityShortcutTargets(shortcutType);
             } catch (RemoteException re) {
                 re.rethrowFromSystemServer();
             }
         }
-        return null;
+        return Collections.emptyList();
     }
 
     /**
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 023fda5..36515b3 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -73,7 +73,7 @@
     void performAccessibilityShortcut();
 
     // Requires Manifest.permission.MANAGE_ACCESSIBILITY
-    String getAccessibilityShortcutService();
+    List<String> getAccessibilityShortcutTargets(int shortcutType);
 
     // System process only
     boolean sendFingerprintGesture(int gestureKeyCode);
diff --git a/core/java/android/view/textclassifier/ConfigParser.java b/core/java/android/view/textclassifier/ConfigParser.java
deleted file mode 100644
index 9b51458..0000000
--- a/core/java/android/view/textclassifier/ConfigParser.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.provider.DeviceConfig;
-import android.util.ArrayMap;
-import android.util.KeyValueListParser;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.internal.util.Preconditions;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Supplier;
-
-/**
- * Retrieves settings from {@link DeviceConfig} and {@link android.provider.Settings}.
- * It will try DeviceConfig first and then Settings.
- *
- * @hide
- */
-@VisibleForTesting(visibility = Visibility.PACKAGE)
-public final class ConfigParser {
-    private static final String TAG = "ConfigParser";
-
-    public static final boolean ENABLE_DEVICE_CONFIG = true;
-
-    private static final String STRING_LIST_DELIMITER = ":";
-
-    private final Supplier<String> mLegacySettingsSupplier;
-    private final Object mLock = new Object();
-    @GuardedBy("mLock")
-    private final Map<String, Object> mCache = new ArrayMap<>();
-    @GuardedBy("mLock")
-    private @Nullable KeyValueListParser mSettingsParser;  // Call getLegacySettings() instead.
-
-    public ConfigParser(Supplier<String> legacySettingsSupplier) {
-        mLegacySettingsSupplier = Preconditions.checkNotNull(legacySettingsSupplier);
-    }
-
-    private KeyValueListParser getLegacySettings() {
-        synchronized (mLock) {
-            if (mSettingsParser == null) {
-                final String legacySettings = mLegacySettingsSupplier.get();
-                try {
-                    mSettingsParser = new KeyValueListParser(',');
-                    mSettingsParser.setString(legacySettings);
-                } catch (IllegalArgumentException e) {
-                    // Failed to parse the settings string, log this and move on with defaults.
-                    Log.w(TAG, "Bad text_classifier_constants: " + legacySettings);
-                }
-            }
-            return mSettingsParser;
-        }
-    }
-
-    /**
-     * Reads a boolean setting through the cache.
-     */
-    public boolean getBoolean(String key, boolean defaultValue) {
-        synchronized (mLock) {
-            final Object cached = mCache.get(key);
-            if (cached instanceof Boolean) {
-                return (boolean) cached;
-            }
-            final boolean value;
-            if (ENABLE_DEVICE_CONFIG) {
-                value = DeviceConfig.getBoolean(
-                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                        key,
-                        getLegacySettings().getBoolean(key, defaultValue));
-            } else {
-                value = getLegacySettings().getBoolean(key, defaultValue);
-            }
-            mCache.put(key, value);
-            return value;
-        }
-    }
-
-    /**
-     * Reads an integer setting through the cache.
-     */
-    public int getInt(String key, int defaultValue) {
-        synchronized (mLock) {
-            final Object cached = mCache.get(key);
-            if (cached instanceof Integer) {
-                return (int) cached;
-            }
-            final int value;
-            if (ENABLE_DEVICE_CONFIG) {
-                value = DeviceConfig.getInt(
-                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                        key,
-                        getLegacySettings().getInt(key, defaultValue));
-            } else {
-                value = getLegacySettings().getInt(key, defaultValue);
-            }
-            mCache.put(key, value);
-            return value;
-        }
-    }
-
-    /**
-     * Reads a float setting through the cache.
-     */
-    public float getFloat(String key, float defaultValue) {
-        synchronized (mLock) {
-            final Object cached = mCache.get(key);
-            if (cached instanceof Float) {
-                return (float) cached;
-            }
-            final float value;
-            if (ENABLE_DEVICE_CONFIG) {
-                value = DeviceConfig.getFloat(
-                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                        key,
-                        getLegacySettings().getFloat(key, defaultValue));
-            } else {
-                value = getLegacySettings().getFloat(key, defaultValue);
-            }
-            mCache.put(key, value);
-            return value;
-        }
-    }
-
-    /**
-     * Reads a string setting through the cache.
-     */
-    public String getString(String key, String defaultValue) {
-        synchronized (mLock) {
-            final Object cached = mCache.get(key);
-            if (cached instanceof String) {
-                return (String) cached;
-            }
-            final String value;
-            if (ENABLE_DEVICE_CONFIG) {
-                value = DeviceConfig.getString(
-                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                        key,
-                        getLegacySettings().getString(key, defaultValue));
-            } else {
-                value = getLegacySettings().getString(key, defaultValue);
-            }
-            mCache.put(key, value);
-            return value;
-        }
-    }
-
-    /**
-     * Reads a string list setting through the cache.
-     */
-    public List<String> getStringList(String key, List<String> defaultValue) {
-        synchronized (mLock) {
-            final Object cached = mCache.get(key);
-            if (cached instanceof List) {
-                final List asList = (List) cached;
-                if (asList.isEmpty()) {
-                    return Collections.emptyList();
-                } else if (asList.get(0) instanceof String) {
-                    return (List<String>) cached;
-                }
-            }
-            final List<String> value;
-            if (ENABLE_DEVICE_CONFIG) {
-                value = getDeviceConfigStringList(
-                        key,
-                        getSettingsStringList(key, defaultValue));
-            } else {
-                value = getSettingsStringList(key, defaultValue);
-            }
-            mCache.put(key, value);
-            return value;
-        }
-    }
-
-    /**
-     * Reads a float array through the cache. The returned array should be expected to be of the
-     * same length as that of the defaultValue.
-     */
-    public float[] getFloatArray(String key, float[] defaultValue) {
-        synchronized (mLock) {
-            final Object cached = mCache.get(key);
-            if (cached instanceof float[]) {
-                return (float[]) cached;
-            }
-            final float[] value;
-            if (ENABLE_DEVICE_CONFIG) {
-                value = getDeviceConfigFloatArray(
-                        key,
-                        getSettingsFloatArray(key, defaultValue));
-            } else {
-                value = getSettingsFloatArray(key, defaultValue);
-            }
-            mCache.put(key, value);
-            return value;
-        }
-    }
-
-    private List<String> getSettingsStringList(String key, List<String> defaultValue) {
-        return parse(mSettingsParser.getString(key, null), defaultValue);
-    }
-
-    private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) {
-        return parse(
-                DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
-                defaultValue);
-    }
-
-    private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) {
-        return parse(
-                DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
-                defaultValue);
-    }
-
-    private float[] getSettingsFloatArray(String key, float[] defaultValue) {
-        return parse(mSettingsParser.getString(key, null), defaultValue);
-    }
-
-    private static List<String> parse(@Nullable String listStr, List<String> defaultValue) {
-        if (listStr != null) {
-            return Collections.unmodifiableList(
-                    Arrays.asList(listStr.split(STRING_LIST_DELIMITER)));
-        }
-        return defaultValue;
-    }
-
-    private static float[] parse(@Nullable String arrayStr, float[] defaultValue) {
-        if (arrayStr != null) {
-            final String[] split = arrayStr.split(STRING_LIST_DELIMITER);
-            if (split.length != defaultValue.length) {
-                return defaultValue;
-            }
-            final float[] result = new float[split.length];
-            for (int i = 0; i < split.length; i++) {
-                try {
-                    result[i] = Float.parseFloat(split[i]);
-                } catch (NumberFormatException e) {
-                    return defaultValue;
-                }
-            }
-            return result;
-        } else {
-            return defaultValue;
-        }
-    }
-}
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 12ed4b9..7d1077e 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -161,7 +161,6 @@
         mEntityType = in.readString();
         mWidgetVersion = in.readInt() > 0 ? in.readString() : null;
         mPackageName = in.readString();
-        mUserId = in.readInt();
         mWidgetType = in.readString();
         mInvocationMethod = in.readInt();
         mResultId = in.readString();
@@ -175,6 +174,7 @@
         mEnd = in.readInt();
         mSmartStart = in.readInt();
         mSmartEnd = in.readInt();
+        mUserId = in.readInt();
     }
 
     @Override
@@ -188,7 +188,6 @@
             dest.writeString(mWidgetVersion);
         }
         dest.writeString(mPackageName);
-        dest.writeInt(mUserId);
         dest.writeString(mWidgetType);
         dest.writeInt(mInvocationMethod);
         dest.writeString(mResultId);
@@ -204,6 +203,7 @@
         dest.writeInt(mEnd);
         dest.writeInt(mSmartStart);
         dest.writeInt(mSmartEnd);
+        dest.writeInt(mUserId);
     }
 
     @Override
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 9f8671a..ed69513 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -16,38 +16,31 @@
 
 package android.view.textclassifier;
 
+import android.annotation.Nullable;
+import android.provider.DeviceConfig;
+
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
-import java.util.function.Supplier;
 
 /**
  * TextClassifier specific settings.
- * This is encoded as a key=value list, separated by commas.
- * <p>
- * Example of setting the values for testing.
- * <p>
- * <pre>
- * adb shell settings put global text_classifier_constants \
- *      model_dark_launch_enabled=true,smart_selection_enabled=true, \
- *      entity_list_default=phone:address, \
- *      lang_id_context_settings=20:1.0:0.4
- * </pre>
- * <p>
- * Settings are also available in device config. These take precedence over those in settings
- * global.
- * <p>
+ *
+ * <p>Currently, this class does not guarantee co-diverted flags are updated atomically.
+ *
  * <pre>
  * adb shell cmd device_config put textclassifier system_textclassifier_enabled true
  * </pre>
  *
- * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
- * @see android.provider.DeviceConfig.NAMESPACE_TEXTCLASSIFIER
+ * @see android.provider.DeviceConfig#NAMESPACE_TEXTCLASSIFIER
  * @hide
  */
 // TODO: Rename to TextClassifierSettings.
 public final class TextClassificationConstants {
+    private static final String DELIMITER = ":";
 
     /**
      * Whether the smart linkify feature is enabled.
@@ -56,11 +49,12 @@
     /**
      * Whether SystemTextClassifier is enabled.
      */
-    private static final String SYSTEM_TEXT_CLASSIFIER_ENABLED = "system_textclassifier_enabled";
+    static final String SYSTEM_TEXT_CLASSIFIER_ENABLED = "system_textclassifier_enabled";
     /**
      * Whether TextClassifierImpl is enabled.
      */
-    private static final String LOCAL_TEXT_CLASSIFIER_ENABLED = "local_textclassifier_enabled";
+    @VisibleForTesting
+    static final String LOCAL_TEXT_CLASSIFIER_ENABLED = "local_textclassifier_enabled";
     /**
      * Enable smart selection without a visible UI changes.
      */
@@ -82,7 +76,8 @@
     /**
      * Max length of text that suggestSelection can accept.
      */
-    private static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
+    @VisibleForTesting
+    static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
             "suggest_selection_max_range_length";
     /**
      * Max length of text that classifyText can accept.
@@ -101,7 +96,8 @@
      * A colon(:) separated string that specifies the default entities types for
      * generateLinks when hint is not given.
      */
-    private static final String ENTITY_LIST_DEFAULT = "entity_list_default";
+    @VisibleForTesting
+    static final String ENTITY_LIST_DEFAULT = "entity_list_default";
     /**
      * A colon(:) separated string that specifies the default entities types for
      * generateLinks when the text is in a not editable UI widget.
@@ -127,7 +123,8 @@
     /**
      * Threshold to accept a suggested language from LangID model.
      */
-    private static final String LANG_ID_THRESHOLD_OVERRIDE = "lang_id_threshold_override";
+    @VisibleForTesting
+    static final String LANG_ID_THRESHOLD_OVERRIDE = "lang_id_threshold_override";
     /**
      * Whether to enable {@link android.view.textclassifier.TemplateIntentFactory}.
      */
@@ -156,7 +153,8 @@
      * Reject all text less than minimumTextSize with penalizeRatio=0
      * @see {@code TextClassifierImpl#detectLanguages(String, int, int)} for reference.
      */
-    private static final String LANG_ID_CONTEXT_SETTINGS = "lang_id_context_settings";
+    @VisibleForTesting
+    static final String LANG_ID_CONTEXT_SETTINGS = "lang_id_context_settings";
 
     /**
      * The TextClassifierService which would like to use. Example of setting the package:
@@ -164,9 +162,9 @@
      * adb shell cmd device_config put textclassifier textclassifier_service_package_override \
      *      com.android.textclassifier
      * </pre>
-     *
      */
-    private static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE =
+    @VisibleForTesting
+    static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE =
             "textclassifier_service_package_override";
 
     private static final String DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = null;
@@ -213,142 +211,124 @@
     private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true;
     private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f};
 
-    private final ConfigParser mConfigParser;
-
-    public TextClassificationConstants(Supplier<String> legacySettingsSupplier) {
-        mConfigParser = new ConfigParser(legacySettingsSupplier);
-    }
-
-    public String getTextClassifierServiceName() {
-        return mConfigParser.getString(
+    @Nullable
+    public String getTextClassifierServicePackageOverride() {
+        return DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
                 TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
                 DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE);
     }
 
     public boolean isLocalTextClassifierEnabled() {
-        return mConfigParser.getBoolean(
-                LOCAL_TEXT_CLASSIFIER_ENABLED,
-                LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                LOCAL_TEXT_CLASSIFIER_ENABLED, LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
     }
 
     public boolean isSystemTextClassifierEnabled() {
-        return mConfigParser.getBoolean(
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
                 SYSTEM_TEXT_CLASSIFIER_ENABLED,
                 SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
     }
 
     public boolean isModelDarkLaunchEnabled() {
-        return mConfigParser.getBoolean(
-                MODEL_DARK_LAUNCH_ENABLED,
-                MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                MODEL_DARK_LAUNCH_ENABLED, MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
     }
 
     public boolean isSmartSelectionEnabled() {
-        return mConfigParser.getBoolean(
-                SMART_SELECTION_ENABLED,
-                SMART_SELECTION_ENABLED_DEFAULT);
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                SMART_SELECTION_ENABLED, SMART_SELECTION_ENABLED_DEFAULT);
     }
 
     public boolean isSmartTextShareEnabled() {
-        return mConfigParser.getBoolean(
-                SMART_TEXT_SHARE_ENABLED,
-                SMART_TEXT_SHARE_ENABLED_DEFAULT);
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                SMART_TEXT_SHARE_ENABLED, SMART_TEXT_SHARE_ENABLED_DEFAULT);
     }
 
     public boolean isSmartLinkifyEnabled() {
-        return mConfigParser.getBoolean(
-                SMART_LINKIFY_ENABLED,
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, SMART_LINKIFY_ENABLED,
                 SMART_LINKIFY_ENABLED_DEFAULT);
     }
 
     public boolean isSmartSelectionAnimationEnabled() {
-        return mConfigParser.getBoolean(
-                SMART_SELECT_ANIMATION_ENABLED,
-                SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                SMART_SELECT_ANIMATION_ENABLED, SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
     }
 
     public int getSuggestSelectionMaxRangeLength() {
-        return mConfigParser.getInt(
-                SUGGEST_SELECTION_MAX_RANGE_LENGTH,
-                SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
+        return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                SUGGEST_SELECTION_MAX_RANGE_LENGTH, SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
     }
 
     public int getClassifyTextMaxRangeLength() {
-        return mConfigParser.getInt(
-                CLASSIFY_TEXT_MAX_RANGE_LENGTH,
-                CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
+        return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                CLASSIFY_TEXT_MAX_RANGE_LENGTH, CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
     }
 
     public int getGenerateLinksMaxTextLength() {
-        return mConfigParser.getInt(
-                GENERATE_LINKS_MAX_TEXT_LENGTH,
-                GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
+        return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                GENERATE_LINKS_MAX_TEXT_LENGTH, GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
     }
 
     public int getGenerateLinksLogSampleRate() {
-        return mConfigParser.getInt(
-                GENERATE_LINKS_LOG_SAMPLE_RATE,
-                GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
+        return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                GENERATE_LINKS_LOG_SAMPLE_RATE, GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
     }
 
     public List<String> getEntityListDefault() {
-        return mConfigParser.getStringList(
-                ENTITY_LIST_DEFAULT,
-                ENTITY_LIST_DEFAULT_VALUE);
+        return getDeviceConfigStringList(ENTITY_LIST_DEFAULT, ENTITY_LIST_DEFAULT_VALUE);
     }
 
     public List<String> getEntityListNotEditable() {
-        return mConfigParser.getStringList(
-                ENTITY_LIST_NOT_EDITABLE,
-                ENTITY_LIST_DEFAULT_VALUE);
+        return getDeviceConfigStringList(ENTITY_LIST_NOT_EDITABLE, ENTITY_LIST_DEFAULT_VALUE);
     }
 
     public List<String> getEntityListEditable() {
-        return mConfigParser.getStringList(
-                ENTITY_LIST_EDITABLE,
-                ENTITY_LIST_DEFAULT_VALUE);
+        return getDeviceConfigStringList(ENTITY_LIST_EDITABLE, ENTITY_LIST_DEFAULT_VALUE);
     }
 
     public List<String> getInAppConversationActionTypes() {
-        return mConfigParser.getStringList(
+        return getDeviceConfigStringList(
                 IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
                 CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
     }
 
     public List<String> getNotificationConversationActionTypes() {
-        return mConfigParser.getStringList(
+        return getDeviceConfigStringList(
                 NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
                 CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
     }
 
     public float getLangIdThresholdOverride() {
-        return mConfigParser.getFloat(
+        return DeviceConfig.getFloat(
+                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
                 LANG_ID_THRESHOLD_OVERRIDE,
                 LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
     }
 
     public boolean isTemplateIntentFactoryEnabled() {
-        return mConfigParser.getBoolean(
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
                 TEMPLATE_INTENT_FACTORY_ENABLED,
                 TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT);
     }
 
     public boolean isTranslateInClassificationEnabled() {
-        return mConfigParser.getBoolean(
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
                 TRANSLATE_IN_CLASSIFICATION_ENABLED,
                 TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT);
     }
 
     public boolean isDetectLanguagesFromTextEnabled() {
-        return mConfigParser.getBoolean(
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
                 DETECT_LANGUAGES_FROM_TEXT_ENABLED,
                 DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT);
     }
 
     public float[] getLangIdContextSettings() {
-        return mConfigParser.getFloatArray(
-                LANG_ID_CONTEXT_SETTINGS,
-                LANG_ID_CONTEXT_SETTINGS_DEFAULT);
+        return getDeviceConfigFloatArray(
+                LANG_ID_CONTEXT_SETTINGS, LANG_ID_CONTEXT_SETTINGS_DEFAULT);
     }
 
     void dump(IndentingPrintWriter pw) {
@@ -356,7 +336,7 @@
         pw.increaseIndent();
         pw.printPair("classify_text_max_range_length", getClassifyTextMaxRangeLength())
                 .println();
-        pw.printPair("detect_language_from_text_enabled", isDetectLanguagesFromTextEnabled())
+        pw.printPair("detect_languages_from_text_enabled", isDetectLanguagesFromTextEnabled())
                 .println();
         pw.printPair("entity_list_default", getEntityListDefault())
                 .println();
@@ -396,8 +376,47 @@
                 .println();
         pw.printPair("translate_in_classification_enabled", isTranslateInClassificationEnabled())
                 .println();
-        pw.printPair("textclassifier_service_package_override", getTextClassifierServiceName())
-                .println();
+        pw.printPair("textclassifier_service_package_override",
+                getTextClassifierServicePackageOverride()).println();
         pw.decreaseIndent();
     }
+
+    private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) {
+        return parse(
+                DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
+                defaultValue);
+    }
+
+    private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) {
+        return parse(
+                DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
+                defaultValue);
+    }
+
+    private static List<String> parse(@Nullable String listStr, List<String> defaultValue) {
+        if (listStr != null) {
+            return Collections.unmodifiableList(Arrays.asList(listStr.split(DELIMITER)));
+        }
+        return defaultValue;
+    }
+
+    private static float[] parse(@Nullable String arrayStr, float[] defaultValue) {
+        if (arrayStr != null) {
+            final String[] split = arrayStr.split(DELIMITER);
+            if (split.length != defaultValue.length) {
+                return defaultValue;
+            }
+            final float[] result = new float[split.length];
+            for (int i = 0; i < split.length; i++) {
+                try {
+                    result[i] = Float.parseFloat(split[i]);
+                } catch (NumberFormatException e) {
+                    return defaultValue;
+                }
+            }
+            return result;
+        } else {
+            return defaultValue;
+        }
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index 3e0f7ee..7c25bf0 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -22,11 +22,9 @@
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
 import android.content.Context;
-import android.database.ContentObserver;
 import android.os.ServiceManager;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.Properties;
-import android.provider.Settings;
 import android.service.textclassifier.TextClassifierService;
 import android.view.textclassifier.TextClassifier.TextClassifierType;
 
@@ -36,6 +34,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.lang.ref.WeakReference;
+import java.util.Set;
 
 /**
  * Interface to the text classification service.
@@ -46,7 +45,7 @@
     private static final String LOG_TAG = "TextClassificationManager";
 
     private static final TextClassificationConstants sDefaultSettings =
-            new TextClassificationConstants(() ->  null);
+            new TextClassificationConstants();
 
     private final Object mLock = new Object();
     private final TextClassificationSessionFactory mDefaultSessionFactory =
@@ -132,10 +131,7 @@
     private TextClassificationConstants getSettings() {
         synchronized (mLock) {
             if (mSettings == null) {
-                mSettings = new TextClassificationConstants(
-                        () ->  Settings.Global.getString(
-                                getApplicationContext().getContentResolver(),
-                                Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+                mSettings = new TextClassificationConstants();
             }
             return mSettings;
         }
@@ -201,11 +197,7 @@
         try {
             // Note that fields could be null if the constructor threw.
             if (mSettingsObserver != null) {
-                getApplicationContext().getContentResolver()
-                        .unregisterContentObserver(mSettingsObserver);
-                if (ConfigParser.ENABLE_DEVICE_CONFIG) {
-                    DeviceConfig.removeOnPropertiesChangedListener(mSettingsObserver);
-                }
+                DeviceConfig.removeOnPropertiesChangedListener(mSettingsObserver);
             }
         } finally {
             super.finalize();
@@ -250,7 +242,7 @@
 
     private boolean isSystemTextClassifierEnabled() {
         return getSettings().isSystemTextClassifierEnabled()
-                && TextClassifierService.getServiceComponentName(mContext, getSettings()) != null;
+                && TextClassifierService.getServiceComponentName(mContext) != null;
     }
 
     /** @hide */
@@ -262,6 +254,12 @@
     private void invalidate() {
         synchronized (mLock) {
             mSettings = null;
+            invalidateTextClassifiers();
+        }
+    }
+
+    private void invalidateTextClassifiers() {
+        synchronized (mLock) {
             mLocalTextClassifier = null;
             mSystemTextClassifier = null;
         }
@@ -293,40 +291,29 @@
         }
     }
 
-    private static final class SettingsObserver extends ContentObserver
-            implements DeviceConfig.OnPropertiesChangedListener {
+    private static final class SettingsObserver implements
+            DeviceConfig.OnPropertiesChangedListener {
 
         private final WeakReference<TextClassificationManager> mTcm;
 
         SettingsObserver(TextClassificationManager tcm) {
-            super(null);
             mTcm = new WeakReference<>(tcm);
-            tcm.getApplicationContext().getContentResolver().registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.TEXT_CLASSIFIER_CONSTANTS),
-                    false /* notifyForDescendants */,
+            DeviceConfig.addOnPropertiesChangedListener(
+                    DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                    ActivityThread.currentApplication().getMainExecutor(),
                     this);
-            if (ConfigParser.ENABLE_DEVICE_CONFIG) {
-                DeviceConfig.addOnPropertiesChangedListener(
-                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                        ActivityThread.currentApplication().getMainExecutor(),
-                        this);
-            }
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            invalidateSettings();
         }
 
         @Override
         public void onPropertiesChanged(Properties properties) {
-            invalidateSettings();
-        }
-
-        private void invalidateSettings() {
             final TextClassificationManager tcm = mTcm.get();
             if (tcm != null) {
-                tcm.invalidate();
+                final Set<String> keys = properties.getKeyset();
+                if (keys.contains(TextClassificationConstants.SYSTEM_TEXT_CLASSIFIER_ENABLED)
+                        || keys.contains(
+                        TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED)) {
+                    tcm.invalidateTextClassifiers();
+                }
             }
         }
     }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7cec440..c571737 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -3473,10 +3473,18 @@
         return applyAsync(context, parent, executor, listener, null);
     }
 
+    private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) {
+        CancellationSignal cancelSignal = new CancellationSignal();
+        cancelSignal.setOnCancelListener(task);
+
+        task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
+        return cancelSignal;
+    }
+
     /** @hide */
     public CancellationSignal applyAsync(Context context, ViewGroup parent,
             Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
-        return getAsyncApplyTask(context, parent, listener, handler).startTaskOnExecutor(executor);
+        return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor);
     }
 
     private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
@@ -3487,7 +3495,6 @@
 
     private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
             implements CancellationSignal.OnCancelListener {
-        final CancellationSignal mCancelSignal = new CancellationSignal();
         final RemoteViews mRV;
         final ViewGroup mParent;
         final Context mContext;
@@ -3538,7 +3545,6 @@
 
         @Override
         protected void onPostExecute(ViewTree viewTree) {
-            mCancelSignal.setOnCancelListener(null);
             if (mError == null) {
                 if (mListener != null) {
                     mListener.onViewInflated(viewTree.mRoot);
@@ -3575,13 +3581,6 @@
         @Override
         public void onCancel() {
             cancel(true);
-            mCancelSignal.setOnCancelListener(null);
-        }
-
-        private CancellationSignal startTaskOnExecutor(Executor executor) {
-            mCancelSignal.setOnCancelListener(this);
-            executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
-            return mCancelSignal;
         }
     }
 
@@ -3647,8 +3646,8 @@
             }
         }
 
-        return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
-                context, listener, handler, v).startTaskOnExecutor(executor);
+        return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
+                context, listener, handler, v), executor);
     }
 
     private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 0b15cd0..3fdedc8 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -17,6 +17,7 @@
 package com.android.internal.accessibility;
 
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
 
 import static com.android.internal.util.ArrayUtils.convertToLongArray;
 
@@ -34,6 +35,7 @@
 import android.media.Ringtone;
 import android.media.RingtoneManager;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.Vibrator;
@@ -52,11 +54,12 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
 /**
- * Class to help manage the accessibility shortcut
+ * Class to help manage the accessibility shortcut key
  */
 public class AccessibilityShortcutController {
     private static final String TAG = "AccessibilityShortcutController";
@@ -66,6 +69,8 @@
             new ComponentName("com.android.server.accessibility", "ColorInversion");
     public static final ComponentName DALTONIZER_COMPONENT_NAME =
             new ComponentName("com.android.server.accessibility", "Daltonizer");
+    public static final String MAGNIFICATION_CONTROLLER_NAME =
+            "com.android.server.accessibility.MagnificationController";
 
     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -84,26 +89,6 @@
     public FrameworkObjectProvider mFrameworkObjectProvider = new FrameworkObjectProvider();
 
     /**
-     * Get the component name string for the service or feature currently assigned to the
-     * accessiblity shortcut
-     *
-     * @param context A valid context
-     * @param userId The user ID of interest
-     * @return The flattened component name string of the service selected by the user, or the
-     *         string for the default service if the user has not made a selection
-     */
-    public static String getTargetServiceComponentNameString(
-            Context context, int userId) {
-        final String currentShortcutServiceId = Settings.Secure.getStringForUser(
-                context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
-                userId);
-        if (currentShortcutServiceId != null) {
-            return currentShortcutServiceId;
-        }
-        return context.getString(R.string.config_defaultAccessibilityService);
-    }
-
-    /**
      * @return An immutable map from dummy component names to feature info for toggling a framework
      *         feature
      */
@@ -163,7 +148,7 @@
     /**
      * Check if the shortcut is available.
      *
-     * @param onLockScreen Whether or not the phone is currently locked.
+     * @param phoneLocked Whether or not the phone is currently locked.
      *
      * @return {@code true} if the shortcut is available
      */
@@ -172,8 +157,7 @@
     }
 
     public void onSettingsChanged() {
-        final boolean haveValidService =
-                !TextUtils.isEmpty(getTargetServiceComponentNameString(mContext, mUserId));
+        final boolean hasShortcutTarget = hasShortcutTarget();
         final ContentResolver cr = mContext.getContentResolver();
         final boolean enabled = Settings.Secure.getIntForUser(
                 cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, mUserId) == 1;
@@ -183,7 +167,7 @@
         mEnabledOnLockScreen = Settings.Secure.getIntForUser(
                 cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
                 dialogAlreadyShown, mUserId) == 1;
-        mIsShortcutEnabled = enabled && haveValidService;
+        mIsShortcutEnabled = enabled && hasShortcutTarget;
     }
 
     /**
@@ -205,7 +189,6 @@
             vibrator.vibrate(vibePattern, -1, VIBRATION_ATTRIBUTES);
         }
 
-
         if (dialogAlreadyShown == 0) {
             // The first time, we show a warning rather than toggle the service to give the user a
             // chance to turn off this feature before stuff gets enabled.
@@ -229,32 +212,44 @@
                 mAlertDialog.dismiss();
                 mAlertDialog = null;
             }
-
-            // Show a toast alerting the user to what's happening
-            final String serviceName = getShortcutFeatureDescription(false /* no summary */);
-            if (serviceName == null) {
-                Slog.e(TAG, "Accessibility shortcut set to invalid service");
-                return;
-            }
-            // For accessibility services, show a toast explaining what we're doing.
-            final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
-            if (serviceInfo != null) {
-                String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
-                        ? R.string.accessibility_shortcut_disabling_service
-                        : R.string.accessibility_shortcut_enabling_service);
-                String toastMessage = String.format(toastMessageFormatString, serviceName);
-                Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
-                        mContext, toastMessage, Toast.LENGTH_LONG);
-                warningToast.getWindowParams().privateFlags |=
-                        WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-                warningToast.show();
-            }
-
+            showToast();
             mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext)
                     .performAccessibilityShortcut();
         }
     }
 
+    /**
+     * Show toast if current assigned shortcut target is an accessibility service and its target
+     * sdk version is less than or equal to Q, or greater than Q and does not request
+     * accessibility button.
+     */
+    private void showToast() {
+        final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
+        if (serviceInfo == null) {
+            return;
+        }
+        final String serviceName = getShortcutFeatureDescription(/* no summary */ false);
+        if (serviceName == null) {
+            return;
+        }
+        final boolean requestA11yButton = (serviceInfo.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+        if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo
+                .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton) {
+            return;
+        }
+        // For accessibility services, show a toast explaining what we're doing.
+        String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
+                ? R.string.accessibility_shortcut_disabling_service
+                : R.string.accessibility_shortcut_enabling_service);
+        String toastMessage = String.format(toastMessageFormatString, serviceName);
+        Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
+                mContext, toastMessage, Toast.LENGTH_LONG);
+        warningToast.getWindowParams().privateFlags |=
+                WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+        warningToast.show();
+    }
+
     private AlertDialog createShortcutWarningDialog(int userId) {
         final String serviceDescription = getShortcutFeatureDescription(true /* Include summary */);
 
@@ -288,25 +283,21 @@
     }
 
     private AccessibilityServiceInfo getInfoForTargetService() {
-        final String currentShortcutServiceString = getTargetServiceComponentNameString(
-                mContext, UserHandle.USER_CURRENT);
-        if (currentShortcutServiceString == null) {
+        final ComponentName targetComponentName = getShortcutTargetComponentName();
+        if (targetComponentName == null) {
             return null;
         }
         AccessibilityManager accessibilityManager =
                 mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext);
         return accessibilityManager.getInstalledServiceInfoWithComponentName(
-                        ComponentName.unflattenFromString(currentShortcutServiceString));
+                targetComponentName);
     }
 
     private String getShortcutFeatureDescription(boolean includeSummary) {
-        final String currentShortcutServiceString = getTargetServiceComponentNameString(
-                mContext, UserHandle.USER_CURRENT);
-        if (currentShortcutServiceString == null) {
+        final ComponentName targetComponentName = getShortcutTargetComponentName();
+        if (targetComponentName == null) {
             return null;
         }
-        final ComponentName targetComponentName =
-                ComponentName.unflattenFromString(currentShortcutServiceString);
         final ToggleableFrameworkFeatureInfo frameworkFeatureInfo =
                 getFrameworkShortcutFeaturesMap().get(targetComponentName);
         if (frameworkFeatureInfo != null) {
@@ -372,6 +363,36 @@
     }
 
     /**
+     * Returns {@code true} if any shortcut targets were assigned to accessibility shortcut key.
+     */
+    private boolean hasShortcutTarget() {
+        // AccessibilityShortcutController is initialized earlier than AccessibilityManagerService.
+        // AccessibilityManager#getAccessibilityShortcutTargets may not return correct shortcut
+        // targets during boot. Needs to read settings directly here.
+        String shortcutTargets = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, mUserId);
+        if (TextUtils.isEmpty(shortcutTargets)) {
+            shortcutTargets = mContext.getString(R.string.config_defaultAccessibilityService);
+        }
+        return !TextUtils.isEmpty(shortcutTargets);
+    }
+
+    /**
+     * Gets the component name of the shortcut target.
+     *
+     * @return The component name, or null if it's assigned by multiple targets.
+     */
+    private ComponentName getShortcutTargetComponentName() {
+        final List<String> shortcutTargets = mFrameworkObjectProvider
+                .getAccessibilityManagerInstance(mContext)
+                .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY);
+        if (shortcutTargets.size() != 1) {
+            return null;
+        }
+        return ComponentName.unflattenFromString(shortcutTargets.get(0));
+    }
+
+    /**
      * Class to wrap TextToSpeech for shortcut dialog spoken feedback.
      */
     private class TtsPrompt implements TextToSpeech.OnInitListener {
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
new file mode 100644
index 0000000..2fd5bfd
--- /dev/null
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.app;
+import android.annotation.IntDef;
+import android.content.Context;
+import android.os.UserHandle;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.PagerAdapter;
+
+import com.android.internal.util.Preconditions;
+import com.android.internal.widget.ViewPager;
+
+/**
+ * Skeletal {@link PagerAdapter} implementation of a work or personal profile page for
+ * intent resolution (including share sheet).
+ */
+public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
+
+    static final int PROFILE_PERSONAL = 0;
+    static final int PROFILE_WORK = 1;
+    @IntDef({PROFILE_PERSONAL, PROFILE_WORK})
+    @interface Profile {}
+
+    private final Context mContext;
+    private int mCurrentPage;
+
+    AbstractMultiProfilePagerAdapter(Context context, int currentPage) {
+        mContext = Preconditions.checkNotNull(context);
+        mCurrentPage = currentPage;
+    }
+
+    Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Sets this instance of this class as {@link ViewPager}'s {@link PagerAdapter} and sets
+     * an {@link ViewPager.OnPageChangeListener} where it keeps track of the currently displayed
+     * page and rebuilds the list.
+     */
+    void setupViewPager(ViewPager viewPager) {
+        viewPager.setCurrentItem(mCurrentPage);
+        viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
+            @Override
+            public void onPageSelected(int position) {
+                mCurrentPage = position;
+                getCurrentListAdapter().rebuildList();
+            }
+        });
+        viewPager.setAdapter(this);
+    }
+
+    @Override
+    public ViewGroup instantiateItem(ViewGroup container, int position) {
+        final ProfileDescriptor profileDescriptor = getItem(position);
+        setupListAdapter(position);
+        container.addView(profileDescriptor.rootView);
+        return profileDescriptor.rootView;
+    }
+
+    @Override
+    public void destroyItem(ViewGroup container, int position, Object view) {
+        container.removeView((View) view);
+    }
+
+    @Override
+    public int getCount() {
+        return getItemCount();
+    }
+
+    protected int getCurrentPage() {
+        return mCurrentPage;
+    }
+
+    UserHandle getCurrentUserHandle() {
+        return getCurrentListAdapter().mResolverListController.getUserHandle();
+    }
+
+    @Override
+    public boolean isViewFromObject(View view, Object object) {
+        return view == object;
+    }
+
+    @Override
+    public CharSequence getPageTitle(int position) {
+        return null;
+    }
+
+    /**
+     * Returns the {@link ProfileDescriptor} relevant to the given <code>pageIndex</code>.
+     * <ul>
+     * <li>For a device with only one user, <code>pageIndex</code> value of
+     * <code>0</code> would return the personal profile {@link ProfileDescriptor}.</li>
+     * <li>For a device with a work profile, <code>pageIndex</code> value of <code>0</code> would
+     * return the personal profile {@link ProfileDescriptor}, and <code>pageIndex</code> value of
+     * <code>1</code> would return the work profile {@link ProfileDescriptor}.</li>
+     * </ul>
+     */
+    abstract ProfileDescriptor getItem(int pageIndex);
+
+    /**
+     * Returns the number of {@link ProfileDescriptor} objects.
+     * <p>For a normal consumer device with only one user returns <code>1</code>.
+     * <p>For a device with a work profile returns <code>2</code>.
+     */
+    abstract int getItemCount();
+
+    /**
+     * Responsible for assigning an adapter to the list view for the relevant page, specified by
+     * <code>pageIndex</code>, and other list view-related initialization procedures.
+     */
+    abstract void setupListAdapter(int pageIndex);
+
+    /**
+     * Returns the adapter of the list view for the relevant page specified by
+     * <code>pageIndex</code>.
+     * <p>This method is meant to be implemented with an implementation-specific return type
+     * depending on the adapter type.
+     */
+    abstract Object getAdapterForIndex(int pageIndex);
+
+    @VisibleForTesting
+    public abstract ResolverListAdapter getCurrentListAdapter();
+
+    abstract Object getCurrentRootAdapter();
+
+    abstract ViewGroup getCurrentAdapterView();
+
+    protected class ProfileDescriptor {
+        final ViewGroup rootView;
+        ProfileDescriptor(ViewGroup rootView) {
+            this.rootView = rootView;
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 183e9a6..1af3926 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -98,6 +98,7 @@
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.WindowInsets;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageView;
@@ -173,7 +174,6 @@
 
     @VisibleForTesting
     public static final int LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS = 250;
-    private static final int SINGLE_CELL_SPAN_SIZE = 1;
 
     private boolean mIsAppPredictorComponentAvailable;
     private AppPredictor mAppPredictor;
@@ -229,9 +229,6 @@
     private long mQueriedTargetServicesTimeMs;
     private long mQueriedSharingShortcutsTimeMs;
 
-    private RecyclerView mRecyclerView;
-    private ChooserListAdapter mChooserListAdapter;
-    private ChooserGridAdapter mChooserGridAdapter;
     private int mChooserRowServiceSpacing;
 
     private int mCurrAvailableWidth = 0;
@@ -265,6 +262,9 @@
 
     private ContentPreviewCoordinator mPreviewCoord;
 
+    @VisibleForTesting
+    protected ChooserMultiProfilePagerAdapter mChooserMultiProfilePagerAdapter;
+
     private class ContentPreviewCoordinator {
         private static final int IMAGE_FADE_IN_MILLIS = 150;
         private static final int IMAGE_LOAD_TIMEOUT = 1;
@@ -362,8 +362,8 @@
                 Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load"
                         + " within " + mImageLoadTimeoutMillis + "ms.");
                 collapseParentView();
-                if (mChooserGridAdapter != null) {
-                    mChooserGridAdapter.hideContentPreview();
+                if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) {
+                    mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().hideContentPreview();
                 }
                 mHideParentOnFail = false;
             }
@@ -431,13 +431,14 @@
                 logDirectShareTargetReceived(
                         MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE);
                 sendVoiceChoicesIfNeeded();
-                mChooserListAdapter.completeServiceTargetLoading();
+                mChooserMultiProfilePagerAdapter.getCurrentListAdapter()
+                        .completeServiceTargetLoading();
             }
         }
 
         @Override
         public void handleMessage(Message msg) {
-            if (mChooserListAdapter == null || isDestroyed()) {
+            if (mChooserMultiProfilePagerAdapter.getCurrentListAdapter() == null || isDestroyed()) {
                 return;
             }
 
@@ -452,8 +453,10 @@
                         break;
                     }
                     if (sri.resultTargets != null) {
-                        mChooserListAdapter.addServiceResults(sri.originalTarget,
-                                sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET);
+                        // TODO(arangelov): Instead of using getCurrentListAdapter(), pass the
+                        // profileId as part of the message.
+                        mChooserMultiProfilePagerAdapter.getCurrentListAdapter().addServiceResults(
+                                sri.originalTarget, sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET);
                     }
                     unbindService(sri.connection);
                     sri.connection.destroy();
@@ -476,15 +479,15 @@
                         Log.d(TAG, "LIST_VIEW_UPDATE_MESSAGE; ");
                     }
 
-                    mChooserListAdapter.refreshListView();
+                    mChooserMultiProfilePagerAdapter.getCurrentListAdapter().refreshListView();
                     break;
 
                 case SHORTCUT_MANAGER_SHARE_TARGET_RESULT:
                     if (DEBUG) Log.d(TAG, "SHORTCUT_MANAGER_SHARE_TARGET_RESULT");
                     final ServiceResultInfo resultInfo = (ServiceResultInfo) msg.obj;
                     if (resultInfo.resultTargets != null) {
-                        mChooserListAdapter.addServiceResults(resultInfo.originalTarget,
-                                resultInfo.resultTargets, msg.arg1);
+                        mChooserMultiProfilePagerAdapter.getCurrentListAdapter().addServiceResults(
+                                resultInfo.originalTarget, resultInfo.resultTargets, msg.arg1);
                     }
                     break;
 
@@ -504,6 +507,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         final long intentReceivedTime = System.currentTimeMillis();
         // This is the only place this value is being set. Effectively final.
+        //TODO(arangelov) - should there be a mIsAppPredictorComponentAvailable flag for work tab?
         mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable();
 
         mIsSuccessfullySelected = false;
@@ -638,19 +642,24 @@
         if (appPredictor != null) {
             mDirectShareAppTargetCache = new HashMap<>();
             mAppPredictorCallback = resultList -> {
+                //TODO(arangelov) Take care of edge case when callback called after swiping tabs
                 if (isFinishing() || isDestroyed()) {
                     return;
                 }
-                if (mChooserListAdapter.getCount() == 0) {
+                if (mChooserMultiProfilePagerAdapter.getCurrentListAdapter().getCount() == 0) {
                     return;
                 }
                 if (resultList.isEmpty()) {
                     // APS may be disabled, so try querying targets ourselves.
-                    queryDirectShareTargets(mChooserListAdapter, true);
+                    //TODO(arangelov) queryDirectShareTargets indirectly uses mIntents.
+                    // Investigate implications for work tab.
+                    queryDirectShareTargets(
+                            mChooserMultiProfilePagerAdapter.getCurrentListAdapter(), true);
                     return;
                 }
                 final List<DisplayResolveInfo> driList =
-                        getDisplayResolveInfos(mChooserListAdapter);
+                        getDisplayResolveInfos(
+                                mChooserMultiProfilePagerAdapter.getCurrentListAdapter());
                 final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos =
                         new ArrayList<>();
                 for (AppTarget appTarget : resultList) {
@@ -684,21 +693,22 @@
             final float chooserHeaderScrollElevation =
                     getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation);
 
-            mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
-                public void onScrollStateChanged(RecyclerView view, int scrollState) {
-                }
-
-                public void onScrolled(RecyclerView view, int dx, int dy) {
-                    if (view.getChildCount() > 0) {
-                        View child = view.getLayoutManager().findViewByPosition(0);
-                        if (child == null || child.getTop() < 0) {
-                            chooserHeader.setElevation(chooserHeaderScrollElevation);
-                            return;
+            mChooserMultiProfilePagerAdapter.getCurrentAdapterView().addOnScrollListener(
+                    new RecyclerView.OnScrollListener() {
+                        public void onScrollStateChanged(RecyclerView view, int scrollState) {
                         }
-                    }
 
-                    chooserHeader.setElevation(defaultElevation);
-                }
+                        public void onScrolled(RecyclerView view, int dx, int dy) {
+                            if (view.getChildCount() > 0) {
+                                View child = view.getLayoutManager().findViewByPosition(0);
+                                if (child == null || child.getTop() < 0) {
+                                    chooserHeader.setElevation(chooserHeaderScrollElevation);
+                                    return;
+                                }
+                            }
+
+                            chooserHeader.setElevation(defaultElevation);
+                        }
             });
 
             mResolverDrawerLayout.setOnCollapsedChangedListener(
@@ -738,6 +748,71 @@
         return context.getSharedPreferences(prefsFile, MODE_PRIVATE);
     }
 
+    @Override
+    protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList,
+            boolean filterLastUsed) {
+        if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+            mChooserMultiProfilePagerAdapter = createChooserMultiProfilePagerAdapterForTwoProfiles(
+                    initialIntents, rList, filterLastUsed);
+        } else {
+            mChooserMultiProfilePagerAdapter = createChooserMultiProfilePagerAdapterForOneProfile(
+                    initialIntents, rList, filterLastUsed);
+        }
+        return mChooserMultiProfilePagerAdapter;
+    }
+
+    private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForOneProfile(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList,
+            boolean filterLastUsed) {
+        ChooserGridAdapter adapter = createChooserGridAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ UserHandle.of(UserHandle.myUserId()));
+        return new ChooserMultiProfilePagerAdapter(
+                /* context */ this,
+                adapter);
+    }
+
+    private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList,
+            boolean filterLastUsed) {
+        ChooserGridAdapter personalAdapter = createChooserGridAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ getPersonalProfileUserHandle());
+        ChooserGridAdapter workAdapter = createChooserGridAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ getWorkProfileUserHandle());
+        return new ChooserMultiProfilePagerAdapter(
+                /* context */ this,
+                personalAdapter,
+                workAdapter,
+                /* defaultProfile */ getCurrentProfile());
+    }
+
+    @Override
+    protected boolean postRebuildList(boolean rebuildCompleted) {
+        mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().maybeLogActionShareWithPreview();
+        return postRebuildListInternal(rebuildCompleted);
+    }
+
     /**
      * Returns true if app prediction service is defined and the component exists on device.
      */
@@ -776,7 +851,7 @@
      * set up)
      */
     protected boolean isWorkProfile() {
-        return ((UserManager) getSystemService(Context.USER_SERVICE))
+        return getSystemService(UserManager.class)
                 .getUserInfo(UserHandle.myUserId()).isManagedProfile();
     }
 
@@ -785,7 +860,9 @@
         return new PackageMonitor() {
             @Override
             public void onSomePackagesChanged() {
-                mAdapter.handlePackagesChanged();
+                // TODO(arangelov): Dispatch this to all adapters when we have the helper methods
+                // in a follow-up CL
+                mChooserMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged();
                 updateProfileViewButton();
             }
         };
@@ -1239,36 +1316,14 @@
     }
 
     @Override
-    public void onPrepareAdapterView(ResolverListAdapter adapter, boolean isVisible) {
-        mRecyclerView = findViewById(R.id.resolver_list);
-        if (!isVisible) {
-            mRecyclerView.setVisibility(View.GONE);
-            return;
-        }
-        mRecyclerView.setVisibility(View.VISIBLE);
+    public void onPrepareAdapterView(ResolverListAdapter adapter) {
+        mChooserMultiProfilePagerAdapter.getCurrentAdapterView().setVisibility(View.VISIBLE);
         if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) {
-            mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets),
+            mChooserMultiProfilePagerAdapter.getCurrentListAdapter().addServiceResults(
+                    /* origTarget */ null,
+                    Lists.newArrayList(mCallerChooserTargets),
                     TARGET_TYPE_DEFAULT);
         }
-        mChooserGridAdapter = new ChooserGridAdapter(mChooserListAdapter);
-        GridLayoutManager glm = (GridLayoutManager) mRecyclerView.getLayoutManager();
-        glm.setSpanCount(mChooserGridAdapter.getMaxTargetsPerRow());
-        glm.setSpanSizeLookup(
-                new GridLayoutManager.SpanSizeLookup() {
-                    @Override
-                    public int getSpanSize(int position) {
-                        return mChooserGridAdapter.getItemViewType(position)
-                                == ChooserGridAdapter.VIEW_TYPE_NORMAL
-                                ? SINGLE_CELL_SPAN_SIZE
-                                : glm.getSpanCount();
-                    }
-                });
-    }
-
-    @Override
-    protected boolean postRebuildList(boolean rebuildCompleted) {
-        mChooserListAdapter = (ChooserListAdapter) mAdapter;
-        return postRebuildListInternal(rebuildCompleted);
     }
 
     @Override
@@ -1348,7 +1403,10 @@
 
     @Override
     public void startSelected(int which, boolean always, boolean filtered) {
-        TargetInfo targetInfo = mChooserListAdapter.targetInfoForPosition(which, filtered);
+        ChooserListAdapter currentListAdapter =
+                mChooserMultiProfilePagerAdapter.getCurrentListAdapter();
+        TargetInfo targetInfo = currentListAdapter
+                .targetInfoForPosition(which, filtered);
         if (targetInfo != null && targetInfo instanceof NotSelectableTargetInfo) {
             return;
         }
@@ -1356,7 +1414,7 @@
         final long selectionCost = System.currentTimeMillis() - mChooserShownTime;
         super.startSelected(which, always, filtered);
 
-        if (mChooserListAdapter.getCount() > 0) {
+        if (currentListAdapter.getCount() > 0) {
             // Log the index of which type of target the user picked.
             // Lower values mean the ranking was better.
             int cat = 0;
@@ -1364,13 +1422,12 @@
             int directTargetAlsoRanked = -1;
             int numCallerProvided = 0;
             HashedStringCache.HashResult directTargetHashed = null;
-            switch (mChooserListAdapter.getPositionTargetType(which)) {
+            switch (currentListAdapter.getPositionTargetType(which)) {
                 case ChooserListAdapter.TARGET_SERVICE:
                     cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET;
                     // Log the package name + target name to answer the question if most users
                     // share to mostly the same person or to a bunch of different people.
-                    ChooserTarget target =
-                            mChooserListAdapter.getChooserTargetForValue(value);
+                    ChooserTarget target = currentListAdapter.getChooserTargetForValue(value);
                     directTargetHashed = HashedStringCache.getInstance().hashString(
                             this,
                             TAG,
@@ -1386,8 +1443,8 @@
                 case ChooserListAdapter.TARGET_CALLER:
                 case ChooserListAdapter.TARGET_STANDARD:
                     cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET;
-                    value -= mChooserListAdapter.getSelectableServiceTargetCount();
-                    numCallerProvided = mChooserListAdapter.getCallerTargetCount();
+                    value -= currentListAdapter.getSelectableServiceTargetCount();
+                    numCallerProvided = currentListAdapter.getCallerTargetCount();
                     break;
                 case ChooserListAdapter.TARGET_STANDARD_AZ:
                     // A-Z targets are unranked standard targets; we use -1 to mark that they
@@ -1429,11 +1486,13 @@
     private int getRankedPosition(SelectableTargetInfo targetInfo) {
         String targetPackageName =
                 targetInfo.getChooserTarget().getComponentName().getPackageName();
-        int maxRankedResults = Math.min(mChooserListAdapter.mDisplayList.size(),
-                        MAX_LOG_RANK_POSITION);
+        ChooserListAdapter currentListAdapter =
+                mChooserMultiProfilePagerAdapter.getCurrentListAdapter();
+        int maxRankedResults = Math.min(currentListAdapter.mDisplayList.size(),
+                MAX_LOG_RANK_POSITION);
 
         for (int i = 0; i < maxRankedResults; i++) {
-            if (mChooserListAdapter.mDisplayList.get(i)
+            if (currentListAdapter.mDisplayList.get(i)
                     .getResolveInfo().activityInfo.packageName.equals(targetPackageName)) {
                 return i;
             }
@@ -1577,6 +1636,7 @@
             }
         }
         // Default to just querying ShortcutManager if AppPredictor not present.
+        //TODO(arangelov) we're using mIntents here, investicate possible implications on work tab
         final IntentFilter filter = getTargetIntentFilter();
         if (filter == null) {
             return;
@@ -1584,6 +1644,7 @@
         final List<DisplayResolveInfo> driList = getDisplayResolveInfos(adapter);
 
         AsyncTask.execute(() -> {
+            //TODO(arangelov) use the selected probile tab's ShortcutManager
             ShortcutManager sm = (ShortcutManager) getSystemService(Context.SHORTCUT_SERVICE);
             List<ShortcutManager.ShareShortcutInfo> resultList = sm.getShareTargets(filter);
             sendShareShortcutInfoList(resultList, driList, null);
@@ -1779,9 +1840,11 @@
             final ResolveInfo ri = info.getResolveInfo();
             Intent targetIntent = getTargetIntent();
             if (ri != null && ri.activityInfo != null && targetIntent != null) {
-                if (mAdapter != null) {
-                    mAdapter.updateModel(info.getResolvedComponentName());
-                    mAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(),
+                ChooserListAdapter currentListAdapter =
+                        mChooserMultiProfilePagerAdapter.getCurrentListAdapter();
+                if (currentListAdapter != null) {
+                    currentListAdapter.updateModel(info.getResolvedComponentName());
+                    currentListAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(),
                             targetIntent.getAction());
                 }
                 if (DEBUG) {
@@ -1956,8 +2019,9 @@
                 Intent targetIntent,
                 String referrerPackageName,
                 int launchedFromUid,
+                UserHandle userId,
                 AbstractResolverComparator resolverComparator) {
-            super(context, pm, targetIntent, referrerPackageName, launchedFromUid,
+            super(context, pm, targetIntent, referrerPackageName, launchedFromUid, userId,
                     resolverComparator);
         }
 
@@ -1980,17 +2044,18 @@
         }
     }
 
-    @Override
-    public ResolverListAdapter createAdapter(Context context, List<Intent> payloadIntents,
-            Intent[] initialIntents, List<ResolveInfo> rList,
-            boolean filterLastUsed, boolean useLayoutForBrowsables) {
-        return new ChooserListAdapter(context, payloadIntents,
-                initialIntents, rList, filterLastUsed, createListController(),
-                useLayoutForBrowsables, this, this);
+    @VisibleForTesting
+    public ChooserGridAdapter createChooserGridAdapter(Context context,
+            List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
+            boolean filterLastUsed, boolean useLayoutForBrowsables, UserHandle userHandle) {
+        return new ChooserGridAdapter(
+                new ChooserListAdapter(context, payloadIntents, initialIntents, rList,
+                        filterLastUsed, createListController(userHandle), useLayoutForBrowsables,
+                        this, this));
     }
 
     @VisibleForTesting
-    protected ResolverListController createListController() {
+    protected ResolverListController createListController(UserHandle userHandle) {
         AppPredictor appPredictor = getAppPredictorForShareActivitesIfEnabled();
         AbstractResolverComparator resolverComparator;
         if (appPredictor != null) {
@@ -2008,6 +2073,7 @@
                 getTargetIntent(),
                 getReferrerPackageName(),
                 mLaunchedFromUid,
+                userHandle,
                 resolverComparator);
     }
 
@@ -2041,8 +2107,8 @@
     }
 
     private void handleScroll(View view, int x, int y, int oldx, int oldy) {
-        if (mChooserGridAdapter != null) {
-            mChooserGridAdapter.handleScroll(view, y, oldy);
+        if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) {
+            mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().handleScroll(view, y, oldy);
         }
     }
 
@@ -2053,37 +2119,42 @@
      */
     private void handleLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
             int oldTop, int oldRight, int oldBottom) {
-        if (mChooserGridAdapter == null || mRecyclerView == null) {
+        if (mChooserMultiProfilePagerAdapter == null) {
+            return;
+        }
+        RecyclerView recyclerView = mChooserMultiProfilePagerAdapter.getCurrentAdapterView();
+        ChooserGridAdapter gridAdapter = mChooserMultiProfilePagerAdapter.getCurrentRootAdapter();
+        if (gridAdapter == null || recyclerView == null) {
             return;
         }
 
         final int availableWidth = right - left - v.getPaddingLeft() - v.getPaddingRight();
-        if (mChooserGridAdapter.consumeLayoutRequest()
-                || mChooserGridAdapter.calculateChooserTargetWidth(availableWidth)
-                || mRecyclerView.getAdapter() == null
+        if (gridAdapter.consumeLayoutRequest()
+                || gridAdapter.calculateChooserTargetWidth(availableWidth)
+                || recyclerView.getAdapter() == null
                 || availableWidth != mCurrAvailableWidth) {
             mCurrAvailableWidth = availableWidth;
-            mRecyclerView.setAdapter(mChooserGridAdapter);
-            ((GridLayoutManager) mRecyclerView.getLayoutManager())
-                    .setSpanCount(mChooserGridAdapter.getMaxTargetsPerRow());
+            recyclerView.setAdapter(gridAdapter);
+            ((GridLayoutManager) recyclerView.getLayoutManager())
+                    .setSpanCount(gridAdapter.getMaxTargetsPerRow());
 
             getMainThreadHandler().post(() -> {
-                if (mResolverDrawerLayout == null || mChooserGridAdapter == null) {
+                if (mResolverDrawerLayout == null || gridAdapter == null) {
                     return;
                 }
 
                 final int bottomInset = mSystemWindowInsets != null
                                             ? mSystemWindowInsets.bottom : 0;
                 int offset = bottomInset;
-                int rowsToShow = mChooserGridAdapter.getContentPreviewRowCount()
-                        + mChooserGridAdapter.getProfileRowCount()
-                        + mChooserGridAdapter.getServiceTargetRowCount()
-                        + mChooserGridAdapter.getCallerAndRankedTargetRowCount();
+                int rowsToShow = gridAdapter.getContentPreviewRowCount()
+                        + gridAdapter.getProfileRowCount()
+                        + gridAdapter.getServiceTargetRowCount()
+                        + gridAdapter.getCallerAndRankedTargetRowCount();
 
                 // then this is most likely not a SEND_* action, so check
                 // the app target count
                 if (rowsToShow == 0) {
-                    rowsToShow = mChooserGridAdapter.getRowCount();
+                    rowsToShow = gridAdapter.getRowCount();
                 }
 
                 // still zero? then use a default height and leave, which
@@ -2097,9 +2168,9 @@
 
                 int directShareHeight = 0;
                 rowsToShow = Math.min(4, rowsToShow);
-                for (int i = 0, childCount = mRecyclerView.getChildCount();
+                for (int i = 0, childCount = recyclerView.getChildCount();
                         i < childCount && rowsToShow > 0; i++) {
-                    View child = mRecyclerView.getChildAt(i);
+                    View child = recyclerView.getChildAt(i);
                     if (((GridLayoutManager.LayoutParams)
                             child.getLayoutParams()).getSpanIndex() != 0) {
                         continue;
@@ -2107,9 +2178,9 @@
                     int height = child.getHeight();
                     offset += height;
 
-                    if (mChooserGridAdapter.getTargetType(
-                            mRecyclerView.getChildAdapterPosition(child))
-                            == mChooserListAdapter.TARGET_SERVICE) {
+                    if (gridAdapter.getTargetType(
+                            recyclerView.getChildAdapterPosition(child))
+                            == ChooserListAdapter.TARGET_SERVICE) {
                         directShareHeight = height;
                     }
                     rowsToShow--;
@@ -2145,13 +2216,13 @@
     @Override // ResolverListCommunicator
     public void onHandlePackagesChanged() {
         mServicesRequested.clear();
-        mAdapter.notifyDataSetChanged();
+        mChooserMultiProfilePagerAdapter.getCurrentListAdapter().notifyDataSetChanged();
         super.onHandlePackagesChanged();
     }
 
     @Override // SelectableTargetInfoCommunicator
     public ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo info) {
-        return mChooserListAdapter.makePresentationGetter(info);
+        return mChooserMultiProfilePagerAdapter.getCurrentListAdapter().makePresentationGetter(info);
     }
 
     @Override // SelectableTargetInfoCommunicator
@@ -2161,9 +2232,9 @@
 
     @Override // ChooserListCommunicator
     public int getMaxRankedTargets() {
-        return mChooserGridAdapter == null
+        return mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() == null
                 ? ChooserGridAdapter.MAX_TARGETS_PER_ROW_PORTRAIT
-                : mChooserGridAdapter.getMaxTargetsPerRow();
+                : mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().getMaxTargetsPerRow();
     }
 
     @Override // ChooserListCommunicator
@@ -2174,19 +2245,21 @@
 
     @Override
     public void onListRebuilt() {
-        if (mChooserListAdapter.mDisplayList == null
-                || mChooserListAdapter.mDisplayList.isEmpty()) {
-            mChooserListAdapter.notifyDataSetChanged();
+        final ChooserListAdapter currentListAdapter =
+                mChooserMultiProfilePagerAdapter.getCurrentListAdapter();
+        if (currentListAdapter.mDisplayList == null
+                || currentListAdapter.mDisplayList.isEmpty()) {
+            currentListAdapter.notifyDataSetChanged();
         } else {
             new AsyncTask<Void, Void, Void>() {
                 @Override
                 protected Void doInBackground(Void... voids) {
-                    mChooserListAdapter.updateAlphabeticalList();
+                    currentListAdapter.updateAlphabeticalList();
                     return null;
                 }
                 @Override
                 protected void onPostExecute(Void aVoid) {
-                    mChooserListAdapter.notifyDataSetChanged();
+                    currentListAdapter.notifyDataSetChanged();
                 }
             }.execute();
         }
@@ -2202,14 +2275,14 @@
                 Log.d(TAG, "querying direct share targets from ShortcutManager");
             }
 
-            queryDirectShareTargets(mChooserListAdapter, false);
+            queryDirectShareTargets(currentListAdapter, false);
         }
         if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) {
             if (DEBUG) {
                 Log.d(TAG, "List built querying services");
             }
 
-            queryTargetServices(mChooserListAdapter);
+            queryTargetServices(currentListAdapter);
         }
     }
 
@@ -2250,8 +2323,8 @@
                         false/* always */, true/* filterd */));
                 itemView.setOnLongClickListener(v -> {
                     showTargetDetails(
-                            mChooserListAdapter.resolveInfoForPosition(
-                                    mListPosition, true/* filtered */));
+                            mChooserMultiProfilePagerAdapter.getCurrentListAdapter()
+                                    .resolveInfoForPosition(mListPosition, /* filtered */ true));
                     return true;
                 });
             }
@@ -2259,6 +2332,29 @@
     }
 
     /**
+     * Intentionally override the {@link ResolverActivity} implementation as we only need that
+     * implementation for the intent resolver case.
+     */
+    @Override
+    protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+        return insets.consumeSystemWindowInsets();
+    }
+
+    /**
+     * Intentionally override the {@link ResolverActivity} implementation as we only need that
+     * implementation for the intent resolver case.
+     */
+    @Override
+    public void onButtonClick(View v) {}
+
+    /**
+     * Intentionally override the {@link ResolverActivity} implementation as we only need that
+     * implementation for the intent resolver case.
+     */
+    @Override
+    protected void resetButtonBar() {}
+
+    /**
      * Adapter for all types of items and targets in ShareSheet.
      * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
      * row level by this adapter but not on the item level. Individual targets within the row are
@@ -2329,12 +2425,11 @@
             return false;
         }
 
-        private int getMaxTargetsPerRow() {
+        int getMaxTargetsPerRow() {
             int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
             if (shouldDisplayLandscape(getResources().getConfiguration().orientation)) {
                 maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
             }
-
             return maxTargets;
         }
 
@@ -2477,10 +2572,6 @@
         private ViewGroup createContentPreviewView(ViewGroup parent) {
             Intent targetIntent = getTargetIntent();
             int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
-
-            getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)
-                        .setSubtype(previewType));
-
             return displayContentPreview(previewType, targetIntent, mLayoutInflater, parent);
         }
 
@@ -2667,6 +2758,7 @@
 
             for (int i = 0; i < columnCount; i++) {
                 final View v = holder.getView(i);
+
                 if (start + i <= end) {
                     holder.setViewVisibility(i, View.VISIBLE);
                     holder.setItemIndex(i, start + i);
@@ -2712,9 +2804,29 @@
                     && !isInMultiWindowMode();
 
             if (mDirectShareViewHolder != null && canExpandDirectShare) {
-                mDirectShareViewHolder.handleScroll(mRecyclerView, y, oldy, getMaxTargetsPerRow());
+                mDirectShareViewHolder.handleScroll(
+                        mChooserMultiProfilePagerAdapter.getCurrentAdapterView(), y, oldy,
+                        getMaxTargetsPerRow());
             }
         }
+
+        public ChooserListAdapter getListAdapter() {
+            return mChooserListAdapter;
+        }
+
+        void maybeLogActionShareWithPreview() {
+            if (getContentPreviewRowCount() == 0) {
+                return;
+            }
+            Intent targetIntent = getTargetIntent();
+            int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
+            getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)
+                    .setSubtype(previewType));
+        }
+
+        boolean shouldCellSpan(int position) {
+            return getItemViewType(position) == VIEW_TYPE_NORMAL;
+        }
     }
 
     /**
@@ -2898,7 +3010,8 @@
 
                 // only expand if we have more than maxTargetsPerRow, and delay that decision
                 // until they start to scroll
-                if (mChooserListAdapter.getSelectableServiceTargetCount() <= maxTargetsPerRow) {
+                if (mChooserMultiProfilePagerAdapter.getCurrentListAdapter()
+                        .getSelectableServiceTargetCount() <= maxTargetsPerRow) {
                     mHideDirectShareExpansion = true;
                     return;
                 }
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
new file mode 100644
index 0000000..aa8ab28
--- /dev/null
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -0,0 +1,128 @@
+/*
+ * 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.internal.app;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.GridLayoutManager;
+import com.android.internal.widget.PagerAdapter;
+import com.android.internal.widget.RecyclerView;
+
+/**
+ * A {@link PagerAdapter} which describes the work and personal profile share sheet screens.
+ */
+@VisibleForTesting
+public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter {
+    private static final int SINGLE_CELL_SPAN_SIZE = 1;
+
+    private final ChooserProfileDescriptor[] mItems;
+
+    ChooserMultiProfilePagerAdapter(Context context,
+            ChooserActivity.ChooserGridAdapter adapter) {
+        super(context, /* currentPage */ 0);
+        mItems = new ChooserProfileDescriptor[] {
+                createProfileDescriptor(adapter)
+        };
+    }
+
+    ChooserMultiProfilePagerAdapter(Context context,
+            ChooserActivity.ChooserGridAdapter personalAdapter,
+            ChooserActivity.ChooserGridAdapter workAdapter,
+            @Profile int defaultProfile) {
+        super(context, /* currentPage */ defaultProfile);
+        mItems = new ChooserProfileDescriptor[] {
+                createProfileDescriptor(personalAdapter),
+                createProfileDescriptor(workAdapter)
+        };
+    }
+
+    private ChooserProfileDescriptor createProfileDescriptor(
+            ChooserActivity.ChooserGridAdapter adapter) {
+        final LayoutInflater inflater = LayoutInflater.from(getContext());
+        final ViewGroup rootView =
+                (ViewGroup) inflater.inflate(R.layout.chooser_list_per_profile, null, false);
+        return new ChooserProfileDescriptor(rootView, adapter);
+    }
+
+    RecyclerView getListViewForIndex(int index) {
+        return getItem(index).recyclerView;
+    }
+
+    @Override
+    ChooserProfileDescriptor getItem(int pageIndex) {
+        return mItems[pageIndex];
+    }
+
+    @Override
+    int getItemCount() {
+        return mItems.length;
+    }
+
+    @Override
+    ChooserActivity.ChooserGridAdapter getAdapterForIndex(int pageIndex) {
+        return mItems[pageIndex].chooserGridAdapter;
+    }
+
+    @Override
+    void setupListAdapter(int pageIndex) {
+        final RecyclerView recyclerView = getItem(pageIndex).recyclerView;
+        ChooserActivity.ChooserGridAdapter chooserGridAdapter =
+                getItem(pageIndex).chooserGridAdapter;
+        recyclerView.setAdapter(chooserGridAdapter);
+        GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager();
+        glm.setSpanCount(chooserGridAdapter.getMaxTargetsPerRow());
+        glm.setSpanSizeLookup(
+                new GridLayoutManager.SpanSizeLookup() {
+                    @Override
+                    public int getSpanSize(int position) {
+                        return chooserGridAdapter.shouldCellSpan(position)
+                                ? SINGLE_CELL_SPAN_SIZE
+                                : glm.getSpanCount();
+                    }
+                });
+    }
+
+    @Override
+    @VisibleForTesting
+    public ChooserListAdapter getCurrentListAdapter() {
+        return getAdapterForIndex(getCurrentPage()).getListAdapter();
+    }
+
+    @Override
+    ChooserActivity.ChooserGridAdapter getCurrentRootAdapter() {
+        return getAdapterForIndex(getCurrentPage());
+    }
+
+    @Override
+    RecyclerView getCurrentAdapterView() {
+        return getListViewForIndex(getCurrentPage());
+    }
+
+    class ChooserProfileDescriptor extends ProfileDescriptor {
+        private ChooserActivity.ChooserGridAdapter chooserGridAdapter;
+        private RecyclerView recyclerView;
+        ChooserProfileDescriptor(ViewGroup rootView, ChooserActivity.ChooserGridAdapter adapter) {
+            super(rootView);
+            chooserGridAdapter = adapter;
+            recyclerView = rootView.findViewById(R.id.resolver_list);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/app/ISoundTriggerService.aidl b/core/java/com/android/internal/app/ISoundTriggerService.aidl
index ea24d5f..d94294f 100644
--- a/core/java/com/android/internal/app/ISoundTriggerService.aidl
+++ b/core/java/com/android/internal/app/ISoundTriggerService.aidl
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.SoundTrigger;
+import android.hardware.soundtrigger.ModelParams;
 import android.os.Bundle;
 import android.os.ParcelUuid;
 
@@ -56,4 +57,16 @@
     int getModelState(in ParcelUuid soundModelId);
 
     @nullable SoundTrigger.ModuleProperties getModuleProperties();
+
+    int setParameter(in ParcelUuid soundModelId, in ModelParams modelParam,
+        int value);
+
+    /**
+     * @throws UnsupportedOperationException if hal or model do not support this API.
+     * @throws IllegalArgumentException if invalid model handle or parameter is passed.
+     */
+    int getParameter(in ParcelUuid soundModelId, in ModelParams modelParam);
+
+    @nullable SoundTrigger.ModelParamRange queryParameter(in ParcelUuid soundModelId,
+        in ModelParams modelParam);
 }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3c028d7..9cf5e9f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -18,11 +18,15 @@
 
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
+import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL;
+import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_WORK;
+
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.annotation.UiThread;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.ActivityThread;
 import android.app.VoiceInteractor.PickOptionRequest;
@@ -72,6 +76,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.Profile;
 import com.android.internal.app.chooser.DisplayResolveInfo;
 import com.android.internal.app.chooser.TargetInfo;
 import com.android.internal.content.PackageMonitor;
@@ -99,10 +104,7 @@
     public ResolverActivity() {
     }
 
-    @UnsupportedAppUsage
-    protected ResolverListAdapter mAdapter;
     private boolean mSafeForwardingMode;
-    private AbsListView mAdapterView;
     private Button mAlwaysButton;
     private Button mOnceButton;
     protected View mProfileView;
@@ -143,8 +145,16 @@
     private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
     private static final String OPEN_LINKS_COMPONENT_KEY = "app_link_state";
 
+    /**
+     * TODO(arangelov): Remove a couple of weeks after work/personal tabs are finalized.
+     */
+    static final boolean ENABLE_TABBED_VIEW = false;
+
     private final PackageMonitor mPackageMonitor = createPackageMonitor();
 
+    @VisibleForTesting
+    protected AbstractMultiProfilePagerAdapter mMultiProfilePagerAdapter;
+
     // Intent extra for connected audio devices
     public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device";
 
@@ -230,7 +240,7 @@
         return new PackageMonitor() {
             @Override
             public void onSomePackagesChanged() {
-                mAdapter.handlePackagesChanged();
+                mMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged();
                 updateProfileViewButton();
             }
 
@@ -325,15 +335,13 @@
 
         mSupportsAlwaysUseOption = supportsAlwaysUseOption;
 
-        // The last argument of createAdapter is whether to do special handling
+        // The last argument of createResolverListAdapter is whether to do special handling
         // of the last used choice to highlight it in the list.  We need to always
         // turn this off when running under voice interaction, since it results in
         // a more complicated UI that the current voice interaction flow is not able
         // to handle.
         boolean filterLastUsed = mSupportsAlwaysUseOption && !isVoiceInteraction();
-        mAdapter = createAdapter(this, mIntents, initialIntents, rList,
-                filterLastUsed, mUseLayoutForBrowsables);
-
+        mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
         if (configureContentView()) {
             return;
         }
@@ -364,15 +372,96 @@
         }
 
         final Set<String> categories = intent.getCategories();
-        MetricsLogger.action(this, mAdapter.hasFilteredItem()
+        MetricsLogger.action(this, mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem()
                 ? MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_APP_FEATURED
                 : MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_NONE_FEATURED,
                 intent.getAction() + ":" + intent.getType() + ":"
                         + (categories != null ? Arrays.toString(categories.toArray()) : ""));
     }
 
+    protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList,
+            boolean filterLastUsed) {
+        AbstractMultiProfilePagerAdapter resolverMultiProfilePagerAdapter = null;
+        if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+            resolverMultiProfilePagerAdapter =
+                    createResolverMultiProfilePagerAdapterForTwoProfiles(
+                            initialIntents, rList, filterLastUsed);
+        } else {
+            resolverMultiProfilePagerAdapter = createResolverMultiProfilePagerAdapterForOneProfile(
+                    initialIntents, rList, filterLastUsed);
+        }
+        return resolverMultiProfilePagerAdapter;
+    }
+
+    private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForOneProfile(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList, boolean filterLastUsed) {
+        ResolverListAdapter adapter = createResolverListAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ UserHandle.of(UserHandle.myUserId()));
+        return new ResolverMultiProfilePagerAdapter(
+                /* context */ this,
+                adapter);
+    }
+
+    private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForTwoProfiles(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList,
+            boolean filterLastUsed) {
+        ResolverListAdapter personalAdapter = createResolverListAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ getPersonalProfileUserHandle());
+        ResolverListAdapter workAdapter = createResolverListAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ getWorkProfileUserHandle());
+        return new ResolverMultiProfilePagerAdapter(
+                /* context */ this,
+                personalAdapter,
+                workAdapter,
+                /* defaultProfile */ getCurrentProfile());
+    }
+
+    protected @Profile int getCurrentProfile() {
+        return (UserHandle.myUserId() == UserHandle.USER_SYSTEM ? PROFILE_PERSONAL : PROFILE_WORK);
+    }
+
+    protected UserHandle getPersonalProfileUserHandle() {
+        return UserHandle.of(ActivityManager.getCurrentUser());
+    }
+    protected @Nullable UserHandle getWorkProfileUserHandle() {
+        UserManager userManager = getSystemService(UserManager.class);
+        for (final UserInfo userInfo : userManager.getProfiles(ActivityManager.getCurrentUser())) {
+            if (userInfo.isManagedProfile()) {
+                return userInfo.getUserHandle();
+            }
+        }
+        return null;
+    }
+
+    protected boolean hasWorkProfile() {
+        return getWorkProfileUserHandle() != null;
+    }
+
     protected void onProfileClick(View v) {
-        final DisplayResolveInfo dri = mAdapter.getOtherProfile();
+        final DisplayResolveInfo dri =
+                mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile();
         if (dri == null) {
             return;
         }
@@ -395,11 +484,13 @@
             if (mFooterSpacer == null) {
                 mFooterSpacer = new Space(getApplicationContext());
             } else {
-                ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+                ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
+                        .getCurrentAdapterView().removeFooterView(mFooterSpacer);
             }
             mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
                                                                        mSystemWindowInsets.bottom));
-            ((ListView) mAdapterView).addFooterView(mFooterSpacer);
+            ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
+                    .getCurrentAdapterView().addFooterView(mFooterSpacer);
         } else {
             View emptyView = findViewById(R.id.empty);
             if (emptyView != null) {
@@ -417,7 +508,7 @@
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        mAdapter.handlePackagesChanged();
+        mMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged();
 
         if (mSystemWindowInsets != null) {
             mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
@@ -432,9 +523,10 @@
             return;
         }
 
-        final Option[] options = new Option[mAdapter.getCount()];
+        int count = mMultiProfilePagerAdapter.getCurrentListAdapter().getCount();
+        final Option[] options = new Option[count];
         for (int i = 0, N = options.length; i < N; i++) {
-            TargetInfo target = mAdapter.getItem(i);
+            TargetInfo target = mMultiProfilePagerAdapter.getCurrentListAdapter().getItem(i);
             if (target == null) {
                 // If this occurs, a new set of targets is being loaded. Let that complete,
                 // and have the next call to send voice choices proceed instead.
@@ -483,8 +575,9 @@
             return;
         }
 
-        final DisplayResolveInfo dri = mAdapter.getOtherProfile();
-        if (dri != null) {
+        final DisplayResolveInfo dri =
+                mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile();
+        if (dri != null && !ENABLE_TABBED_VIEW) {
             mProfileView.setVisibility(View.VISIBLE);
             View text = mProfileView.findViewById(R.id.profile_button);
             if (!(text instanceof TextView)) {
@@ -535,7 +628,8 @@
 
         // While there may already be a filtered item, we can only use it in the title if the list
         // is already sorted and all information relevant to it is already in the list.
-        final boolean named = mAdapter.getFilteredPosition() >= 0;
+        final boolean named =
+                mMultiProfilePagerAdapter.getCurrentListAdapter().getFilteredPosition() >= 0;
         if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
             return getString(defaultTitleRes);
         } else if (isHttpSchemeAndViewAction(intent)) {
@@ -544,12 +638,14 @@
             String dialogTitle = null;
             if (named && !mUseLayoutForBrowsables) {
                 dialogTitle = getString(ActionTitle.BROWSABLE_APP_TITLE_RES,
-                        mAdapter.getFilteredItem().getDisplayLabel());
+                        mMultiProfilePagerAdapter.getCurrentListAdapter().getFilteredItem()
+                                .getDisplayLabel());
             } else if (named && mUseLayoutForBrowsables) {
                 dialogTitle = getString(ActionTitle.BROWSABLE_HOST_APP_TITLE_RES,
                         intent.getData().getHost(),
-                        mAdapter.getFilteredItem().getDisplayLabel());
-            } else if (mAdapter.areAllTargetsBrowsers()) {
+                        mMultiProfilePagerAdapter.getCurrentListAdapter().getFilteredItem()
+                                .getDisplayLabel());
+            } else if (mMultiProfilePagerAdapter.getCurrentListAdapter().areAllTargetsBrowsers()) {
                 dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
             } else {
                 dialogTitle = getString(ActionTitle.BROWSABLE_HOST_TITLE_RES,
@@ -558,7 +654,8 @@
             return dialogTitle;
         } else {
             return named
-                    ? getString(title.namedTitleRes, mAdapter.getFilteredItem().getDisplayLabel())
+                    ? getString(title.namedTitleRes, mMultiProfilePagerAdapter
+                            .getCurrentListAdapter().getFilteredItem().getDisplayLabel())
                     : getString(title.titleRes);
         }
     }
@@ -576,7 +673,7 @@
             mPackageMonitor.register(this, getMainLooper(), false);
             mRegistered = true;
         }
-        mAdapter.handlePackagesChanged();
+        mMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged();
         updateProfileViewButton();
     }
 
@@ -609,8 +706,8 @@
         if (!isChangingConfigurations() && mPickOptionRequest != null) {
             mPickOptionRequest.cancel();
         }
-        if (mAdapter != null) {
-            mAdapter.onDestroy();
+        if (mMultiProfilePagerAdapter.getCurrentListAdapter() != null) {
+            mMultiProfilePagerAdapter.getCurrentListAdapter().onDestroy();
         }
     }
 
@@ -660,7 +757,8 @@
         boolean enabled = false;
         ResolveInfo ri = null;
         if (hasValidSelection) {
-            ri = mAdapter.resolveInfoForPosition(checkedPos, filtered);
+            ri = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                    .resolveInfoForPosition(checkedPos, filtered);
             if (ri == null) {
                 Log.e(TAG, "Invalid position supplied to setAlwaysButtonEnabled");
                 return;
@@ -701,11 +799,13 @@
 
     public void onButtonClick(View v) {
         final int id = v.getId();
-        int which = mAdapter.hasFilteredItem()
-                ? mAdapter.getFilteredPosition()
-                : mAdapterView.getCheckedItemPosition();
-        boolean hasIndexBeenFiltered = !mAdapter.hasFilteredItem();
-        ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
+        ListView listView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
+        ResolverListAdapter currentListAdapter = mMultiProfilePagerAdapter.getCurrentListAdapter();
+        int which = currentListAdapter.hasFilteredItem()
+                ? currentListAdapter.getFilteredPosition()
+                : listView.getCheckedItemPosition();
+        boolean hasIndexBeenFiltered = !currentListAdapter.hasFilteredItem();
+        ResolveInfo ri = currentListAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
         if (mUseLayoutForBrowsables
                 && !ri.handleAllWebDataURI && id == R.id.button_always) {
             showSettingsForSelected(ri);
@@ -736,7 +836,8 @@
         if (isFinishing()) {
             return;
         }
-        ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
+        ResolveInfo ri = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                .resolveInfoForPosition(which, hasIndexBeenFiltered);
         if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
             Toast.makeText(this, String.format(getResources().getString(
                     com.android.internal.R.string.activity_resolver_work_profiles_support),
@@ -745,7 +846,8 @@
             return;
         }
 
-        TargetInfo target = mAdapter.targetInfoForPosition(which, hasIndexBeenFiltered);
+        TargetInfo target = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                .targetInfoForPosition(which, hasIndexBeenFiltered);
         if (target == null) {
             return;
         }
@@ -760,7 +862,8 @@
                 MetricsLogger.action(
                         this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_TAP);
             }
-            MetricsLogger.action(this, mAdapter.hasFilteredItem()
+            MetricsLogger.action(this,
+                    mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem()
                             ? MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_APP_FEATURED
                             : MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED);
             finish();
@@ -783,10 +886,11 @@
     }
 
     protected void onListRebuilt() {
-        int count = mAdapter.getUnfilteredCount();
-        if (count == 1 && mAdapter.getOtherProfile() == null) {
+        int count = mMultiProfilePagerAdapter.getCurrentListAdapter().getUnfilteredCount();
+        if (count == 1 && mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile() == null) {
             // Only one target, so we're a candidate to auto-launch!
-            final TargetInfo target = mAdapter.targetInfoForPosition(0, false);
+            final TargetInfo target =
+                    mMultiProfilePagerAdapter.getCurrentListAdapter().targetInfoForPosition(0, false);
             if (shouldAutoLaunchSingleChoice(target)) {
                 safelyStartActivity(target);
                 finish();
@@ -798,8 +902,9 @@
         final ResolveInfo ri = target.getResolveInfo();
         final Intent intent = target != null ? target.getResolvedIntent() : null;
 
-        if (intent != null && (mSupportsAlwaysUseOption || mAdapter.hasFilteredItem())
-                && mAdapter.mUnfilteredResolveList != null) {
+        if (intent != null && (mSupportsAlwaysUseOption
+                || mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem())
+                && mMultiProfilePagerAdapter.getCurrentListAdapter().getUnfilteredResolveList() != null) {
             // Build a reasonable intent filter, based on what matched.
             IntentFilter filter = new IntentFilter();
             Intent filterIntent;
@@ -884,13 +989,14 @@
             }
 
             if (filter != null) {
-                final int N = mAdapter.mUnfilteredResolveList.size();
+                final int N = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                        .getUnfilteredResolveList().size();
                 ComponentName[] set;
                 // If we don't add back in the component for forwarding the intent to a managed
                 // profile, the preferred activity may not be updated correctly (as the set of
                 // components we tell it we knew about will have changed).
                 final boolean needToAddBackProfileForwardingComponent =
-                        mAdapter.getOtherProfile() != null;
+                        mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile() != null;
                 if (!needToAddBackProfileForwardingComponent) {
                     set = new ComponentName[N];
                 } else {
@@ -899,15 +1005,18 @@
 
                 int bestMatch = 0;
                 for (int i=0; i<N; i++) {
-                    ResolveInfo r = mAdapter.mUnfilteredResolveList.get(i).getResolveInfoAt(0);
+                    ResolveInfo r = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                            .getUnfilteredResolveList().get(i).getResolveInfoAt(0);
                     set[i] = new ComponentName(r.activityInfo.packageName,
                             r.activityInfo.name);
                     if (r.match > bestMatch) bestMatch = r.match;
                 }
 
                 if (needToAddBackProfileForwardingComponent) {
-                    set[N] = mAdapter.getOtherProfile().getResolvedComponentName();
-                    final int otherProfileMatch = mAdapter.getOtherProfile().getResolveInfo().match;
+                    set[N] = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                            .getOtherProfile().getResolvedComponentName();
+                    final int otherProfileMatch = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                            .getOtherProfile().getResolveInfo().match;
                     if (otherProfileMatch > bestMatch) bestMatch = otherProfileMatch;
                 }
 
@@ -946,7 +1055,8 @@
                     }
                 } else {
                     try {
-                        mAdapter.mResolverListController.setLastChosen(intent, filter, bestMatch);
+                        mMultiProfilePagerAdapter.getCurrentListAdapter()
+                                .mResolverListController.setLastChosen(intent, filter, bestMatch);
                     } catch (RemoteException re) {
                         Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
                     }
@@ -984,14 +1094,15 @@
         if (mProfileSwitchMessageId != -1) {
             Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
         }
+        UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle();
         if (!mSafeForwardingMode) {
-            if (cti.start(this, null)) {
+            if (cti.startAsUser(this, null, currentUserHandle)) {
                 onActivityStarted(cti);
             }
             return;
         }
         try {
-            if (cti.startAsCaller(this, null, UserHandle.USER_NULL)) {
+            if (cti.startAsCaller(this, null, currentUserHandle.getIdentifier())) {
                 onActivityStarted(cti);
             }
         } catch (RuntimeException e) {
@@ -1061,26 +1172,27 @@
         startActivity(in);
     }
 
-    public ResolverListAdapter createAdapter(Context context, List<Intent> payloadIntents,
-            Intent[] initialIntents, List<ResolveInfo> rList,
-            boolean filterLastUsed, boolean useLayoutForBrowsables) {
-
+    @VisibleForTesting
+    protected ResolverListAdapter createResolverListAdapter(Context context,
+            List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
+            boolean filterLastUsed, boolean useLayoutForBrowsables, UserHandle userHandle) {
         Intent startIntent = getIntent();
         boolean isAudioCaptureDevice =
                 startIntent.getBooleanExtra(EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
         return new ResolverListAdapter(context, payloadIntents, initialIntents, rList,
-                filterLastUsed, createListController(), useLayoutForBrowsables, this,
+                filterLastUsed, createListController(userHandle), useLayoutForBrowsables, this,
                 isAudioCaptureDevice);
     }
 
     @VisibleForTesting
-    protected ResolverListController createListController() {
+    protected ResolverListController createListController(UserHandle userHandle) {
         return new ResolverListController(
                 this,
                 mPm,
                 getTargetIntent(),
                 getReferrerPackageName(),
-                mLaunchedFromUid);
+                mLaunchedFromUid,
+                userHandle);
     }
 
     /**
@@ -1088,16 +1200,17 @@
      * @return <code>true</code> if the activity is finishing and creation should halt.
      */
     private boolean configureContentView() {
-        if (mAdapter == null) {
+        if (mMultiProfilePagerAdapter.getCurrentListAdapter() == null) {
             throw new IllegalStateException("mAdapter cannot be null.");
         }
-        boolean rebuildCompleted = mAdapter.rebuildList();
+        boolean rebuildCompleted = mMultiProfilePagerAdapter.getCurrentListAdapter().rebuildList();
         if (useLayoutWithDefault()) {
             mLayoutId = R.layout.resolver_list_with_default;
         } else {
             mLayoutId = getLayoutResource();
         }
         setContentView(mLayoutId);
+        mMultiProfilePagerAdapter.setupViewPager(findViewById(R.id.profile_pager));
         return postRebuildList(rebuildCompleted);
     }
 
@@ -1118,14 +1231,16 @@
      */
     final boolean postRebuildListInternal(boolean rebuildCompleted) {
 
-        int count = mAdapter.getUnfilteredCount();
+        int count = mMultiProfilePagerAdapter.getCurrentListAdapter().getUnfilteredCount();
 
         // We only rebuild asynchronously when we have multiple elements to sort. In the case where
         // we're already done, we can check if we should auto-launch immediately.
         if (rebuildCompleted) {
-            if (count == 1 && mAdapter.getOtherProfile() == null) {
+            if (count == 1
+                    && mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile() == null) {
                 // Only one target, so we're a candidate to auto-launch!
-                final TargetInfo target = mAdapter.targetInfoForPosition(0, false);
+                final TargetInfo target = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                        .targetInfoForPosition(0, false);
                 if (shouldAutoLaunchSingleChoice(target)) {
                     safelyStartActivity(target);
                     mPackageMonitor.unregister();
@@ -1136,37 +1251,32 @@
             }
         }
 
-        boolean isAdapterViewVisible = true;
-        if (count == 0 && mAdapter.getPlaceholderCount() == 0) {
+        setupViewVisibilities(count);
+        return false;
+    }
+
+    private void setupViewVisibilities(int count) {
+        if (count == 0
+                && mMultiProfilePagerAdapter.getCurrentListAdapter().getPlaceholderCount() == 0) {
             final TextView emptyView = findViewById(R.id.empty);
             emptyView.setVisibility(View.VISIBLE);
-            isAdapterViewVisible = false;
+            findViewById(R.id.profile_pager).setVisibility(View.GONE);
+        } else {
+            onPrepareAdapterView(mMultiProfilePagerAdapter.getCurrentListAdapter());
         }
-
-        onPrepareAdapterView(mAdapter, isAdapterViewVisible);
-        return false;
     }
 
     /**
      * Prepare the scrollable view which consumes data in the list adapter.
      * @param adapter The adapter used to provide data to item views.
-     * @param isVisible True if the scrollable view should be visible; false, otherwise.
      */
-    public void onPrepareAdapterView(ResolverListAdapter adapter, boolean isVisible) {
-        mAdapterView = findViewById(R.id.resolver_list);
-        if (!isVisible) {
-            mAdapterView.setVisibility(View.GONE);
-            return;
-        }
-        mAdapterView.setVisibility(View.VISIBLE);
+    public void onPrepareAdapterView(ResolverListAdapter adapter) {
+        mMultiProfilePagerAdapter.getCurrentAdapterView().setVisibility(View.VISIBLE);
         final boolean useHeader = adapter.hasFilteredItem();
-        final ListView listView = mAdapterView instanceof ListView ? (ListView) mAdapterView : null;
-
-        mAdapterView.setAdapter(mAdapter);
-
+        final ListView listView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
         final ItemClickListener listener = new ItemClickListener();
-        mAdapterView.setOnItemClickListener(listener);
-        mAdapterView.setOnItemLongClickListener(listener);
+        listView.setOnItemClickListener(listener);
+        listView.setOnItemLongClickListener(listener);
 
         if (mSupportsAlwaysUseOption || mUseLayoutForBrowsables) {
             listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
@@ -1185,7 +1295,8 @@
      * Configure the area above the app selection list (title, content preview, etc).
      */
     public void setHeader() {
-        if (mAdapter.getCount() == 0 && mAdapter.getPlaceholderCount() == 0) {
+        if (mMultiProfilePagerAdapter.getCurrentListAdapter().getCount() == 0
+                && mMultiProfilePagerAdapter.getCurrentListAdapter().getPlaceholderCount() == 0) {
             final TextView titleView = findViewById(R.id.title);
             if (titleView != null) {
                 titleView.setVisibility(View.GONE);
@@ -1206,11 +1317,11 @@
 
         final ImageView iconView = findViewById(R.id.icon);
         if (iconView != null) {
-            mAdapter.loadFilteredItemIconTaskAsync(iconView);
+            mMultiProfilePagerAdapter.getCurrentListAdapter().loadFilteredItemIconTaskAsync(iconView);
         }
     }
 
-    private void resetButtonBar() {
+    protected void resetButtonBar() {
         if (!mSupportsAlwaysUseOption && !mUseLayoutForBrowsables) {
             return;
         }
@@ -1234,24 +1345,27 @@
     }
 
     private void resetAlwaysOrOnceButtonBar() {
-        if (useLayoutWithDefault()
-                && mAdapter.getFilteredPosition() != ListView.INVALID_POSITION) {
-            setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
+        int filteredPosition = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                .getFilteredPosition();
+        if (useLayoutWithDefault() && filteredPosition != ListView.INVALID_POSITION) {
+            setAlwaysButtonEnabled(true, filteredPosition, false);
             mOnceButton.setEnabled(true);
             return;
         }
 
         // When the items load in, if an item was already selected, enable the buttons
-        if (mAdapterView != null
-                && mAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) {
-            setAlwaysButtonEnabled(true, mAdapterView.getCheckedItemPosition(), true);
+        ListView currentAdapterView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
+        if (currentAdapterView != null
+                && currentAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) {
+            setAlwaysButtonEnabled(true, currentAdapterView.getCheckedItemPosition(), true);
             mOnceButton.setEnabled(true);
         }
     }
 
     @Override // ResolverListCommunicator
     public boolean useLayoutWithDefault() {
-        return mSupportsAlwaysUseOption && mAdapter.hasFilteredItem();
+        return mSupportsAlwaysUseOption
+                && mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem();
     }
 
     /**
@@ -1275,7 +1389,7 @@
 
     @Override // ResolverListCommunicator
     public void onHandlePackagesChanged() {
-        if (mAdapter.getCount() == 0) {
+        if (mMultiProfilePagerAdapter.getCurrentListAdapter().getCount() == 0) {
             // We no longer have any items...  just finish the activity.
             finish();
         }
@@ -1350,11 +1464,14 @@
                 return;
             }
             // If we're still loading, we can't yet enable the buttons.
-            if (mAdapter.resolveInfoForPosition(position, true) == null) {
+            if (mMultiProfilePagerAdapter.getCurrentListAdapter()
+                    .resolveInfoForPosition(position, true) == null) {
                 return;
             }
 
-            final int checkedPos = mAdapterView.getCheckedItemPosition();
+            ListView currentAdapterView =
+                    (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
+            final int checkedPos = currentAdapterView.getCheckedItemPosition();
             final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
             if (!useLayoutWithDefault()
                     && (!hasValidSelection || mLastSelected != checkedPos)
@@ -1362,7 +1479,7 @@
                 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
                 mOnceButton.setEnabled(hasValidSelection);
                 if (hasValidSelection) {
-                    mAdapterView.smoothScrollToPosition(checkedPos);
+                    currentAdapterView.smoothScrollToPosition(checkedPos);
                 }
                 mLastSelected = checkedPos;
             } else {
@@ -1380,7 +1497,8 @@
                 // Header views don't count.
                 return false;
             }
-            ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true);
+            ResolveInfo ri = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                    .resolveInfoForPosition(position, true);
             showTargetDetails(ri);
             return true;
         }
@@ -1420,7 +1538,8 @@
 
             final ResolverActivity ra = (ResolverActivity) getActivity();
             if (ra != null) {
-                final TargetInfo ti = ra.mAdapter.getItem(selections[0].getIndex());
+                final TargetInfo ti = ra.mMultiProfilePagerAdapter.getCurrentListAdapter()
+                        .getItem(selections[0].getIndex());
                 if (ra.onTargetSelected(ti, false)) {
                     ra.mPickOptionRequest = null;
                     ra.finish();
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index bb7ca35..48064da 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -37,7 +37,6 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -81,7 +80,7 @@
 
     // This one is the list that the Adapter will actually present.
     List<DisplayResolveInfo> mDisplayList;
-    List<ResolvedComponentInfo> mUnfilteredResolveList;
+    private List<ResolvedComponentInfo> mUnfilteredResolveList;
 
     private int mLastChosenPosition = -1;
     private boolean mFilterLastUsed;
@@ -162,6 +161,10 @@
         mResolverListController.updateChooserCounts(packageName, userId, action);
     }
 
+    List<ResolvedComponentInfo> getUnfilteredResolveList() {
+        return mUnfilteredResolveList;
+    }
+
     /**
      * @return true if all items in the display list are defined as browsers by
      *         ResolveInfo.handleAllWebDataURI
@@ -576,7 +579,7 @@
 
     Drawable loadIconForResolveInfo(ResolveInfo ri) {
         // Load icons based on the current process. If in work profile icons should be badged.
-        return makePresentationGetter(ri).getIcon(Process.myUserHandle());
+        return makePresentationGetter(ri).getIcon(mResolverListController.getUserHandle());
     }
 
     void loadFilteredItemIconTaskAsync(@NonNull ImageView iconView) {
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index b456ca0..abd3eb2 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -28,6 +28,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -55,6 +56,7 @@
 
     private static final String TAG = "ResolverListController";
     private static final boolean DEBUG = false;
+    private final UserHandle mUserHandle;
 
     private AbstractResolverComparator mResolverComparator;
     private boolean isComputed = false;
@@ -64,8 +66,9 @@
             PackageManager pm,
             Intent targetIntent,
             String referrerPackage,
-            int launchedFromUid) {
-        this(context, pm, targetIntent, referrerPackage, launchedFromUid,
+            int launchedFromUid,
+            UserHandle userHandle) {
+        this(context, pm, targetIntent, referrerPackage, launchedFromUid, userHandle,
                     new ResolverRankerServiceResolverComparator(
                         context, targetIntent, referrerPackage, null));
     }
@@ -76,12 +79,14 @@
             Intent targetIntent,
             String referrerPackage,
             int launchedFromUid,
+            UserHandle userHandle,
             AbstractResolverComparator resolverComparator) {
         mContext = context;
         mpm = pm;
         mLaunchedFromUid = launchedFromUid;
         mTargetIntent = targetIntent;
         mReferrerPackage = referrerPackage;
+        mUserHandle = userHandle;
         mResolverComparator = resolverComparator;
     }
 
@@ -116,7 +121,8 @@
                         || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
                 flags |= PackageManager.MATCH_INSTANT;
             }
-            final List<ResolveInfo> infos = mpm.queryIntentActivities(intent, flags);
+            final List<ResolveInfo> infos = mpm.queryIntentActivitiesAsUser(intent, flags,
+                    mUserHandle);
             if (infos != null) {
                 if (resolvedComponents == null) {
                     resolvedComponents = new ArrayList<>();
@@ -127,6 +133,10 @@
         return resolvedComponents;
     }
 
+    UserHandle getUserHandle() {
+        return mUserHandle;
+    }
+
     @VisibleForTesting
     public void addResolveListDedupe(List<ResolverActivity.ResolvedComponentInfo> into,
             Intent intent,
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
new file mode 100644
index 0000000..9e814ab
--- /dev/null
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -0,0 +1,113 @@
+/*
+ * 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.internal.app;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.PagerAdapter;
+
+/**
+ * A {@link PagerAdapter} which describes the work and personal profile intent resolver screens.
+ */
+@VisibleForTesting
+public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter {
+
+    private final ResolverProfileDescriptor[] mItems;
+
+    ResolverMultiProfilePagerAdapter(Context context,
+            ResolverListAdapter adapter) {
+        super(context, /* currentPage */ 0);
+        mItems = new ResolverProfileDescriptor[] {
+                createProfileDescriptor(adapter)
+        };
+    }
+
+    ResolverMultiProfilePagerAdapter(Context context,
+            ResolverListAdapter personalAdapter,
+            ResolverListAdapter workAdapter,
+            @Profile int defaultProfile) {
+        super(context, /* currentPage */ defaultProfile);
+        mItems = new ResolverProfileDescriptor[] {
+                createProfileDescriptor(personalAdapter),
+                createProfileDescriptor(workAdapter)
+        };
+    }
+
+    private ResolverProfileDescriptor createProfileDescriptor(
+            ResolverListAdapter adapter) {
+        final LayoutInflater inflater = LayoutInflater.from(getContext());
+        final ViewGroup rootView =
+                (ViewGroup) inflater.inflate(R.layout.resolver_list_per_profile, null, false);
+        return new ResolverProfileDescriptor(rootView, adapter);
+    }
+
+    ListView getListViewForIndex(int index) {
+        return getItem(index).listView;
+    }
+
+    @Override
+    ResolverProfileDescriptor getItem(int pageIndex) {
+        return mItems[pageIndex];
+    }
+
+    @Override
+    int getItemCount() {
+        return mItems.length;
+    }
+
+    @Override
+    void setupListAdapter(int pageIndex) {
+        final ListView listView = getItem(pageIndex).listView;
+        listView.setAdapter(getItem(pageIndex).resolverListAdapter);
+    }
+
+    @Override
+    ResolverListAdapter getAdapterForIndex(int pageIndex) {
+        return mItems[pageIndex].resolverListAdapter;
+    }
+
+    @Override
+    @VisibleForTesting
+    public ResolverListAdapter getCurrentListAdapter() {
+        return getAdapterForIndex(getCurrentPage());
+    }
+
+    @Override
+    ResolverListAdapter getCurrentRootAdapter() {
+        return getCurrentListAdapter();
+    }
+
+    @Override
+    ListView getCurrentAdapterView() {
+        return getListViewForIndex(getCurrentPage());
+    }
+
+    class ResolverProfileDescriptor extends ProfileDescriptor {
+        private ResolverListAdapter resolverListAdapter;
+        final ListView listView;
+        ResolverProfileDescriptor(ViewGroup rootView, ResolverListAdapter adapter) {
+            super(rootView);
+            resolverListAdapter = adapter;
+            listView = rootView.findViewById(R.id.resolver_list);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/app/WrapHeightViewPager.java b/core/java/com/android/internal/app/WrapHeightViewPager.java
new file mode 100644
index 0000000..b017bb4
--- /dev/null
+++ b/core/java/com/android/internal/app/WrapHeightViewPager.java
@@ -0,0 +1,71 @@
+/*
+ * 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.internal.app;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.internal.widget.ViewPager;
+
+/**
+ * A {@link ViewPager} which wraps around its first child's height.
+ * <p>Normally {@link ViewPager} instances expand their height to cover all remaining space in
+ * the layout.
+ * <p>This class is used for the intent resolver picker's tabbed view to maintain
+ * consistency with the previous behavior.
+ */
+public class WrapHeightViewPager extends ViewPager {
+
+    public WrapHeightViewPager(Context context) {
+        super(context);
+    }
+
+    public WrapHeightViewPager(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public WrapHeightViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public WrapHeightViewPager(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    // TODO(arangelov): When we have multiple pages, the height should wrap to the currently
+    // displayed page. Investigate whether onMeasure is called when changing a page, and instead
+    // of getChildAt(0), use the currently displayed one.
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.AT_MOST) {
+            return;
+        }
+        widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
+        int height = getMeasuredHeight();
+        if (getChildCount() > 0) {
+            View firstChild = getChildAt(0);
+            firstChild.measure(widthMeasureSpec,
+                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+            height = firstChild.getMeasuredHeight();
+        }
+        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+}
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index c8929e9..d78bd73 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -513,6 +513,11 @@
             try {
                 result = get();
             } catch (Exception t) {
+                // Exceptions coming out of get() are wrapped in ExecutionException, which is not
+                // handled by Parcel.
+                if (t instanceof ExecutionException && t.getCause() instanceof Exception) {
+                    t = (Exception) t.getCause();
+                }
                 dest.writeBoolean(true);
                 dest.writeException(t);
                 return;
diff --git a/core/java/com/android/internal/infra/TEST_MAPPING b/core/java/com/android/internal/infra/TEST_MAPPING
index 3781d63..3de107e 100644
--- a/core/java/com/android/internal/infra/TEST_MAPPING
+++ b/core/java/com/android/internal/infra/TEST_MAPPING
@@ -6,10 +6,18 @@
     {
       "name": "CtsPermissionTestCases",
       "options": [
-          {
-              "include-filter": "android.permission.cts.PermissionControllerTest"
-          }
+        {
+          "include-filter": "android.permission.cts.PermissionControllerTest"
+        }
+      ]
+    },
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "com.android.internal.infra."
+        }
       ]
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index bc44fcf..7140e7e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5267,7 +5267,7 @@
         // Unknown is included in DATA_CONNECTION_OTHER.
         int bin = DATA_CONNECTION_OUT_OF_SERVICE;
         if (hasData) {
-            if (dataType > 0 && dataType <= TelephonyManager.MAX_NETWORK_TYPE) {
+            if (dataType > 0 && dataType <= TelephonyManager.getAllNetworkTypes().length) {
                 bin = dataType;
             } else {
                 switch (serviceType) {
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index a211871..fa823c4 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -367,8 +367,8 @@
         if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
     }
 
-    protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
-            ClassLoader classLoader) {
+    protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
+            String[] argv, ClassLoader classLoader) {
         // If the application calls System.exit(), terminate the process
         // immediately without running any shutdown hooks.  It is not possible to
         // shutdown an Android application gracefully.  Among other things, the
@@ -377,6 +377,7 @@
         nativeSetExitWithoutCleanup(true);
 
         VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
+        VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
 
         final Arguments args = new Arguments(argv);
 
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index f0e7796..790d7f7 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -23,16 +23,18 @@
 import android.system.OsConstants;
 import android.system.StructCapUserData;
 import android.system.StructCapUserHeader;
-import android.util.TimingsTraceLog;
 import android.util.Slog;
+import android.util.TimingsTraceLog;
+
 import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+
 import java.io.DataOutputStream;
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.IOException;
 
-import libcore.io.IoUtils;
-
 /**
  * Startup class for the wrapper process.
  * @hide
@@ -166,10 +168,10 @@
             System.arraycopy(argv, 2, removedArgs, 0, argv.length - 2);
             argv = removedArgs;
         }
-
         // Perform the same initialization that would happen after the Zygote forks.
         Zygote.nativePreApplicationInit();
-        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
+        return RuntimeInit.applicationInit(targetSdkVersion, /*disabledCompatChanges*/ null,
+                argv, classLoader);
     }
 
     /**
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fbacdd7..2b988c1 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -672,6 +672,7 @@
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
             return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
+                                         args.mDisabledCompatChanges,
                                          args.mRemainingArgs,
                                          null /* classLoader */);
         } finally {
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index a23e659..54b2a20 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -215,6 +215,12 @@
     boolean mIsTopApp;
 
     /**
+     * A set of disabled app compatibility changes for the running app. From
+     * --disabled-compat-changes.
+     */
+    long[] mDisabledCompatChanges = null;
+
+    /**
      * Constructs instance and parses args
      *
      * @param args zygote command-line args
@@ -421,6 +427,16 @@
                 expectRuntimeArgs = false;
             } else if (arg.startsWith(Zygote.START_AS_TOP_APP_ARG)) {
                 mIsTopApp = true;
+            } else if (arg.startsWith("--disabled-compat-changes=")) {
+                if (mDisabledCompatChanges != null) {
+                    throw new IllegalArgumentException("Duplicate arg specified");
+                }
+                final String[] params = getAssignmentList(arg);
+                final int length = params.length;
+                mDisabledCompatChanges = new long[length];
+                for (int i = 0; i < length; i++) {
+                    mDisabledCompatChanges[i] = Long.parseLong(params[i]);
+                }
             } else {
                 break;
             }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index a14b093..3111b6f 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -501,6 +501,7 @@
         } else {
             if (!isZygote) {
                 return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
+                        parsedArgs.mDisabledCompatChanges,
                         parsedArgs.mRemainingArgs, null /* classLoader */);
             } else {
                 return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9ee79ea..49b4cf8 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -553,6 +553,7 @@
              * Pass the remaining arguments to SystemServer.
              */
             return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
+                    parsedArgs.mDisabledCompatChanges,
                     parsedArgs.mRemainingArgs, cl);
         }
 
@@ -988,14 +989,16 @@
      *
      * Current recognized args:
      * <ul>
-     *   <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
+     * <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
      * </ul>
      *
      * @param targetSdkVersion target SDK version
-     * @param argv arg strings
+     * @param disabledCompatChanges set of disabled compat changes for the process (all others
+     *                              are enabled)
+     * @param argv             arg strings
      */
-    public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,
-            ClassLoader classLoader) {
+    public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
+            String[] argv, ClassLoader classLoader) {
         if (RuntimeInit.DEBUG) {
             Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
         }
@@ -1005,7 +1008,8 @@
 
         RuntimeInit.commonInit();
         ZygoteInit.nativeZygoteInit();
-        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
+        return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
+                classLoader);
     }
 
     /**
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 764d4a59..9aa56f0 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -22,7 +22,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
@@ -2423,19 +2422,8 @@
 
         final Context context = getContext();
         final int targetSdk = context.getApplicationInfo().targetSdkVersion;
-        final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
-        final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
         final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
         final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;
-        final boolean targetHcNeedsOptions = context.getResources().getBoolean(
-                R.bool.target_honeycomb_needs_options_menu);
-        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
-
-        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
-            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
-        } else {
-            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
-        }
 
         if (!mForcedStatusBarColor) {
             mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 4dac542..4165f20 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -308,17 +308,6 @@
     }
 
     /**
-     * @see #add(List, Object)
-     */
-    public static @NonNull <K, V> Map<K, V> add(@Nullable Map<K, V> map, K key, V value) {
-        if (map == null || map == Collections.emptyMap()) {
-            map = new ArrayMap<>();
-        }
-        map.put(key, value);
-        return map;
-    }
-
-    /**
      * Similar to {@link List#remove}, but with support for list values of {@code null} and
      * {@link Collections#emptyList}
      */
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index e6232e85..49a73ee 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -226,7 +226,7 @@
      * Map of system pre-defined, uniquely named actors; keys are namespace,
      * value maps actor name to package name.
      */
-    private Map<String, Map<String, String>> mNamedActors = null;
+    private ArrayMap<String, ArrayMap<String, String>> mNamedActors = null;
 
     public static SystemConfig getInstance() {
         if (!isSystemProcess()) {
@@ -406,7 +406,7 @@
     }
 
     @NonNull
-    public Map<String, Map<String, String>> getNamedActors() {
+    public Map<String, ? extends Map<String, String>> getNamedActors() {
         return mNamedActors != null ? mNamedActors : Collections.emptyMap();
     }
 
@@ -1063,7 +1063,7 @@
                                 mNamedActors = new ArrayMap<>();
                             }
 
-                            Map<String, String> nameToPkgMap = mNamedActors.get(namespace);
+                            ArrayMap<String, String> nameToPkgMap = mNamedActors.get(namespace);
                             if (nameToPkgMap == null) {
                                 nameToPkgMap = new ArrayMap<>();
                                 mNamedActors.put(namespace, nameToPkgMap);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 025de5e..b91d359 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -144,6 +144,7 @@
                 "android_os_VintfRuntimeInfo.cpp",
                 "android_net_LocalSocketImpl.cpp",
                 "android_net_NetUtils.cpp",
+                "android_service_DataLoaderService.cpp",
                 "android_util_AssetManager.cpp",
                 "android_util_Binder.cpp",
                 "android_util_StatsLog.cpp",
@@ -240,6 +241,8 @@
                 "libGLESv1_CM",
                 "libGLESv2",
                 "libGLESv3",
+                "libincfs",
+                "libdataloader",
                 "libvulkan",
                 "libETC1",
                 "libhardware",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d92ab49..b8fd3ad 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -151,6 +151,7 @@
 extern int register_android_os_HidlMemory(JNIEnv* env);
 extern int register_android_os_MemoryFile(JNIEnv* env);
 extern int register_android_os_SharedMemory(JNIEnv* env);
+extern int register_android_service_DataLoaderService(JNIEnv* env);
 extern int register_android_net_LocalSocketImpl(JNIEnv* env);
 extern int register_android_net_NetworkUtils(JNIEnv* env);
 extern int register_android_text_AndroidCharacter(JNIEnv *env);
@@ -1452,6 +1453,7 @@
     REG_JNI(register_android_os_NativeHandle),
     REG_JNI(register_android_os_VintfObject),
     REG_JNI(register_android_os_VintfRuntimeInfo),
+    REG_JNI(register_android_service_DataLoaderService),
     REG_JNI(register_android_view_DisplayEventReceiver),
     REG_JNI(register_android_view_RenderNodeAnimator),
     REG_JNI(register_android_view_InputApplicationHandle),
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 3f05c3b..fa64fd1 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -7,7 +7,6 @@
 #include "SkAndroidCodec.h"
 #include "SkBRDAllocator.h"
 #include "SkFrontBufferedStream.h"
-#include "SkMakeUnique.h"
 #include "SkMath.h"
 #include "SkPixelRef.h"
 #include "SkStream.h"
@@ -586,7 +585,7 @@
     Asset* asset = reinterpret_cast<Asset*>(native_asset);
     // since we know we'll be done with the asset when we return, we can
     // just use a simple wrapper
-    return doDecode(env, skstd::make_unique<AssetStreamAdaptor>(asset), padding, options,
+    return doDecode(env, std::make_unique<AssetStreamAdaptor>(asset), padding, options,
                     inBitmapHandle, colorSpaceHandle);
 }
 
@@ -594,7 +593,7 @@
         jint offset, jint length, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) {
 
     AutoJavaByteArray ar(env, byteArray);
-    return doDecode(env, skstd::make_unique<SkMemoryStream>(ar.ptr() + offset, length, false),
+    return doDecode(env, std::make_unique<SkMemoryStream>(ar.ptr() + offset, length, false),
                     nullptr, options, inBitmapHandle, colorSpaceHandle);
 }
 
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 0002f8b..4376b0b 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -44,6 +44,13 @@
     jmethodID    toString;
 } gUUIDMethods;
 
+static const char* const kUnsupportedOperationExceptionClassPathName =
+     "java/lang/UnsupportedOperationException";
+static jclass gUnsupportedOperationExceptionClass;
+static const char* const kIllegalArgumentExceptionClassPathName =
+     "java/lang/IllegalArgumentException";
+static jclass gIllegalArgumentExceptionClass;
+
 static const char* const kSoundTriggerClassPathName = "android/hardware/soundtrigger/SoundTrigger";
 static jclass gSoundTriggerClass;
 
@@ -91,6 +98,11 @@
     jfieldID    keyphrases;
 } gKeyphraseSoundModelFields;
 
+static const char* const kModelParamRangeClassPathName =
+                                "android/hardware/soundtrigger/SoundTrigger$ModelParamRange";
+static jclass gModelParamRangeClass;
+static jmethodID gModelParamRangeCstor;
+
 static const char* const kRecognitionConfigClassPathName =
                                      "android/hardware/soundtrigger/SoundTrigger$RecognitionConfig";
 static jclass gRecognitionConfigClass;
@@ -164,6 +176,16 @@
     SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE = 4,
 };
 
+static jint throwUnsupportedOperationException(JNIEnv *env)
+{
+    return env->ThrowNew(gUnsupportedOperationExceptionClass, nullptr);
+}
+
+static jint throwIllegalArgumentException(JNIEnv *env)
+{
+    return env->ThrowNew(gIllegalArgumentExceptionClass, nullptr);
+}
+
 // ----------------------------------------------------------------------------
 // ref-counted object for callbacks
 class JNISoundTriggerCallback: public SoundTriggerCallback
@@ -822,6 +844,69 @@
     return status;
 }
 
+static jint
+android_hardware_SoundTrigger_setParameter(JNIEnv *env, jobject thiz,
+                                            jint jHandle, jint jModelParam, jint jValue)
+{
+    ALOGV("setParameter");
+    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
+    if (module == NULL) {
+        return SOUNDTRIGGER_STATUS_NO_INIT;
+    }
+    return module->setParameter(jHandle, (sound_trigger_model_parameter_t) jModelParam, jValue);
+}
+
+static jint
+android_hardware_SoundTrigger_getParameter(JNIEnv *env, jobject thiz,
+                                            jint jHandle, jint jModelParam)
+{
+    ALOGV("getParameter");
+    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
+    if (module == NULL) {
+        throwUnsupportedOperationException(env);
+        return -1;
+    }
+
+    jint nValue;
+    jint status = module->getParameter(jHandle,
+            (sound_trigger_model_parameter_t) jModelParam, &nValue);
+
+    switch (status) {
+        case 0:
+            return nValue;
+        case -EINVAL:
+            throwIllegalArgumentException(env);
+            break;
+        default:
+            throwUnsupportedOperationException(env);
+            break;
+    }
+
+    return -1;
+}
+
+static jobject
+android_hardware_SoundTrigger_queryParameter(JNIEnv *env, jobject thiz,
+                                            jint jHandle, jint jModelParam)
+{
+    ALOGV("queryParameter");
+    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
+    if (module == nullptr) {
+        return nullptr;
+    }
+
+    sound_trigger_model_parameter_range_t nRange;
+    jint nValue = module->queryParameter(jHandle,
+            (sound_trigger_model_parameter_t) jModelParam, &nRange);
+
+    if (nValue != 0) {
+        ALOGE("failed to query parameter error code: %d", nValue);
+        return nullptr;
+    }
+
+    return env->NewObject(gModelParamRangeClass, gModelParamRangeCstor, nRange.start, nRange.end);
+}
+
 static const JNINativeMethod gMethods[] = {
     {"listModules",
         "(Ljava/lang/String;Ljava/util/ArrayList;)I",
@@ -854,6 +939,15 @@
     {"getModelState",
         "(I)I",
         (void *)android_hardware_SoundTrigger_getModelState},
+    {"setParameter",
+         "(III)I",
+         (void *)android_hardware_SoundTrigger_setParameter},
+    {"getParameter",
+         "(II)I",
+         (void *)android_hardware_SoundTrigger_getParameter},
+    {"queryParameter",
+         "(II)Landroid/hardware/soundtrigger/SoundTrigger$ModelParamRange;",
+         (void *)android_hardware_SoundTrigger_queryParameter}
 };
 
 int register_android_hardware_SoundTrigger(JNIEnv *env)
@@ -866,6 +960,12 @@
     gUUIDClass = MakeGlobalRefOrDie(env, uuidClass);
     gUUIDMethods.toString = GetMethodIDOrDie(env, uuidClass, "toString", "()Ljava/lang/String;");
 
+    jclass exUClass = FindClassOrDie(env, kUnsupportedOperationExceptionClassPathName);
+    gUnsupportedOperationExceptionClass = MakeGlobalRefOrDie(env, exUClass);
+
+    jclass exIClass = FindClassOrDie(env, kIllegalArgumentExceptionClassPathName);
+    gIllegalArgumentExceptionClass = MakeGlobalRefOrDie(env, exIClass);
+
     jclass lClass = FindClassOrDie(env, kSoundTriggerClassPathName);
     gSoundTriggerClass = MakeGlobalRefOrDie(env, lClass);
 
@@ -906,6 +1006,10 @@
                                          "keyphrases",
                                          "[Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;");
 
+    jclass modelParamRangeClass = FindClassOrDie(env, kModelParamRangeClassPathName);
+    gModelParamRangeClass = MakeGlobalRefOrDie(env, modelParamRangeClass);
+    gModelParamRangeCstor = GetMethodIDOrDie(env, modelParamRangeClass, "<init>", "(II)V");
+
     jclass recognitionEventClass = FindClassOrDie(env, kRecognitionEventClassPathName);
     gRecognitionEventClass = MakeGlobalRefOrDie(env, recognitionEventClass);
     gRecognitionEventCstor = GetMethodIDOrDie(env, recognitionEventClass, "<init>",
diff --git a/core/jni/android_service_DataLoaderService.cpp b/core/jni/android_service_DataLoaderService.cpp
new file mode 100644
index 0000000..4c0f55f
--- /dev/null
+++ b/core/jni/android_service_DataLoaderService.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "dataloader-jni"
+
+#include <vector>
+
+#include "core_jni_helpers.h"
+#include "dataloader_ndk.h"
+#include "jni.h"
+
+namespace android {
+namespace {
+
+struct JniIds {
+    jfieldID dataBlockFileIno;
+    jfieldID dataBlockBlockIndex;
+    jfieldID dataBlockDataBytes;
+    jfieldID dataBlockCompressionType;
+
+    JniIds(JNIEnv* env) {
+        const auto dataBlock =
+                FindClassOrDie(env,
+                               "android/service/incremental/"
+                               "IncrementalDataLoaderService$FileSystemConnector$DataBlock");
+        dataBlockFileIno = GetFieldIDOrDie(env, dataBlock, "mFileIno", "J");
+        dataBlockBlockIndex =
+                GetFieldIDOrDie(env, dataBlock, "mBlockIndex", "I");
+        dataBlockDataBytes = GetFieldIDOrDie(env, dataBlock, "mDataBytes", "[B");
+        dataBlockCompressionType =
+                GetFieldIDOrDie(env, dataBlock, "mCompressionType", "I");
+    }
+};
+
+const JniIds& jniIds(JNIEnv* env) {
+    static const JniIds ids(env);
+    return ids;
+}
+
+class ScopedJniArrayCritical {
+public:
+    ScopedJniArrayCritical(JNIEnv* env, jarray array) : mEnv(env), mArr(array) {
+        mPtr = array ? env->GetPrimitiveArrayCritical(array, nullptr) : nullptr;
+    }
+    ~ScopedJniArrayCritical() {
+        if (mPtr) {
+            mEnv->ReleasePrimitiveArrayCritical(mArr, mPtr, 0);
+            mPtr = nullptr;
+        }
+    }
+
+    ScopedJniArrayCritical(const ScopedJniArrayCritical&) = delete;
+    void operator=(const ScopedJniArrayCritical&) = delete;
+
+    ScopedJniArrayCritical(ScopedJniArrayCritical&& other)
+        : mEnv(other.mEnv),
+          mArr(std::exchange(mArr, nullptr)),
+          mPtr(std::exchange(mPtr, nullptr)) {}
+    ScopedJniArrayCritical& operator=(ScopedJniArrayCritical&& other) {
+        mEnv = other.mEnv;
+        mArr = std::exchange(other.mArr, nullptr);
+        mPtr = std::exchange(other.mPtr, nullptr);
+        return *this;
+    }
+
+    void* ptr() const { return mPtr; }
+    jsize size() const { return mArr ? mEnv->GetArrayLength(mArr) : 0; }
+
+private:
+    JNIEnv* mEnv;
+    jarray mArr;
+    void* mPtr;
+};
+
+static jboolean nativeCreateDataLoader(JNIEnv* env,
+                                       jobject thiz,
+                                       jint storageId,
+                                       jobject control,
+                                       jobject params,
+                                       jobject callback) {
+    ALOGE("nativeCreateDataLoader: %p/%d, %d, %p, %p, %p", thiz,
+          env->GetObjectRefType(thiz), storageId, params, control, callback);
+    return DataLoaderService_OnCreate(env, thiz,
+                     storageId, control, params, callback);
+}
+
+static jboolean nativeStartDataLoader(JNIEnv* env,
+                                      jobject thiz,
+                                      jint storageId) {
+    ALOGE("nativeStartDataLoader: %p/%d, %d", thiz, env->GetObjectRefType(thiz),
+          storageId);
+    return DataLoaderService_OnStart(storageId);
+}
+
+static jboolean nativeStopDataLoader(JNIEnv* env,
+                                     jobject thiz,
+                                     jint storageId) {
+    ALOGE("nativeStopDataLoader: %p/%d, %d", thiz, env->GetObjectRefType(thiz),
+          storageId);
+    return DataLoaderService_OnStop(storageId);
+}
+
+static jboolean nativeDestroyDataLoader(JNIEnv* env,
+                                        jobject thiz,
+                                        jint storageId) {
+    ALOGE("nativeDestroyDataLoader: %p/%d, %d", thiz,
+          env->GetObjectRefType(thiz), storageId);
+    return DataLoaderService_OnDestroy(storageId);
+}
+
+
+static jboolean nativeOnFileCreated(JNIEnv* env,
+                                   jobject thiz,
+                                   jint storageId,
+                                   jlong inode,
+                                   jbyteArray metadata) {
+    ALOGE("nativeOnFileCreated: %p/%d, %d", thiz,
+          env->GetObjectRefType(thiz), storageId);
+    return DataLoaderService_OnFileCreated(storageId, inode, metadata);
+}
+
+static jboolean nativeIsFileRangeLoadedNode(JNIEnv* env,
+                                            jobject clazz,
+                                            jlong self,
+                                            jlong node,
+                                            jlong start,
+                                            jlong end) {
+    // TODO(b/136132412): implement this
+    return JNI_FALSE;
+}
+
+static jboolean nativeWriteMissingData(JNIEnv* env,
+                                       jobject clazz,
+                                       jlong self,
+                                       jobjectArray data_block,
+                                       jobjectArray hash_blocks) {
+    const auto& jni = jniIds(env);
+    auto length = env->GetArrayLength(data_block);
+    std::vector<incfs_new_data_block> instructions(length);
+
+    // May not call back into Java after even a single jniArrayCritical, so
+    // let's collect the Java pointers to byte buffers first and lock them in
+    // memory later.
+
+    std::vector<jbyteArray> blockBuffers(length);
+    for (int i = 0; i != length; ++i) {
+        auto& inst = instructions[i];
+        auto jniBlock = env->GetObjectArrayElement(data_block, i);
+        inst.file_ino = env->GetLongField(jniBlock, jni.dataBlockFileIno);
+        inst.block_index = env->GetIntField(jniBlock, jni.dataBlockBlockIndex);
+        blockBuffers[i] = (jbyteArray)env->GetObjectField(
+                jniBlock, jni.dataBlockDataBytes);
+        inst.compression = (incfs_compression_alg)env->GetIntField(
+                jniBlock, jni.dataBlockCompressionType);
+    }
+
+    std::vector<ScopedJniArrayCritical> jniScopedArrays;
+    jniScopedArrays.reserve(length);
+    for (int i = 0; i != length; ++i) {
+        auto buffer = blockBuffers[i];
+        jniScopedArrays.emplace_back(env, buffer);
+        auto& inst = instructions[i];
+        inst.data = (uint64_t)jniScopedArrays.back().ptr();
+        inst.data_len = jniScopedArrays.back().size();
+    }
+
+    auto connector = (DataLoaderFilesystemConnectorPtr)self;
+    if (auto err = DataLoader_FilesystemConnector_writeBlocks(
+                             connector, instructions.data(), length);
+        err < 0) {
+        jniScopedArrays.clear();
+        return JNI_FALSE;
+    }
+
+    return JNI_TRUE;
+}
+
+static jboolean nativeWriteSignerDataNode(JNIEnv* env,
+                                          jobject clazz,
+                                          jlong self,
+                                          jstring relative_path,
+                                          jbyteArray signer_data) {
+    // TODO(b/136132412): implement this
+    return JNI_TRUE;
+}
+
+static jbyteArray nativeGetFileMetadataNode(JNIEnv* env,
+                                            jobject clazz,
+                                            jlong self,
+                                            jlong inode) {
+    auto connector = (DataLoaderFilesystemConnectorPtr)self;
+    std::vector<char> metadata(INCFS_MAX_FILE_ATTR_SIZE);
+    size_t size = metadata.size();
+    if (DataLoader_FilesystemConnector_getRawMetadata(connector, inode,
+                  metadata.data(), &size) < 0) {
+        size = 0;
+    }
+    metadata.resize(size);
+
+    auto buffer = env->NewByteArray(metadata.size());
+    env->SetByteArrayRegion(buffer, 0, metadata.size(),
+                            (jbyte*)metadata.data());
+    return buffer;
+}
+
+static jbyteArray nativeGetFileInfoNode(JNIEnv* env,
+                                        jobject clazz,
+                                        jlong self,
+                                        jlong inode) {
+    // TODO(b/136132412): implement this
+    return nullptr;
+}
+
+static jboolean nativeReportStatus(JNIEnv* env,
+                                   jobject clazz,
+                                   jlong self,
+                                   jint status) {
+    auto listener = (DataLoaderStatusListenerPtr)self;
+    return DataLoader_StatusListener_reportStatus(listener,
+                     (DataLoaderStatus)status);
+}
+
+static const JNINativeMethod dlc_method_table[] = {
+        {"nativeCreateDataLoader",
+         "(ILandroid/os/incremental/IncrementalFileSystemControlParcel;"
+         "Landroid/os/incremental/IncrementalDataLoaderParamsParcel;"
+         "Landroid/content/pm/IDataLoaderStatusListener;)Z",
+         (void*)nativeCreateDataLoader},
+        {"nativeStartDataLoader", "(I)Z", (void*)nativeStartDataLoader},
+        {"nativeStopDataLoader", "(I)Z", (void*)nativeStopDataLoader},
+        {"nativeDestroyDataLoader", "(I)Z", (void*)nativeDestroyDataLoader},
+        {"nativeIsFileRangeLoadedNode", "(JJJJ)Z",
+         (void*)nativeIsFileRangeLoadedNode},
+        {"nativeWriteMissingData",
+         "(J[Landroid/service/incremental/"
+         "IncrementalDataLoaderService$FileSystemConnector$DataBlock;[Landroid/service/incremental/"
+         "IncrementalDataLoaderService$FileSystemConnector$HashBlock;)Z",
+         (void*)nativeWriteMissingData},
+        {"nativeWriteSignerDataNode", "(JJ[B)Z",
+         (void*)nativeWriteSignerDataNode},
+        {"nativeGetFileMetadataNode", "(JJ)[B",
+         (void*)nativeGetFileMetadataNode},
+        {"nativeGetFileInfoNode", "(JJ)[B", (void*)nativeGetFileInfoNode},
+        {"nativeReportStatus", "(JI)Z", (void*)nativeReportStatus},
+        {"nativeOnFileCreated", "(IJ[B)Z", (void*)nativeOnFileCreated},
+};
+
+}  // namespace
+
+int register_android_service_DataLoaderService(JNIEnv* env) {
+    return jniRegisterNativeMethods(env,
+                                    "android/service/incremental/IncrementalDataLoaderService",
+                                    dlc_method_table, NELEM(dlc_method_table));
+}
+
+}  // namespace android
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 24456d8..0c74842 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -163,6 +163,7 @@
     repeated IdentifierProto opening_apps = 17;
     repeated IdentifierProto closing_apps = 18;
     repeated IdentifierProto changing_apps = 19;
+    repeated WindowTokenProto overlay_windows = 20;
 }
 
 /* represents DisplayFrames */
diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto
index 93a9fe2..e7c2827 100644
--- a/core/proto/android/view/windowlayoutparams.proto
+++ b/core/proto/android/view/windowlayoutparams.proto
@@ -56,13 +56,6 @@
     optional uint32 input_feature_flags = 19;
     optional int64 user_activity_timeout = 20;
 
-    enum NeedsMenuState {
-        NEEDS_MENU_UNSET = 0;
-        NEEDS_MENU_SET_TRUE = 1;
-        NEEDS_MENU_SET_FALSE = 2;
-    }
-    optional NeedsMenuState needs_menu_key = 22;
-
     optional DisplayProto.ColorMode color_mode = 23;
     optional uint32 flags = 24;
     optional uint32 private_flags = 26;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8f1114c..c925744 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3264,13 +3264,6 @@
     <permission android:name="android.permission.BIND_TV_INPUT"
         android:protectionLevel="signature|privileged" />
 
-    <!-- Must be required by an {@link android.service.sms.FinancialSmsService}
-         to ensure that only the system can bind to it.
-         @hide This is not a third-party API (intended for OEMs and system apps).
-    -->
-    <permission android:name="android.permission.BIND_FINANCIAL_SMS_SERVICE"
-                android:protectionLevel="signature" />
-
     <!-- @SystemApi
          Must be required by a {@link com.android.media.tv.remoteprovider.TvRemoteProvider}
          to ensure that only the system can bind to it.
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 6807f9a..9f296f8f 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -29,7 +29,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alwaysShow="true"
-        android:elevation="1dp"
+        android:elevation="0dp"
         android:background="@drawable/bottomsheet_background">
 
         <ImageView
@@ -55,16 +55,10 @@
                   android:layout_centerHorizontal="true"/>
     </RelativeLayout>
 
-    <com.android.internal.widget.RecyclerView
+    <com.android.internal.widget.ViewPager
+        android:id="@+id/profile_pager"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layoutManager="com.android.internal.widget.GridLayoutManager"
-        android:id="@+id/resolver_list"
-        android:clipToPadding="false"
-        android:background="?attr/colorBackgroundFloating"
-        android:scrollbars="none"
-        android:elevation="1dp"
-        android:nestedScrollingEnabled="true"/>
+        android:layout_height="wrap_content"/>
 
     <TextView android:id="@+id/empty"
               android:layout_width="match_parent"
diff --git a/core/res/res/layout/chooser_list_per_profile.xml b/core/res/res/layout/chooser_list_per_profile.xml
new file mode 100644
index 0000000..212813f
--- /dev/null
+++ b/core/res/res/layout/chooser_list_per_profile.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<com.android.internal.widget.RecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layoutManager="com.android.internal.widget.GridLayoutManager"
+    android:id="@+id/resolver_list"
+    android:clipToPadding="false"
+    android:background="?attr/colorBackgroundFloating"
+    android:scrollbars="none"
+    android:elevation="1dp"
+    android:nestedScrollingEnabled="true"/>
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 6e45e7a..c5d8912 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -63,32 +63,28 @@
     </RelativeLayout>
 
     <View
-        android:layout_alwaysShow="true"
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:background="?attr/colorBackgroundFloating"
-        android:foreground="?attr/dividerVertical" />
-    <ListView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:id="@+id/resolver_list"
-        android:clipToPadding="false"
-        android:background="?attr/colorBackgroundFloating"
-        android:elevation="@dimen/resolver_elevation"
-        android:nestedScrollingEnabled="true"
-        android:scrollbarStyle="outsideOverlay"
-        android:scrollIndicators="top|bottom"
-        android:divider="?attr/dividerVertical"
-        android:footerDividersEnabled="false"
-        android:headerDividersEnabled="false"
-        android:dividerHeight="1dp" />
-    <View
+        android:id="@+id/divider"
         android:layout_alwaysShow="true"
         android:layout_width="match_parent"
         android:layout_height="1dp"
         android:background="?attr/colorBackgroundFloating"
         android:foreground="?attr/dividerVertical" />
 
+    <com.android.internal.app.WrapHeightViewPager
+        android:id="@+id/profile_pager"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:divider="?attr/dividerVertical"
+        android:footerDividersEnabled="false"
+        android:headerDividersEnabled="false"
+        android:dividerHeight="1dp"/>
+
+    <View
+        android:layout_alwaysShow="true"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?attr/colorBackgroundFloating"
+        android:foreground="?attr/dividerVertical" />
 
     <TextView android:id="@+id/empty"
               android:layout_width="match_parent"
diff --git a/core/res/res/layout/resolver_list_per_profile.xml b/core/res/res/layout/resolver_list_per_profile.xml
new file mode 100644
index 0000000..68b9917
--- /dev/null
+++ b/core/res/res/layout/resolver_list_per_profile.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<ListView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:id="@+id/resolver_list"
+    android:clipToPadding="false"
+    android:background="?attr/colorBackgroundFloating"
+    android:elevation="@dimen/resolver_elevation"
+    android:nestedScrollingEnabled="true"
+    android:scrollbarStyle="outsideOverlay"
+    android:scrollIndicators="top|bottom"
+    android:divider="?attr/dividerVertical"
+    android:footerDividersEnabled="false"
+    android:headerDividersEnabled="false"
+    android:dividerHeight="1dp" />
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index dbba0b7..5b3d929 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -144,25 +144,21 @@
     </LinearLayout>
 
     <View
+        android:id="@+id/divider"
         android:layout_alwaysShow="true"
         android:layout_width="match_parent"
         android:layout_height="1dp"
         android:background="?attr/colorBackgroundFloating"
         android:foreground="?attr/dividerVertical" />
-    <ListView
+
+    <com.android.internal.app.WrapHeightViewPager
+        android:id="@+id/profile_pager"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:id="@+id/resolver_list"
-        android:clipToPadding="false"
-        android:background="?attr/colorBackgroundFloating"
-        android:elevation="@dimen/resolver_elevation"
-        android:nestedScrollingEnabled="true"
-        android:scrollbarStyle="outsideOverlay"
-        android:scrollIndicators="top|bottom"
+        android:dividerHeight="1dp"
         android:divider="?attr/dividerVertical"
         android:footerDividersEnabled="false"
-        android:headerDividersEnabled="false"
-        android:dividerHeight="1dp" />
+        android:headerDividersEnabled="false"/>
     <View
         android:layout_alwaysShow="true"
         android:layout_width="match_parent"
diff --git a/core/res/res/values-sw600dp/bools.xml b/core/res/res/values-sw600dp/bools.xml
index 00f45c1..b339717 100644
--- a/core/res/res/values-sw600dp/bools.xml
+++ b/core/res/res/values-sw600dp/bools.xml
@@ -15,7 +15,6 @@
 -->
 
 <resources>
-    <bool name="target_honeycomb_needs_options_menu">false</bool>
     <bool name="show_ongoing_ime_switcher">true</bool>
     <bool name="kg_share_status_area">false</bool>
     <bool name="kg_sim_puk_account_full_screen">false</bool>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index b49fe49..29f9f6c 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -23,7 +23,6 @@
     <bool name="preferences_prefer_dual_pane">false</bool>
     <bool name="show_ongoing_ime_switcher">true</bool>
     <bool name="action_bar_expanded_action_views_exclusive">true</bool>
-    <bool name="target_honeycomb_needs_options_menu">true</bool>
     <!-- Whether or not to use the drawable/lockscreen_notselected and
          drawable/lockscreen_selected instead of the generic dots when displaying
          the LockPatternView.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index bfbd959..a66aa29 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2652,11 +2652,6 @@
     <!-- Package name for default network scorer app; overridden by product overlays. -->
     <string name="config_defaultNetworkScorerPackageName"></string>
 
-    <!-- Feature flag to enable memory efficient task snapshots that are used in recents optimized
-         for low memory devices and replace the app transition starting window with the splash
-         screen. -->
-    <bool name="config_lowRamTaskSnapshotsAndRecents">false</bool>
-
     <!-- The amount to scale fullscreen snapshots for Overview and snapshot starting windows. -->
     <item name="config_fullTaskSnapshotScale" format="float" type="dimen">1.0</item>
 
@@ -4185,4 +4180,18 @@
 
     <!-- The package name for the default bug report handler app from power menu short press. This app must be whitelisted. -->
     <string name="config_defaultBugReportHandlerApp" translatable="false"></string>
+
+    <!-- The default value used for RawContacts.ACCOUNT_NAME when contacts are inserted without this
+         column set. These contacts are stored locally on the device and will not be removed even
+         if an android.account.Account with this name and type exists. A null string will be used
+         if the value is left empty. When this is non-empty then config_rawContactsLocalAccountType
+         should also be non-empty.  -->
+    <string name="config_rawContactsLocalAccountName" translatable="false"></string>
+
+    <!-- The default value used for RawContacts.ACCOUNT_TYPE when contacts are inserted without this
+         column set. These contacts are stored locally on the device and will not be removed even
+         if an android.account.Account with this name and type exists. A null string will be used
+         if the value is left empty.  When this is non-empty then config_rawContactsLocalAccountName
+         should also be non-empty.-->
+    <string name="config_rawContactsLocalAccountType" translatable="false"></string>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0f5da39..cc420bd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1120,6 +1120,8 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_accessCoarseLocation" product="tv">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when when the app is in the foreground. These location services must be turned on and available on your Android TV device for the app to be able to use them.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_accessCoarseLocation" product="automotive">This app can get your approximate location only when it is in the foreground. These location services must be turned on and available on your car for the app to be able to use them.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_accessCoarseLocation" product="default">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when the app is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ee9287c..9ad6bad 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -247,6 +247,7 @@
   <java-symbol type="id" name="mic" />
   <java-symbol type="id" name="overlay" />
   <java-symbol type="id" name="app_ops" />
+  <java-symbol type="id" name="profile_pager" />
 
   <java-symbol type="attr" name="actionModeShareDrawable" />
   <java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -356,7 +357,6 @@
   <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
   <java-symbol type="dimen" name="config_fullTaskSnapshotScale" />
   <java-symbol type="bool" name="config_use16BitTaskSnapshotPixelFormat" />
-  <java-symbol type="bool" name="config_lowRamTaskSnapshotsAndRecents" />
   <java-symbol type="bool" name="config_hasRecents" />
   <java-symbol type="string" name="config_recentsComponentName" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
@@ -1533,6 +1533,8 @@
   <java-symbol type="layout" name="user_switching_dialog" />
   <java-symbol type="layout" name="common_tab_settings" />
   <java-symbol type="layout" name="notification_material_media_seekbar" />
+  <java-symbol type="layout" name="resolver_list_per_profile" />
+  <java-symbol type="layout" name="chooser_list_per_profile" />
 
   <java-symbol type="anim" name="slide_in_child_bottom" />
   <java-symbol type="anim" name="slide_in_right" />
@@ -1657,7 +1659,6 @@
   <java-symbol type="bool" name="config_perDisplayFocusEnabled" />
   <java-symbol type="bool" name="config_showNavigationBar" />
   <java-symbol type="bool" name="config_supportAutoRotation" />
-  <java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
   <java-symbol type="dimen" name="docked_stack_divider_thickness" />
   <java-symbol type="dimen" name="docked_stack_divider_insets" />
   <java-symbol type="dimen" name="docked_stack_minimize_thickness" />
@@ -3768,4 +3769,8 @@
   <java-symbol type="string" name="accessibility_system_action_toggle_split_screen_label" />
 
   <java-symbol type="string" name="accessibility_freeform_caption" />
+
+  <!-- For contacts provider. -->
+  <java-symbol type="string" name="config_rawContactsLocalAccountName" />
+  <java-symbol type="string" name="config_rawContactsLocalAccountType" />
 </resources>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index b906d84..ed613c3 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -176,14 +176,12 @@
                         mDevice.setPin(mPin);
                         break;
                     case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
-                        mDevice.setPasskey(mPasskey);
                         break;
                     case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
                     case BluetoothDevice.PAIRING_VARIANT_CONSENT:
                         mDevice.setPairingConfirmation(true);
                         break;
                     case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
-                        mDevice.setRemoteOutOfBandData();
                         break;
                 }
             } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index c009f58..caae908 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -24,6 +24,7 @@
     ],
     static_libs: [
         "frameworks-base-testutils",
+        "core-test-rules", // for libcore.dalvik.system.CloseGuardSupport
         "core-tests-support",
         "android-common",
         "frameworks-core-util-lib",
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 8c1c3b5..2f91a09 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -488,7 +488,7 @@
     }
 
     @Test
-    public void setProperties() {
+    public void setProperties() throws DeviceConfig.BadConfigException {
         Map<String, String> keyValues = new HashMap<>();
         keyValues.put(KEY, VALUE);
         keyValues.put(KEY2, VALUE2);
@@ -514,7 +514,7 @@
     }
 
     @Test
-    public void setProperties_multipleNamespaces() {
+    public void setProperties_multipleNamespaces() throws DeviceConfig.BadConfigException {
         Map<String, String> keyValues = new HashMap<>();
         keyValues.put(KEY, VALUE);
         keyValues.put(KEY2, VALUE2);
diff --git a/core/tests/coretests/src/android/util/CloseGuardTest.java b/core/tests/coretests/src/android/util/CloseGuardTest.java
new file mode 100644
index 0000000..d86c7b7
--- /dev/null
+++ b/core/tests/coretests/src/android/util/CloseGuardTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import libcore.dalvik.system.CloseGuardSupport;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+
+/** Unit tests for {@link android.util.CloseGuard} */
+public class CloseGuardTest {
+
+    @Rule
+    public final TestRule rule = CloseGuardSupport.getRule();
+
+    @Test
+    public void testEnabled_NotOpen() throws Throwable {
+        ResourceOwner owner = new ResourceOwner();
+        assertUnreleasedResources(owner, 0);
+    }
+
+    @Test
+    public void testEnabled_OpenNotClosed() throws Throwable {
+        ResourceOwner owner = new ResourceOwner();
+        owner.open();
+        assertUnreleasedResources(owner, 1);
+    }
+
+    @Test
+    public void testEnabled_OpenThenClosed() throws Throwable {
+        ResourceOwner owner = new ResourceOwner();
+        owner.open();
+        owner.close();
+        assertUnreleasedResources(owner, 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testOpen_withNullMethodName_throwsNPE() throws Throwable {
+        CloseGuard closeGuard = new CloseGuard();
+        closeGuard.open(null);
+    }
+
+    private void assertUnreleasedResources(ResourceOwner owner, int expectedCount)
+            throws Throwable {
+        try {
+            CloseGuardSupport.getFinalizerChecker().accept(owner, expectedCount);
+        } finally {
+            // Close the resource so that CloseGuard does not generate a warning for real when it
+            // is actually finalized.
+            owner.close();
+        }
+    }
+
+    /**
+     * A test user of {@link CloseGuard}.
+     */
+    private static class ResourceOwner {
+
+        private final CloseGuard mCloseGuard;
+
+        ResourceOwner() {
+            mCloseGuard = new CloseGuard();
+        }
+
+        public void open() {
+            mCloseGuard.open("close");
+        }
+
+        public void close() {
+            mCloseGuard.close();
+        }
+
+        /**
+         * Make finalize public so that it can be tested directly without relying on garbage
+         * collection to trigger it.
+         */
+        @Override
+        public void finalize() throws Throwable {
+            mCloseGuard.warnIfOpen();
+            super.finalize();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/util/StatsEventTest.java b/core/tests/coretests/src/android/util/StatsEventTest.java
index 097bada..ac25e27 100644
--- a/core/tests/coretests/src/android/util/StatsEventTest.java
+++ b/core/tests/coretests/src/android/util/StatsEventTest.java
@@ -44,7 +44,7 @@
     @Test
     public void testNoFields() {
         final long minTimestamp = SystemClock.elapsedRealtimeNanos();
-        final StatsEvent statsEvent = StatsEvent.newBuilder().build();
+        final StatsEvent statsEvent = StatsEvent.newBuilder().usePooledBuffer().build();
         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
 
         final int expectedAtomId = 0;
@@ -99,6 +99,7 @@
                 .writeBoolean(field2)
                 .writeInt(field3)
                 .writeInt(field4)
+                .usePooledBuffer()
                 .build();
         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
 
@@ -167,6 +168,7 @@
                 .writeString(field1)
                 .writeFloat(field2)
                 .writeByteArray(field3)
+                .usePooledBuffer()
                 .build();
         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
 
@@ -230,6 +232,7 @@
                 .setAtomId(expectedAtomId)
                 .writeAttributionChain(uids, tags)
                 .writeLong(field2)
+                .usePooledBuffer()
                 .build();
         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
 
@@ -299,6 +302,7 @@
         final StatsEvent statsEvent = StatsEvent.newBuilder()
                 .setAtomId(expectedAtomId)
                 .writeKeyValuePairs(intMap, longMap, stringMap, floatMap)
+                .usePooledBuffer()
                 .build();
         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
 
@@ -392,6 +396,7 @@
                 .addBooleanAnnotation(field1AnnotationId, field1AnnotationValue)
                 .writeBoolean(field2)
                 .addIntAnnotation(field2AnnotationId, field2AnnotationValue)
+                .usePooledBuffer()
                 .build();
         final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
deleted file mode 100644
index d54ce51..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.provider.DeviceConfig;
-import android.support.test.uiautomator.UiDevice;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.util.function.Supplier;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ConfigParserTest {
-    private static final Supplier<String> SETTINGS =
-            () -> "int=42,float=12.3,boolean=true,string=abc";
-    private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
-            "device_config delete " + DeviceConfig.NAMESPACE_TEXTCLASSIFIER;
-    private static final String[] DEVICE_CONFIG_KEYS = new String[]{
-            "boolean",
-            "string",
-            "int",
-            "float"
-    };
-
-    private ConfigParser mConfigParser;
-
-    @Before
-    public void setup() throws IOException {
-        mConfigParser = new ConfigParser(SETTINGS);
-        clearDeviceConfig();
-    }
-
-    @After
-    public void tearDown() throws IOException {
-        clearDeviceConfig();
-    }
-
-    @Test
-    public void getBoolean_deviceConfig() {
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                "boolean",
-                "false",
-                false);
-        boolean value = mConfigParser.getBoolean("boolean", true);
-        assertThat(value).isFalse();
-    }
-
-    @Test
-    public void getBoolean_settings() {
-        boolean value = mConfigParser.getBoolean(
-                "boolean",
-                false);
-        assertThat(value).isTrue();
-    }
-
-    @Test
-    public void getInt_deviceConfig() {
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                "int",
-                "1",
-                false);
-        int value = mConfigParser.getInt("int", 0);
-        assertThat(value).isEqualTo(1);
-    }
-
-    @Test
-    public void getInt_settings() {
-        int value = mConfigParser.getInt("int", 0);
-        assertThat(value).isEqualTo(42);
-    }
-
-    @Test
-    public void getFloat_deviceConfig() {
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                "float",
-                "3.14",
-                false);
-        float value = mConfigParser.getFloat("float", 0);
-        assertThat(value).isWithin(0.0001f).of(3.14f);
-    }
-
-    @Test
-    public void getFloat_settings() {
-        float value = mConfigParser.getFloat("float", 0);
-        assertThat(value).isWithin(0.0001f).of(12.3f);
-    }
-
-    @Test
-    public void getString_deviceConfig() {
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                "string",
-                "hello",
-                false);
-        String value = mConfigParser.getString("string", "");
-        assertThat(value).isEqualTo("hello");
-    }
-
-    @Test
-    public void getString_settings() {
-        String value = mConfigParser.getString("string", "");
-        assertThat(value).isEqualTo("abc");
-    }
-
-    private static void clearDeviceConfig() throws IOException {
-        UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        for (String key : DEVICE_CONFIG_KEYS) {
-            uiDevice.executeShellCommand(CLEAR_DEVICE_CONFIG_KEY_CMD + " " + key);
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index 64fb141..82fa73f 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -16,230 +16,141 @@
 
 package android.view.textclassifier;
 
+import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.provider.DeviceConfig;
+
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.Assert;
+import com.google.common.primitives.Floats;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class TextClassificationConstantsTest {
-
     private static final float EPSILON = 0.0001f;
 
     @Test
-    public void testLoadFromString() {
-        final String s = "local_textclassifier_enabled=true,"
-                + "system_textclassifier_enabled=true,"
-                + "model_dark_launch_enabled=true,"
-                + "smart_selection_enabled=true,"
-                + "smart_text_share_enabled=true,"
-                + "smart_linkify_enabled=true,"
-                + "smart_select_animation_enabled=true,"
-                + "suggest_selection_max_range_length=10,"
-                + "classify_text_max_range_length=11,"
-                + "generate_links_max_text_length=12,"
-                + "generate_links_log_sample_rate=13,"
-                + "entity_list_default=phone,"
-                + "entity_list_not_editable=address:flight,"
-                + "entity_list_editable=date:datetime,"
-                + "in_app_conversation_action_types_default=text_reply,"
-                + "notification_conversation_action_types_default=send_email:call_phone,"
-                + "lang_id_threshold_override=0.3,"
-                + "lang_id_context_settings=10:1:0.5,"
-                + "detect_language_from_text_enabled=true,"
-                + "template_intent_factory_enabled=true,"
-                + "translate_in_classification_enabled=true";
-        final TextClassificationConstants constants = new TextClassificationConstants(() -> s);
+    public void testLoadFromDeviceConfig_booleanValue() throws Exception {
+        // Saves config original value.
+        final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED);
 
-        assertWithMessage("local_textclassifier_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isTrue();
-        assertWithMessage("system_textclassifier_enabled")
-                .that(constants.isSystemTextClassifierEnabled()).isTrue();
-        assertWithMessage("model_dark_launch_enabled")
-                .that(constants.isModelDarkLaunchEnabled()).isTrue();
-        assertWithMessage("smart_selection_enabled")
-                .that(constants.isSmartSelectionEnabled()).isTrue();
-        assertWithMessage("smart_text_share_enabled")
-                .that(constants.isSmartTextShareEnabled()).isTrue();
-        assertWithMessage("smart_linkify_enabled")
-                .that(constants.isSmartLinkifyEnabled()).isTrue();
-        assertWithMessage("smart_select_animation_enabled")
-                .that(constants.isSmartSelectionAnimationEnabled()).isTrue();
-        assertWithMessage("suggest_selection_max_range_length")
-                .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(10);
-        assertWithMessage("classify_text_max_range_length")
-                .that(constants.getClassifyTextMaxRangeLength()).isEqualTo(11);
-        assertWithMessage("generate_links_max_text_length")
-                .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(12);
-        assertWithMessage("generate_links_log_sample_rate")
-                .that(constants.getGenerateLinksLogSampleRate()).isEqualTo(13);
-        assertWithMessage("entity_list_default")
-                .that(constants.getEntityListDefault())
-                .containsExactly("phone");
-        assertWithMessage("entity_list_not_editable")
-                .that(constants.getEntityListNotEditable())
-                .containsExactly("address", "flight");
-        assertWithMessage("entity_list_editable")
-                .that(constants.getEntityListEditable())
-                .containsExactly("date", "datetime");
-        assertWithMessage("in_app_conversation_action_types_default")
-                .that(constants.getInAppConversationActionTypes())
-                .containsExactly("text_reply");
-        assertWithMessage("notification_conversation_action_types_default")
-                .that(constants.getNotificationConversationActionTypes())
-                .containsExactly("send_email", "call_phone");
-        assertWithMessage("lang_id_threshold_override")
-                .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(0.3f);
-        Assert.assertArrayEquals("lang_id_context_settings",
-                constants.getLangIdContextSettings(), new float[]{10, 1, 0.5f}, EPSILON);
-        assertWithMessage("detect_language_from_text_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isTrue();
-        assertWithMessage("template_intent_factory_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isTrue();
-        assertWithMessage("translate_in_classification_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isTrue();
+        final TextClassificationConstants constants = new TextClassificationConstants();
+        try {
+            // Sets and checks different value.
+            setDeviceConfig(TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED, "false");
+            assertWithMessage(TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED)
+                    .that(constants.isLocalTextClassifierEnabled()).isFalse();
+        } finally {
+            // Restores config original value.
+            setDeviceConfig(TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED,
+                    originalValue);
+        }
     }
 
     @Test
-    public void testLoadFromString_differentValues() {
-        final String testTextClassifier = "com.example.textclassifier";
-        final String s = "local_textclassifier_enabled=false,"
-                + "system_textclassifier_enabled=false,"
-                + "model_dark_launch_enabled=false,"
-                + "smart_selection_enabled=false,"
-                + "smart_text_share_enabled=false,"
-                + "smart_linkify_enabled=false,"
-                + "smart_select_animation_enabled=false,"
-                + "suggest_selection_max_range_length=8,"
-                + "classify_text_max_range_length=7,"
-                + "generate_links_max_text_length=6,"
-                + "generate_links_log_sample_rate=5,"
-                + "entity_list_default=email:url,"
-                + "entity_list_not_editable=date,"
-                + "entity_list_editable=flight,"
-                + "in_app_conversation_action_types_default=view_map:track_flight,"
-                + "notification_conversation_action_types_default=share_location,"
-                + "lang_id_threshold_override=2,"
-                + "lang_id_context_settings=30:0.5:0.3,"
-                + "detect_language_from_text_enabled=false,"
-                + "template_intent_factory_enabled=false,"
-                + "textclassifier_service_package_override=" + testTextClassifier + ","
-                + "translate_in_classification_enabled=false";
-        final TextClassificationConstants constants = new TextClassificationConstants(() -> s);
+    public void testLoadFromDeviceConfig_IntValue() throws Exception {
+        // Saves config original value.
+        final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH);
 
-        assertWithMessage("local_textclassifier_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isFalse();
-        assertWithMessage("system_textclassifier_enabled")
-                .that(constants.isSystemTextClassifierEnabled()).isFalse();
-        assertWithMessage("model_dark_launch_enabled")
-                .that(constants.isModelDarkLaunchEnabled()).isFalse();
-        assertWithMessage("smart_selection_enabled")
-                .that(constants.isSmartSelectionEnabled()).isFalse();
-        assertWithMessage("smart_text_share_enabled")
-                .that(constants.isSmartTextShareEnabled()).isFalse();
-        assertWithMessage("smart_linkify_enabled")
-                .that(constants.isSmartLinkifyEnabled()).isFalse();
-        assertWithMessage("smart_select_animation_enabled")
-                .that(constants.isSmartSelectionAnimationEnabled()).isFalse();
-        assertWithMessage("suggest_selection_max_range_length")
-                .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(8);
-        assertWithMessage("classify_text_max_range_length")
-                .that(constants.getClassifyTextMaxRangeLength()).isEqualTo(7);
-        assertWithMessage("generate_links_max_text_length")
-                .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(6);
-        assertWithMessage("generate_links_log_sample_rate")
-                .that(constants.getGenerateLinksLogSampleRate()).isEqualTo(5);
-        assertWithMessage("entity_list_default")
-                .that(constants.getEntityListDefault())
-                .containsExactly("email", "url");
-        assertWithMessage("entity_list_not_editable")
-                .that(constants.getEntityListNotEditable())
-                .containsExactly("date");
-        assertWithMessage("entity_list_editable")
-                .that(constants.getEntityListEditable())
-                .containsExactly("flight");
-        assertWithMessage("in_app_conversation_action_types_default")
-                .that(constants.getInAppConversationActionTypes())
-                .containsExactly("view_map", "track_flight");
-        assertWithMessage("notification_conversation_action_types_default")
-                .that(constants.getNotificationConversationActionTypes())
-                .containsExactly("share_location");
-        assertWithMessage("lang_id_threshold_override")
-                .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f);
-        Assert.assertArrayEquals("lang_id_context_settings",
-                constants.getLangIdContextSettings(), new float[]{30, 0.5f, 0.3f}, EPSILON);
-        assertWithMessage("detect_language_from_text_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isFalse();
-        assertWithMessage("template_intent_factory_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isFalse();
-        assertWithMessage("translate_in_classification_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isFalse();
-        assertWithMessage("textclassifier_service_package_override")
-                .that(constants.getTextClassifierServiceName()).isEqualTo(
-                testTextClassifier);
+        final TextClassificationConstants constants = new TextClassificationConstants();
+        try {
+            // Sets and checks different value.
+            setDeviceConfig(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH, "8");
+            assertWithMessage(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH)
+                    .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(8);
+        } finally {
+            // Restores config original value.
+            setDeviceConfig(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH,
+                    originalValue);
+        }
     }
 
     @Test
-    public void testLoadFromString_defaultValues() {
-        final TextClassificationConstants constants = new TextClassificationConstants(() -> "");
+    public void testLoadFromDeviceConfig_StringValue() throws Exception {
+        // Saves config original value.
+        final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE);
 
-        assertWithMessage("local_textclassifier_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isTrue();
-        assertWithMessage("system_textclassifier_enabled")
-                .that(constants.isSystemTextClassifierEnabled()).isTrue();
-        assertWithMessage("model_dark_launch_enabled")
-                .that(constants.isModelDarkLaunchEnabled()).isFalse();
-        assertWithMessage("smart_selection_enabled")
-                .that(constants.isSmartSelectionEnabled()).isTrue();
-        assertWithMessage("smart_text_share_enabled")
-                .that(constants.isSmartTextShareEnabled()).isTrue();
-        assertWithMessage("smart_linkify_enabled")
-                .that(constants.isSmartLinkifyEnabled()).isTrue();
-        assertWithMessage("smart_select_animation_enabled")
-                .that(constants.isSmartSelectionAnimationEnabled()).isTrue();
-        assertWithMessage("suggest_selection_max_range_length")
-                .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(10 * 1000);
-        assertWithMessage("classify_text_max_range_length")
-                .that(constants.getClassifyTextMaxRangeLength()).isEqualTo(10 * 1000);
-        assertWithMessage("generate_links_max_text_length")
-                .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(100 * 1000);
-        assertWithMessage("generate_links_log_sample_rate")
-                .that(constants.getGenerateLinksLogSampleRate()).isEqualTo(100);
-        assertWithMessage("entity_list_default")
-                .that(constants.getEntityListDefault())
-                .containsExactly("address", "email", "url", "phone", "date", "datetime", "flight");
-        assertWithMessage("entity_list_not_editable")
-                .that(constants.getEntityListNotEditable())
-                .containsExactly("address", "email", "url", "phone", "date", "datetime", "flight");
-        assertWithMessage("entity_list_editable")
-                .that(constants.getEntityListEditable())
-                .containsExactly("address", "email", "url", "phone", "date", "datetime", "flight");
-        assertWithMessage("in_app_conversation_action_types_default")
-                .that(constants.getInAppConversationActionTypes())
-                .containsExactly("text_reply", "create_reminder", "call_phone", "open_url",
-                        "send_email", "send_sms", "track_flight", "view_calendar", "view_map",
-                        "add_contact", "copy");
-        assertWithMessage("notification_conversation_action_types_default")
-                .that(constants.getNotificationConversationActionTypes())
-                .containsExactly("text_reply", "create_reminder", "call_phone", "open_url",
-                        "send_email", "send_sms", "track_flight", "view_calendar", "view_map",
-                        "add_contact", "copy");
-        assertWithMessage("lang_id_threshold_override")
-                .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(-1f);
-        Assert.assertArrayEquals("lang_id_context_settings",
-                constants.getLangIdContextSettings(), new float[]{20, 1, 0.4f}, EPSILON);
-        assertWithMessage("detect_language_from_text_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isTrue();
-        assertWithMessage("template_intent_factory_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isTrue();
-        assertWithMessage("translate_in_classification_enabled")
-                .that(constants.isLocalTextClassifierEnabled()).isTrue();
-        assertWithMessage("textclassifier_service_package_override")
-                .that(constants.getTextClassifierServiceName()).isNull();
+        final TextClassificationConstants constants = new TextClassificationConstants();
+        try {
+            // Sets and checks different value.
+            final String testTextClassifier = "com.example.textclassifier";
+            setDeviceConfig(TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
+                    testTextClassifier);
+            assertWithMessage(TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE)
+                    .that(constants.getTextClassifierServicePackageOverride()).isEqualTo(
+                    testTextClassifier);
+        } finally {
+            // Restores config original value.
+            setDeviceConfig(TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
+                    originalValue);
+        }
+    }
+
+    @Test
+    public void testLoadFromDeviceConfig_FloatValue() throws Exception {
+        // Saves config original value.
+        final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE);
+
+        final TextClassificationConstants constants = new TextClassificationConstants();
+        try {
+            // Sets and checks different value.
+            setDeviceConfig(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE, "2");
+            assertWithMessage(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE)
+                    .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f);
+        } finally {
+            // Restores config original value.
+            setDeviceConfig(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE, originalValue);
+        }
+    }
+
+    @Test
+    public void testLoadFromDeviceConfig_StringList() throws Exception {
+        // Saves config original value.
+        final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                TextClassificationConstants.ENTITY_LIST_DEFAULT);
+
+        final TextClassificationConstants constants = new TextClassificationConstants();
+        try {
+            // Sets and checks different value.
+            setDeviceConfig(TextClassificationConstants.ENTITY_LIST_DEFAULT, "email:url");
+            assertWithMessage(TextClassificationConstants.ENTITY_LIST_DEFAULT)
+                    .that(constants.getEntityListDefault())
+                    .containsExactly("email", "url");
+        } finally {
+            // Restores config original value.
+            setDeviceConfig(TextClassificationConstants.ENTITY_LIST_DEFAULT, originalValue);
+        }
+    }
+
+    @Test
+    public void testLoadFromDeviceConfig_FloatList() throws Exception {
+        // Saves config original value.
+        final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS);
+
+        final TextClassificationConstants constants = new TextClassificationConstants();
+        try {
+            // Sets and checks different value.
+            setDeviceConfig(TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS, "30:0.5:0.3");
+            assertThat(Floats.asList(constants.getLangIdContextSettings())).containsExactly(30f,
+                    0.5f, 0.3f).inOrder();
+        } finally {
+            // Restores config original value.
+            setDeviceConfig(TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS, originalValue);
+        }
+    }
+
+    private void setDeviceConfig(String key, String value) {
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key,
+                value, /* makeDefault */ false);
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index b3f2bbe..8faf790 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -78,7 +78,7 @@
 
         TextClassifier fallback = TextClassifier.NO_OP;
         TextClassifier classifier = new TextClassifierImpl(
-                fakeContext, new TextClassificationConstants(() -> null), fallback);
+                fakeContext, new TextClassificationConstants(), fallback);
 
         String text = "Contact me at +12122537077";
         String classifiedText = "+12122537077";
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index deb0f18..2304ba6 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -69,7 +69,7 @@
     public String mTextClassifierType;
 
     private static final TextClassificationConstants TC_CONSTANTS =
-            new TextClassificationConstants(() -> "");
+            new TextClassificationConstants();
     private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
     private static final String NO_TYPE = null;
 
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 7b405434..82854e5 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -20,6 +20,7 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -49,8 +50,10 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
 import android.media.Ringtone;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Vibrator;
@@ -157,9 +160,12 @@
         when(mFrameworkObjectProvider.getRingtone(eq(mContext), any())).thenReturn(mRingtone);
 
         when(mResources.getString(anyInt())).thenReturn("Howdy %s");
+        when(mResources.getString(R.string.config_defaultAccessibilityService)).thenReturn(null);
         when(mResources.getIntArray(anyInt())).thenReturn(VIBRATOR_PATTERN_INT);
 
         ResolveInfo resolveInfo = mock(ResolveInfo.class);
+        resolveInfo.serviceInfo = mock(ServiceInfo.class);
+        resolveInfo.serviceInfo.applicationInfo = mApplicationInfo;
         when(resolveInfo.loadLabel(anyObject())).thenReturn("Service name");
         when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
         when(mServiceInfo.getComponentName())
@@ -200,42 +206,47 @@
     }
 
     @Test
-    public void testShortcutAvailable_enabledButNoServiceWhenCreated_shouldReturnFalse() {
+    public void testShortcutAvailable_enabledButNoServiceWhenCreated_shouldReturnFalse()
+            throws Exception {
         configureNoShortcutService();
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         assertFalse(getController().isAccessibilityShortcutAvailable(false));
     }
 
     @Test
-    public void testShortcutAvailable_enabledWithValidServiceWhenCreated_shouldReturnTrue() {
+    public void testShortcutAvailable_enabledWithValidServiceWhenCreated_shouldReturnTrue()
+            throws Exception {
         configureValidShortcutService();
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         assertTrue(getController().isAccessibilityShortcutAvailable(false));
     }
 
     @Test
-    public void testShortcutAvailable_disabledWithValidServiceWhenCreated_shouldReturnFalse() {
+    public void testShortcutAvailable_disabledWithValidServiceWhenCreated_shouldReturnFalse()
+            throws Exception {
         configureValidShortcutService();
         configureShortcutEnabled(DISABLED_BUT_LOCK_SCREEN_ON);
         assertFalse(getController().isAccessibilityShortcutAvailable(false));
     }
 
     @Test
-    public void testShortcutAvailable_onLockScreenButDisabledThere_shouldReturnFalse() {
+    public void testShortcutAvailable_onLockScreenButDisabledThere_shouldReturnFalse()
+            throws Exception {
         configureValidShortcutService();
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         assertFalse(getController().isAccessibilityShortcutAvailable(true));
     }
 
     @Test
-    public void testShortcutAvailable_onLockScreenAndEnabledThere_shouldReturnTrue() {
+    public void testShortcutAvailable_onLockScreenAndEnabledThere_shouldReturnTrue()
+            throws Exception {
         configureValidShortcutService();
         configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN);
         assertTrue(getController().isAccessibilityShortcutAvailable(true));
     }
 
     @Test
-    public void testShortcutAvailable_onLockScreenAndLockScreenPreferenceUnset() {
+    public void testShortcutAvailable_onLockScreenAndLockScreenPreferenceUnset() throws Exception {
         // When the user hasn't specified a lock screen preference, we allow from the lock screen
         // as long as the user has agreed to enable the shortcut
         configureValidShortcutService();
@@ -249,17 +260,19 @@
     }
 
     @Test
-    public void testShortcutAvailable_whenServiceIdBecomesNull_shouldReturnFalse() {
+    public void testShortcutAvailable_whenServiceIdBecomesNull_shouldReturnFalse()
+            throws Exception {
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         configureValidShortcutService();
         AccessibilityShortcutController accessibilityShortcutController = getController();
-        Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
+        configureNoShortcutService();
         accessibilityShortcutController.onSettingsChanged();
         assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable(false));
     }
 
     @Test
-    public void testShortcutAvailable_whenServiceIdBecomesNonNull_shouldReturnTrue() {
+    public void testShortcutAvailable_whenServiceIdBecomesNonNull_shouldReturnTrue()
+            throws Exception {
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         configureNoShortcutService();
         AccessibilityShortcutController accessibilityShortcutController = getController();
@@ -269,7 +282,8 @@
     }
 
     @Test
-    public void testShortcutAvailable_whenShortcutBecomesDisabled_shouldReturnFalse() {
+    public void testShortcutAvailable_whenShortcutBecomesDisabled_shouldReturnFalse()
+            throws Exception {
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         configureValidShortcutService();
         AccessibilityShortcutController accessibilityShortcutController = getController();
@@ -279,7 +293,8 @@
     }
 
     @Test
-    public void testShortcutAvailable_whenShortcutBecomesEnabled_shouldReturnTrue() {
+    public void testShortcutAvailable_whenShortcutBecomesEnabled_shouldReturnTrue()
+            throws Exception {
         configureShortcutEnabled(DISABLED);
         configureValidShortcutService();
         AccessibilityShortcutController accessibilityShortcutController = getController();
@@ -289,7 +304,8 @@
     }
 
     @Test
-    public void testShortcutAvailable_whenLockscreenBecomesDisabled_shouldReturnFalse() {
+    public void testShortcutAvailable_whenLockscreenBecomesDisabled_shouldReturnFalse()
+            throws Exception {
         configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN);
         configureValidShortcutService();
         AccessibilityShortcutController accessibilityShortcutController = getController();
@@ -299,7 +315,8 @@
     }
 
     @Test
-    public void testShortcutAvailable_whenLockscreenBecomesEnabled_shouldReturnTrue() {
+    public void testShortcutAvailable_whenLockscreenBecomesEnabled_shouldReturnTrue()
+            throws Exception {
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         configureValidShortcutService();
         AccessibilityShortcutController accessibilityShortcutController = getController();
@@ -370,7 +387,7 @@
     }
 
     @Test
-    public void testClickingDisableButtonInDialog_shouldClearShortcutId() {
+    public void testClickingDisableButtonInDialog_shouldClearShortcutId() throws Exception {
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         configureValidShortcutService();
         Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
@@ -458,7 +475,22 @@
     }
 
     @Test
-    public void testOnAccessibilityShortcut_showsWarningDialog_shouldTtsSpokenPrompt() {
+    public void testOnAccessibilityShortcut_sdkGreaterThanQ_reqA11yButton_callsServiceWithNoToast()
+            throws Exception {
+        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
+        configureValidShortcutService();
+        configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
+        configureRequestAccessibilityButton();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
+        getController().performAccessibilityShortcut();
+
+        verifyZeroInteractions(mToast);
+        verify(mAccessibilityManagerService).performAccessibilityShortcut();
+    }
+
+    @Test
+    public void testOnAccessibilityShortcut_showsWarningDialog_shouldTtsSpokenPrompt()
+            throws Exception {
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         configureValidShortcutService();
         configureTtsSpokenPromptEnabled();
@@ -482,7 +514,8 @@
     }
 
     @Test
-    public void testOnAccessibilityShortcut_showsWarningDialog_ttsInitFail_noSpokenPrompt() {
+    public void testOnAccessibilityShortcut_showsWarningDialog_ttsInitFail_noSpokenPrompt()
+            throws Exception {
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         configureValidShortcutService();
         configureTtsSpokenPromptEnabled();
@@ -500,19 +533,28 @@
         verify(mRingtone).play();
     }
 
-    private void configureNoShortcutService() {
+    private void configureNoShortcutService() throws Exception {
+        when(mAccessibilityManagerService
+                .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY))
+                .thenReturn(Collections.emptyList());
         Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
     }
 
-    private void configureValidShortcutService() {
+    private void configureValidShortcutService() throws Exception {
+        when(mAccessibilityManagerService
+                .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY))
+                .thenReturn(Collections.singletonList(SERVICE_NAME_STRING));
         Settings.Secure.putString(
                 mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, SERVICE_NAME_STRING);
     }
 
-    private void configureFirstFrameworkFeature() {
+    private void configureFirstFrameworkFeature() throws Exception {
         ComponentName featureComponentName =
                 (ComponentName) AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
                         .keySet().toArray()[0];
+        when(mAccessibilityManagerService
+                .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY))
+                .thenReturn(Collections.singletonList(featureComponentName.flattenToString()));
         Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
                 featureComponentName.flattenToString());
     }
@@ -552,6 +594,15 @@
                 .FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK;
     }
 
+    private void configureRequestAccessibilityButton() {
+        mServiceInfo.flags |= AccessibilityServiceInfo
+                .FLAG_REQUEST_ACCESSIBILITY_BUTTON;
+    }
+
+    private void configureApplicationTargetSdkVersion(int versionCode) {
+        mApplicationInfo.targetSdkVersion = versionCode;
+    }
+
     private void configureHandlerCallbackInvocation() {
         doAnswer((InvocationOnMock invocation) -> {
             Message m = (Message) invocation.getArguments()[0];
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index d427cbd..8622b7e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -313,7 +313,7 @@
         assertThat(activity.isFinishing(), is(false));
 
         onView(withId(R.id.empty)).check(matches(isDisplayed()));
-        onView(withId(R.id.resolver_list)).check(matches(not(isDisplayed())));
+        onView(withId(R.id.profile_pager)).check(matches(not(isDisplayed())));
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
                 () -> activity.getAdapter().handlePackagesChanged()
         );
@@ -674,12 +674,12 @@
 
         mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
         waitForIdle();
+        // Second invocation is from onCreate
         verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture());
-        // First invocation is from onCreate
-        assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
-                is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
-        assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
+        assertThat(logMakerCaptor.getAllValues().get(0).getSubtype(),
                 is(CONTENT_PREVIEW_TEXT));
+        assertThat(logMakerCaptor.getAllValues().get(0).getCategory(),
+                is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
     }
 
     @Test
@@ -706,10 +706,10 @@
         waitForIdle();
         verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture());
         // First invocation is from onCreate
-        assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
-                is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
-        assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
+        assertThat(logMakerCaptor.getAllValues().get(0).getSubtype(),
                 is(CONTENT_PREVIEW_IMAGE));
+        assertThat(logMakerCaptor.getAllValues().get(0).getCategory(),
+                is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 03705d0..a2e0095 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -29,6 +29,7 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.os.UserHandle;
 import android.util.Size;
 
 import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
@@ -47,7 +48,7 @@
     private UsageStatsManager mUsm;
 
     ChooserListAdapter getAdapter() {
-        return (ChooserListAdapter) mAdapter;
+        return mChooserMultiProfilePagerAdapter.getCurrentListAdapter();
     }
 
     boolean getIsSelected() { return mIsSuccessfullySelected; }
@@ -77,7 +78,7 @@
     }
 
     @Override
-    protected ResolverListController createListController() {
+    protected ResolverListController createListController(UserHandle userHandle) {
         return sOverrides.resolverListController;
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 344c286..923ce3e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -116,14 +116,14 @@
         waitForIdle();
 
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
-        final View resolverList = activity.findViewById(R.id.resolver_list);
-        final int initialResolverHeight = resolverList.getHeight();
+        final View viewPager = activity.findViewById(R.id.profile_pager);
+        final int initialResolverHeight = viewPager.getHeight();
 
         activity.runOnUiThread(() -> {
             ResolverDrawerLayout layout = (ResolverDrawerLayout)
                     activity.findViewById(
                             R.id.contentPanel);
-            ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+            ((ResolverDrawerLayout.LayoutParams) viewPager.getLayoutParams()).maxHeight
                 = initialResolverHeight - 1;
             // Force a relayout
             layout.invalidate();
@@ -131,13 +131,13 @@
         });
         waitForIdle();
         assertThat("Drawer should be capped at maxHeight",
-            resolverList.getHeight() == (initialResolverHeight - 1));
+                viewPager.getHeight() == (initialResolverHeight - 1));
 
         activity.runOnUiThread(() -> {
             ResolverDrawerLayout layout = (ResolverDrawerLayout)
                     activity.findViewById(
                             R.id.contentPanel);
-            ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+            ((ResolverDrawerLayout.LayoutParams) viewPager.getLayoutParams()).maxHeight
                 = initialResolverHeight + 1;
             // Force a relayout
             layout.invalidate();
@@ -145,7 +145,7 @@
         });
         waitForIdle();
         assertThat("Drawer should not change height if its height is less than maxHeight",
-            resolverList.getHeight() == initialResolverHeight);
+                viewPager.getHeight() == initialResolverHeight);
     }
 
     @Ignore // Failing - b/144929805
@@ -160,11 +160,13 @@
         waitForIdle();
 
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
-        final View resolverList = activity.findViewById(R.id.resolver_list);
+        final View viewPager = activity.findViewById(R.id.profile_pager);
+        final View divider = activity.findViewById(R.id.divider);
         final RelativeLayout profileView =
             (RelativeLayout) activity.findViewById(R.id.profile_button).getParent();
         assertThat("Drawer should show at bottom by default",
-                profileView.getBottom() == resolverList.getTop() && profileView.getTop() > 0);
+                profileView.getBottom() + divider.getHeight() == viewPager.getTop()
+                        && profileView.getTop() > 0);
 
         activity.runOnUiThread(() -> {
             ResolverDrawerLayout layout = (ResolverDrawerLayout)
@@ -174,7 +176,8 @@
         });
         waitForIdle();
         assertThat("Drawer should show at top with new attribute",
-            profileView.getBottom() == resolverList.getTop() && profileView.getTop() == 0);
+            profileView.getBottom() + divider.getHeight() == viewPager.getTop()
+                    && profileView.getTop() == 0);
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 5ac1489..64906bb 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -115,7 +115,7 @@
         mUsm = new UsageStatsManager(mMockContext, mMockService);
         when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm);
         mController = new ResolverListController(mMockContext, mMockPackageManager, sendIntent,
-                refererPackage, UserHandle.USER_CURRENT);
+                refererPackage, UserHandle.USER_CURRENT, /* userHandle */ UserHandle.SYSTEM);
         mController.sort(new ArrayList<ResolvedComponentInfo>());
         long beforeReport = getCount(mUsm, packageName, action, annotation);
         mController.updateChooserCounts(packageName, UserHandle.USER_CURRENT, action);
@@ -132,7 +132,7 @@
         mUsm = new UsageStatsManager(mMockContext, mMockService);
         when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm);
         mController = new ResolverListController(mMockContext, mMockPackageManager, sendIntent,
-                refererPackage, UserHandle.USER_CURRENT);
+                refererPackage, UserHandle.USER_CURRENT, /* userHandle */ UserHandle.SYSTEM);
         List<ResolvedComponentInfo> topKList = new ArrayList<>(resolvedComponents);
         mController.topK(topKList, 5);
         List<ResolvedComponentInfo> sortList = new ArrayList<>(topKList);
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 39cc83c..93357af 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
 
 import com.android.internal.app.chooser.TargetInfo;
 
@@ -37,15 +38,15 @@
     private UsageStatsManager mUsm;
 
     @Override
-    public ResolverListAdapter createAdapter(Context context, List<Intent> payloadIntents,
-            Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed,
-            boolean useLayoutForBrowsables) {
+    public ResolverListAdapter createResolverListAdapter(Context context,
+            List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
+            boolean filterLastUsed, boolean useLayoutForBrowsables, UserHandle userHandle) {
         return new ResolverWrapperAdapter(context, payloadIntents, initialIntents, rList,
-                filterLastUsed, createListController(), useLayoutForBrowsables, this);
+                filterLastUsed, createListController(userHandle), useLayoutForBrowsables, this);
     }
 
     ResolverWrapperAdapter getAdapter() {
-        return (ResolverWrapperAdapter) mAdapter;
+        return (ResolverWrapperAdapter) mMultiProfilePagerAdapter.getCurrentListAdapter();
     }
 
     @Override
@@ -66,7 +67,7 @@
     }
 
     @Override
-    protected ResolverListController createListController() {
+    protected ResolverListController createListController(UserHandle userHandle) {
         return sOverrides.resolverListController;
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
new file mode 100644
index 0000000..9002c2c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.internal.infra;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.os.Parcel;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Unit test for {@link AndroidFuture}.
+ *
+ * <p>To run it:
+ * {@code atest FrameworksCoreTests:com.android.internal.infra.AndroidFutureTest}
+ */
+
+@RunWith(AndroidJUnit4.class)
+public class AndroidFutureTest {
+    @Test
+    public void testGet() throws Exception {
+        AndroidFuture<Integer> future = new AndroidFuture<>();
+        future.complete(5);
+        assertThat(future.get()).isEqualTo(5);
+    }
+
+    @Test
+    public void testWhenComplete_AlreadyComplete() throws Exception {
+        AndroidFuture<Integer> future = new AndroidFuture<>();
+        future.complete(5);
+        CountDownLatch latch = new CountDownLatch(1);
+        future.whenComplete((obj, err) -> {
+            assertThat(obj).isEqualTo(5);
+            assertThat(err).isNull();
+            latch.countDown();
+        });
+        latch.await();
+    }
+
+    @Test
+    public void testWhenComplete_NotYetComplete() throws Exception {
+        AndroidFuture<Integer> future = new AndroidFuture<>();
+        CountDownLatch latch = new CountDownLatch(1);
+        future.whenComplete((obj, err) -> {
+            assertThat(obj).isEqualTo(5);
+            assertThat(err).isNull();
+            latch.countDown();
+        });
+        assertThat(latch.getCount()).isEqualTo(1);
+        future.complete(5);
+        latch.await();
+        assertThat(latch.getCount()).isEqualTo(0);
+    }
+
+    @Test
+    public void testCompleteExceptionally() {
+        AndroidFuture<Integer> future = new AndroidFuture<>();
+        Exception origException = new UnsupportedOperationException();
+        future.completeExceptionally(origException);
+        ExecutionException executionException =
+                expectThrows(ExecutionException.class, future::get);
+        assertThat(executionException.getCause()).isSameAs(origException);
+    }
+
+    @Test
+    public void testCompleteExceptionally_Listener() throws Exception {
+        AndroidFuture<Integer> future = new AndroidFuture<>();
+        Exception origException = new UnsupportedOperationException();
+        future.completeExceptionally(origException);
+        CountDownLatch latch = new CountDownLatch(1);
+        future.whenComplete((obj, err) -> {
+            assertThat(obj).isNull();
+            assertThat(err).isSameAs(origException);
+            latch.countDown();
+        });
+        latch.await();
+    }
+
+    @Test
+    public void testWriteToParcel() throws Exception {
+        Parcel parcel = Parcel.obtain();
+        AndroidFuture<Integer> future1 = new AndroidFuture<>();
+        future1.complete(5);
+        future1.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        AndroidFuture future2 = AndroidFuture.CREATOR.createFromParcel(parcel);
+        assertThat(future2.get()).isEqualTo(5);
+    }
+
+    @Test
+    public void testWriteToParcel_Exceptionally() throws Exception {
+        Parcel parcel = Parcel.obtain();
+        AndroidFuture<Integer> future1 = new AndroidFuture<>();
+        future1.completeExceptionally(new UnsupportedOperationException());
+        future1.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        AndroidFuture future2 = AndroidFuture.CREATOR.createFromParcel(parcel);
+        ExecutionException executionException =
+                expectThrows(ExecutionException.class, future2::get);
+        assertThat(executionException.getCause()).isInstanceOf(UnsupportedOperationException.class);
+    }
+}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index ff521be..43f65e3 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -88,7 +88,7 @@
 
 prebuilt_etc {
     name: "privapp_whitelist_com.android.launcher3",
-    product_specific: true,
+    system_ext_specific: true,
     sub_dir: "permissions",
     src: "com.android.launcher3.xml",
     filename_from_src: true,
diff --git a/data/etc/CleanSpec.mk b/data/etc/CleanSpec.mk
index 3fe421e..b76eb15 100644
--- a/data/etc/CleanSpec.mk
+++ b/data/etc/CleanSpec.mk
@@ -51,6 +51,8 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.provision.xml)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.settings.xml)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.settings.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.launcher3.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.launcher3.xml)
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
 # ******************************************************************
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index ba877f8..ee989cc 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -51,5 +51,6 @@
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
         <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+        <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 440b885..eff353c 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -79,12 +79,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-1976550065": {
-      "message": "commitVisibility: %s: visible=%b visibleRequested=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "-1963461591": {
       "message": "Removing %s from %s",
       "level": "VERBOSE",
@@ -283,6 +277,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1521427940": {
+      "message": "commitVisibility: %s: visible=%b mVisibleRequested=%b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1515151503": {
       "message": ">>> OPEN TRANSACTION removeReplacedWindows",
       "level": "INFO",
@@ -697,12 +697,6 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
-    "-633961578": {
-      "message": "applyAnimation: transition animation is disabled or skipped. container=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "-622997754": {
       "message": "postWindowRemoveCleanupLocked: %s",
       "level": "VERBOSE",
@@ -1477,12 +1471,6 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
-    "841702299": {
-      "message": "Changing app %s visible=%b performLayout=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "845234215": {
       "message": "App is requesting an orientation, return %d for display id=%d",
       "level": "VERBOSE",
@@ -1993,6 +1981,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "1967975839": {
+      "message": "Changing app %s visible=%b performLayout=%b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/AppTransitionController.java"
+    },
     "1984470582": {
       "message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
       "level": "DEBUG",
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 6619dba..8ebac66 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -1675,6 +1675,9 @@
         if (r == null) {
             return;
         }
+        if (r.width() <= 0 || r.height() <= 0) {
+            throw new IllegalStateException("Subset " + r + " is empty/unsorted");
+        }
         if (r.left < 0 || r.top < 0 || r.right > width || r.bottom > height) {
             throw new IllegalStateException("Subset " + r + " not contained by "
                     + "scaled image bounds: (" + width + " x " + height + ")");
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 9c84634..00ceb2d 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -41,7 +41,7 @@
 
 void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas,
                                                      const SkiaDisplayList& displayList,
-                                                     int nestLevel) {
+                                                     int nestLevel) const {
     LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver);
     for (auto& child : displayList.mChildNodes) {
         const RenderProperties& childProperties = child.getNodeProperties();
@@ -132,7 +132,7 @@
     RenderNode& mNode;
 };
 
-void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
+void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const {
     RenderNode* renderNode = mRenderNode.get();
     MarkDraw _marker{*canvas, *renderNode};
 
@@ -230,7 +230,14 @@
             // we need to restrict the portion of the surface drawn to the size of the renderNode.
             SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
             SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
-            canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), bounds,
+
+            // If SKP recording is active save an annotation that indicates this drawImageRect
+            // could also be rendered with the commands saved at ID associated with this node.
+            if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+                canvas->drawAnnotation(bounds, String8::format(
+                    "SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr);
+            }
+            canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot(), bounds,
                                   bounds, &paint);
 
             if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index 6ba8e59..6c390c3 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -58,7 +58,7 @@
      * projection receiver then all projected children (excluding direct children) will be drawn
      * last. Any projected node not matching those requirements will not be drawn by this function.
      */
-    void forceDraw(SkCanvas* canvas);
+    void forceDraw(SkCanvas* canvas) const;
 
     /**
      * Returns readonly render properties for this render node.
@@ -113,7 +113,7 @@
      * @param nestLevel should be always 0. Used to track how far we are from the receiver.
      */
     void drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList,
-                                     int nestLevel = 0);
+                                     int nestLevel = 0) const;
 
     /**
      * Applies the rendering properties of a view onto a SkCanvas.
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 0647977..11dc013 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -26,11 +26,11 @@
 #include <SkPictureRecorder.h>
 #include <SkSerialProcs.h>
 #include "LightingInfo.h"
-#include "TreeInfo.h"
 #include "VectorDrawable.h"
 #include "thread/CommonPool.h"
 #include "tools/SkSharingProc.h"
 #include "utils/TraceUtils.h"
+#include "utils/String8.h"
 
 #include <unistd.h>
 
@@ -90,58 +90,61 @@
         // only schedule repaint if node still on layer - possible it may have been
         // removed during a dropped frame, but layers may still remain scheduled so
         // as not to lose info on what portion is damaged
-        if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) {
-            SkASSERT(layerNode->getLayerSurface());
-            SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList();
-            if (!displayList || displayList->isEmpty()) {
-                ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName());
-                return;
+        if (CC_UNLIKELY(layerNode->getLayerSurface() == nullptr)) {
+            continue;
+        }
+        SkASSERT(layerNode->getLayerSurface());
+        SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList();
+        if (!displayList || displayList->isEmpty()) {
+            ALOGE("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName());
+            return;
+        }
+
+        const Rect& layerDamage = layers.entries()[i].damage;
+
+        SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
+
+        int saveCount = layerCanvas->save();
+        SkASSERT(saveCount == 1);
+
+        layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
+
+        // TODO: put localized light center calculation and storage to a drawable related code.
+        // It does not seem right to store something localized in a global state
+        // fix here and in recordLayers
+        const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw());
+        Vector3 transformedLightCenter(savedLightCenter);
+        // map current light center into RenderNode's coordinate space
+        layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter);
+        LightingInfo::setLightCenterRaw(transformedLightCenter);
+
+        const RenderProperties& properties = layerNode->properties();
+        const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
+        if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
+            return;
+        }
+
+        ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(),
+                      bounds.height());
+
+        layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
+        layerCanvas->clear(SK_ColorTRANSPARENT);
+
+        RenderNodeDrawable root(layerNode, layerCanvas, false);
+        root.forceDraw(layerCanvas);
+        layerCanvas->restoreToCount(saveCount);
+
+        LightingInfo::setLightCenterRaw(savedLightCenter);
+
+        // cache the current context so that we can defer flushing it until
+        // either all the layers have been rendered or the context changes
+        GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
+        if (cachedContext.get() != currentContext) {
+            if (cachedContext.get()) {
+                ATRACE_NAME("flush layers (context changed)");
+                cachedContext->flush();
             }
-
-            const Rect& layerDamage = layers.entries()[i].damage;
-
-            SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
-
-            int saveCount = layerCanvas->save();
-            SkASSERT(saveCount == 1);
-
-            layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
-
-            // TODO: put localized light center calculation and storage to a drawable related code.
-            // It does not seem right to store something localized in a global state
-            const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw());
-            Vector3 transformedLightCenter(savedLightCenter);
-            // map current light center into RenderNode's coordinate space
-            layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter);
-            LightingInfo::setLightCenterRaw(transformedLightCenter);
-
-            const RenderProperties& properties = layerNode->properties();
-            const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
-            if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
-                return;
-            }
-
-            ATRACE_FORMAT("drawLayer [%s] %.1f x %.1f", layerNode->getName(), bounds.width(),
-                          bounds.height());
-
-            layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
-            layerCanvas->clear(SK_ColorTRANSPARENT);
-
-            RenderNodeDrawable root(layerNode, layerCanvas, false);
-            root.forceDraw(layerCanvas);
-            layerCanvas->restoreToCount(saveCount);
-            LightingInfo::setLightCenterRaw(savedLightCenter);
-
-            // cache the current context so that we can defer flushing it until
-            // either all the layers have been rendered or the context changes
-            GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
-            if (cachedContext.get() != currentContext) {
-                if (cachedContext.get()) {
-                    ATRACE_NAME("flush layers (context changed)");
-                    cachedContext->flush();
-                }
-                cachedContext.reset(SkSafeRef(currentContext));
-            }
+            cachedContext.reset(SkSafeRef(currentContext));
         }
     }
 
@@ -275,12 +278,60 @@
     }
 }
 
-SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) {
+// recurse through the rendernode's children, add any nodes which are layers to the queue.
+static void collectLayers(RenderNode* node, LayerUpdateQueue* layers) {
+    SkiaDisplayList* dl = (SkiaDisplayList*)node->getDisplayList();
+    if (dl) {
+        const auto& prop = node->properties();
+        if (node->hasLayer()) {
+            layers->enqueueLayerWithDamage(node, Rect(prop.getWidth(), prop.getHeight()));
+        }
+        // The way to recurse through rendernodes is to call this with a lambda.
+        dl->updateChildren([&](RenderNode* child) { collectLayers(child, layers); });
+    }
+}
+
+// record the provided layers to the provided canvas as self-contained skpictures.
+static void recordLayers(const LayerUpdateQueue& layers,
+    SkCanvas* mskpCanvas) {
+    const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw());
+    // Record the commands to re-draw each dirty layer into an SkPicture
+    for (size_t i = 0; i < layers.entries().size(); i++) {
+        RenderNode* layerNode = layers.entries()[i].renderNode.get();
+        const Rect& layerDamage = layers.entries()[i].damage;
+        const RenderProperties& properties = layerNode->properties();
+
+        // Temporarily map current light center into RenderNode's coordinate space
+        Vector3 transformedLightCenter(savedLightCenter);
+        layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter);
+        LightingInfo::setLightCenterRaw(transformedLightCenter);
+
+        SkPictureRecorder layerRec;
+        auto* recCanvas = layerRec.beginRecording(properties.getWidth(),
+            properties.getHeight());
+        // This is not recorded but still causes clipping.
+        recCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
+        RenderNodeDrawable root(layerNode, recCanvas, false);
+        root.forceDraw(recCanvas);
+        // Now write this picture into the SKP canvas with an annotation indicating what it is
+        mskpCanvas->drawAnnotation(layerDamage.toSkRect(), String8::format(
+            "OffscreenLayerDraw|%" PRId64, layerNode->uniqueId()).c_str(), nullptr);
+        mskpCanvas->drawPicture(layerRec.finishRecordingAsPicture());
+    }
+    LightingInfo::setLightCenterRaw(savedLightCenter);
+}
+
+SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface, RenderNode* root,
+    const LayerUpdateQueue& dirtyLayers) {
     if (CC_LIKELY(!Properties::skpCaptureEnabled)) {
         return surface->getCanvas(); // Bail out early when capture is not turned on.
     }
     // Note that shouldStartNewFileCapture tells us if this is the *first* frame of a capture.
+    bool firstFrameOfAnim = false;
     if (shouldStartNewFileCapture() && mCaptureMode == CaptureMode::MultiFrameSKP) {
+        // set a reminder to record every layer near the end of this method, after we have set up
+        // the nway canvas.
+        firstFrameOfAnim = true;
         if (!setupMultiFrameCapture()) {
             return surface->getCanvas();
         }
@@ -309,6 +360,20 @@
     mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
     mNwayCanvas->addCanvas(surface->getCanvas());
     mNwayCanvas->addCanvas(pictureCanvas);
+
+    if (firstFrameOfAnim) {
+        // On the first frame of any mskp capture we want to record any layers that are needed in
+        // frame but may have been rendered offscreen before recording began.
+        // We do not maintain a list of all layers, since it isn't needed outside this rare,
+        // recording use case. Traverse the tree to find them and put them in this LayerUpdateQueue.
+        LayerUpdateQueue luq;
+        collectLayers(root, &luq);
+        recordLayers(luq, mNwayCanvas.get());
+    } else {
+        // on non-first frames, we record any normal layer draws (dirty regions)
+        recordLayers(dirtyLayers, mNwayCanvas.get());
+    }
+
     return mNwayCanvas.get();
 }
 
@@ -359,13 +424,13 @@
         Properties::skpCaptureEnabled = true;
     }
 
+    // Initialize the canvas for the current frame, that might be a recording canvas if SKP
+    // capture is enabled.
+    SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers);
+
     // draw all layers up front
     renderLayersImpl(layers, opaque);
 
-    // initialize the canvas for the current frame, that might be a recording canvas if SKP
-    // capture is enabled.
-    SkCanvas* canvas = tryCapture(surface.get());
-
     renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform);
 
     endCapture(surface.get());
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 7d575ad..af8414d 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -45,6 +45,8 @@
     void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
                       bool opaque, const LightInfo& lightInfo) override;
 
+    // If the given node didn't have a layer surface, or had one of the wrong size, this method
+    // creates a new one and returns true. Otherwise does nothing and returns false.
     bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
                              ErrorHandler* errorHandler) override;
 
@@ -92,7 +94,7 @@
 
     // Called every frame. Normally returns early with screen canvas.
     // But when capture is enabled, returns an nwaycanvas where commands are also recorded.
-    SkCanvas* tryCapture(SkSurface* surface);
+    SkCanvas* tryCapture(SkSurface* surface, RenderNode* root, const LayerUpdateQueue& dirtyLayers);
     // Called at the end of every frame, closes the recording if necessary.
     void endCapture(SkSurface* surface);
     // Determine if a new file-based capture should be started.
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
index b0fad57d..901ffaa 100644
--- a/libs/services/Android.bp
+++ b/libs/services/Android.bp
@@ -22,17 +22,16 @@
         "src/os/DropBoxManager.cpp",
         "src/os/StatsDimensionsValue.cpp",
         "src/os/StatsLogEventWrapper.cpp",
-        "src/util/StatsEvent.cpp",
     ],
 
     shared_libs: [
         "libbinder",
-        "liblog",
         "libcutils",
+        "liblog",
         "libutils",
     ],
     header_libs: [
-	"libbase_headers",
+        "libbase_headers",
     ],
     aidl: {
         include_dirs: ["frameworks/base/core/java/"],
diff --git a/libs/services/include/android/util/StatsEvent.h b/libs/services/include/android/util/StatsEvent.h
deleted file mode 100644
index 4863117..0000000
--- a/libs/services/include/android/util/StatsEvent.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-#ifndef STATS_EVENT_H
-#define STATS_EVENT_H
-
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-#include <binder/Status.h>
-#include <vector>
-
-namespace android {
-namespace util {
-class StatsEvent : public android::Parcelable {
-    public:
-        StatsEvent();
-
-        StatsEvent(StatsEvent&& in) = default;
-
-        android::status_t writeToParcel(android::Parcel* out) const;
-
-        android::status_t readFromParcel(const android::Parcel* in);
-
-    private:
-        int mAtomTag;
-        std::vector<uint8_t> mBuffer;
-};
-} // Namespace util
-} // Namespace android
-
-#endif  // STATS_ EVENT_H
\ No newline at end of file
diff --git a/libs/services/src/util/StatsEvent.cpp b/libs/services/src/util/StatsEvent.cpp
deleted file mode 100644
index 8b85791..0000000
--- a/libs/services/src/util/StatsEvent.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.
- */
-#include <android/util/StatsEvent.h>
-
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-#include <binder/Status.h>
-#include <vector>
-
-using android::Parcel;
-using android::Parcelable;
-using android::status_t;
-using std::vector;
-
-namespace android {
-namespace util {
-
-StatsEvent::StatsEvent(){};
-
-status_t StatsEvent::writeToParcel(Parcel* out) const {
-    // Implement me if desired. We don't currently use this.
-    ALOGE("Cannot do c++ StatsEvent.writeToParcel(); it is not implemented.");
-    (void)out;  // To prevent compile error of unused parameter 'out'
-    return UNKNOWN_ERROR;
-};
-
-status_t StatsEvent::readFromParcel(const Parcel* in) {
-    status_t res = OK;
-    if (in == NULL) {
-        ALOGE("statsd received parcel argument was NULL.");
-        return BAD_VALUE;
-    }
-    if ((res = in->readInt32(&mAtomTag)) != OK) {
-        ALOGE("statsd could not read atom tag from parcel");
-        return res;
-    }
-    if ((res = in->readByteVector(&mBuffer)) != OK) {
-        ALOGE("statsd could not read buffer from parcel");
-        return res;
-    }
-    return NO_ERROR;
-};
-
-} // Namespace util
-} // Namespace android
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 1f8c1d53..aa1484f 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -125,6 +125,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final String FUSED_PROVIDER = "fused";
 
     /**
@@ -1837,12 +1838,9 @@
     @Deprecated
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) {
-        UnsupportedOperationException ex = new UnsupportedOperationException(
-                "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead");
         if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.R) {
-            throw ex;
-        } else {
-            Log.w(TAG, ex);
+            throw new UnsupportedOperationException(
+                    "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead");
         }
 
         GnssStatus gnssStatus = mGnssStatusListenerManager.getGnssStatus();
diff --git a/media/apex/java/android/media/MediaParser.java b/media/apex/java/android/media/MediaParser.java
index 8824269..b83f445 100644
--- a/media/apex/java/android/media/MediaParser.java
+++ b/media/apex/java/android/media/MediaParser.java
@@ -156,19 +156,29 @@
      * <p>A {@link SeekPoint} is a position in the stream from which a player may successfully start
      * playing media samples.
      */
-    public interface SeekMap {
+    public static final class SeekMap {
 
         /** Returned by {@link #getDurationUs()} when the duration is unknown. */
-        int UNKNOWN_DURATION = Integer.MIN_VALUE;
+        public static final int UNKNOWN_DURATION = Integer.MIN_VALUE;
+
+        private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
+
+        private SeekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
+            mExoPlayerSeekMap = exoplayerSeekMap;
+        }
 
         /** Returns whether seeking is supported. */
-        boolean isSeekable();
+        public boolean isSeekable() {
+            return mExoPlayerSeekMap.isSeekable();
+        }
 
         /**
          * Returns the duration of the stream in microseconds or {@link #UNKNOWN_DURATION} if the
          * duration is unknown.
          */
-        long getDurationUs();
+        public long getDurationUs() {
+            return mExoPlayerSeekMap.getDurationUs();
+        }
 
         /**
          * Obtains {@link SeekPoint SeekPoints} for the specified seek time in microseconds.
@@ -184,7 +194,10 @@
          * @return The corresponding {@link SeekPoint SeekPoints}.
          */
         @NonNull
-        Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs);
+        public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) {
+            SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs);
+            return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second));
+        }
     }
 
     /** Defines a seek point in a media stream. */
@@ -647,7 +660,7 @@
 
         @Override
         public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
-            mOutputConsumer.onSeekMap(new ExoToMediaParserSeekMapAdapter(exoplayerSeekMap));
+            mOutputConsumer.onSeekMap(new SeekMap(exoplayerSeekMap));
         }
     }
 
@@ -764,32 +777,6 @@
         Extractor createInstance();
     }
 
-    private static class ExoToMediaParserSeekMapAdapter implements SeekMap {
-
-        private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
-
-        private ExoToMediaParserSeekMapAdapter(
-                com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
-            mExoPlayerSeekMap = exoplayerSeekMap;
-        }
-
-        @Override
-        public boolean isSeekable() {
-            return mExoPlayerSeekMap.isSeekable();
-        }
-
-        @Override
-        public long getDurationUs() {
-            return mExoPlayerSeekMap.getDurationUs();
-        }
-
-        @Override
-        public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) {
-            SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs);
-            return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second));
-        }
-    }
-
     // Private static methods.
 
     private static MediaFormat toMediaFormat(Format format) {
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index c03e8e2..8de3e0a 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.media.audiopolicy.AudioProductStrategy;
@@ -178,6 +179,13 @@
      * utterances.
      */
     public final static int USAGE_ASSISTANT = 16;
+    /**
+     * @hide
+     * Usage value to use for assistant voice interaction with remote caller on Cell and VoIP calls.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public static final int USAGE_CALL_ASSISTANT = 17;
 
     /**
      * IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES
@@ -254,6 +262,7 @@
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,    SUPPRESSIBLE_MEDIA);
         SUPPRESSIBLE_USAGES.put(USAGE_GAME,                              SUPPRESSIBLE_MEDIA);
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT,                         SUPPRESSIBLE_MEDIA);
+        SUPPRESSIBLE_USAGES.put(USAGE_CALL_ASSISTANT,                    SUPPRESSIBLE_NEVER);
         /** default volume assignment is STREAM_MUSIC, handle unknown usage as media */
         SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN,                           SUPPRESSIBLE_MEDIA);
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_SONIFICATION,           SUPPRESSIBLE_SYSTEM);
@@ -682,6 +691,7 @@
                 case USAGE_GAME:
                 case USAGE_VIRTUAL_SOURCE:
                 case USAGE_ASSISTANT:
+                case USAGE_CALL_ASSISTANT:
                     mUsage = usage;
                     break;
                 default:
@@ -1135,6 +1145,8 @@
                 return new String("USAGE_GAME");
             case USAGE_ASSISTANT:
                 return new String("USAGE_ASSISTANT");
+            case USAGE_CALL_ASSISTANT:
+                return new String("USAGE_CALL_ASSISTANT");
             default:
                 return new String("unknown usage " + usage);
         }
@@ -1238,6 +1250,7 @@
             case USAGE_ASSISTANCE_SONIFICATION:
                 return AudioSystem.STREAM_SYSTEM;
             case USAGE_VOICE_COMMUNICATION:
+            case USAGE_CALL_ASSISTANT:
                 return AudioSystem.STREAM_VOICE_CALL;
             case USAGE_VOICE_COMMUNICATION_SIGNALLING:
                 return fromGetVolumeControlStream ?
@@ -1302,6 +1315,7 @@
         USAGE_ASSISTANCE_SONIFICATION,
         USAGE_GAME,
         USAGE_ASSISTANT,
+        USAGE_CALL_ASSISTANT,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AttributeUsage {}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 94a6f13..dead066 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -46,6 +46,8 @@
  * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr>
  * <tr><td>{@link #KEY_MIME}</td><td>String</td><td>The type of the format.</td></tr>
  * <tr><td>{@link #KEY_MAX_INPUT_SIZE}</td><td>Integer</td><td>optional, maximum size of a buffer of input data</td></tr>
+ * <tr><td>{@link #KEY_PIXEL_ASPECT_RATIO_WIDTH}</td><td>Integer</td><td>optional, the pixel aspect ratio width</td></tr>
+ * <tr><td>{@link #KEY_PIXEL_ASPECT_RATIO_HEIGHT}</td><td>Integer</td><td>optional, the pixel aspect ratio height</td></tr>
  * <tr><td>{@link #KEY_BIT_RATE}</td><td>Integer</td><td><b>encoder-only</b>, desired bitrate in bits/second</td></tr>
  * </table>
  *
@@ -272,6 +274,18 @@
     public static final String KEY_MAX_INPUT_SIZE = "max-input-size";
 
     /**
+     * A key describing the pixel aspect ratio width.
+     * The associated value is an integer
+     */
+    public static final String KEY_PIXEL_ASPECT_RATIO_WIDTH = "sar-width";
+
+    /**
+     * A key describing the pixel aspect ratio height.
+     * The associated value is an integer
+     */
+    public static final String KEY_PIXEL_ASPECT_RATIO_HEIGHT = "sar-height";
+
+    /**
      * A key describing the average bitrate in bits/sec.
      * The associated value is an integer
      */
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index dc400ad..61b3e76 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -26,9 +26,11 @@
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
+import android.hardware.soundtrigger.ModelParams;
 import android.hardware.soundtrigger.SoundTrigger;
 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
 import android.hardware.soundtrigger.SoundTrigger.SoundModel;
 import android.os.Bundle;
@@ -67,7 +69,7 @@
     /**
      * @hide
      */
-    public SoundTriggerManager(Context context, ISoundTriggerService soundTriggerService ) {
+    public SoundTriggerManager(Context context, ISoundTriggerService soundTriggerService) {
         if (DBG) {
             Slog.i(TAG, "SoundTriggerManager created.");
         }
@@ -89,14 +91,22 @@
     }
 
     /**
-     * Returns the sound trigger model represented by the given UUID. An instance of {@link Model}
-     * is returned.
+     * Get {@link SoundTriggerManager.Model} which is registered with the passed UUID
+     *
+     * @param soundModelId UUID associated with a loaded model
+     * @return {@link SoundTriggerManager.Model} associated with UUID soundModelId
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
+    @Nullable
     public Model getModel(UUID soundModelId) {
         try {
-            return new Model(mSoundTriggerService.getSoundModel(
-                    new ParcelUuid(soundModelId)));
+            GenericSoundModel model =
+                    mSoundTriggerService.getSoundModel(new ParcelUuid(soundModelId));
+            if (model == null) {
+                return null;
+            }
+
+            return new Model(model);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -399,4 +409,80 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Set a model specific {@link ModelParams} with the given value. This
+     * parameter will keep its value for the duration the model is loaded regardless of starting and
+     * stopping recognition. Once the model is unloaded, the value will be lost.
+     * {@link SoundTriggerManager#queryParameter} should be checked first before calling this
+     * method.
+     *
+     * @param soundModelId UUID of model to apply the parameter value to.
+     * @param modelParam   {@link ModelParams}
+     * @param value        Value to set
+     * @return - {@link SoundTrigger#STATUS_OK} in case of success
+     *         - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+     *         - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
+     *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
+     *           if API is not supported by HAL
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
+    public int setParameter(@Nullable UUID soundModelId,
+            @ModelParams int modelParam, int value)
+            throws UnsupportedOperationException, IllegalArgumentException {
+        try {
+            return mSoundTriggerService.setParameter(new ParcelUuid(soundModelId), modelParam,
+                    value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get a model specific {@link ModelParams}. This parameter will keep its value
+     * for the duration the model is loaded regardless of starting and stopping recognition.
+     * Once the model is unloaded, the value will be lost. If the value is not set, a default
+     * value is returned. See {@link ModelParams} for parameter default values.
+     * {@link SoundTriggerManager#queryParameter} should be checked first before
+     * calling this method. Otherwise, an exception can be thrown.
+     *
+     * @param soundModelId UUID of model to get parameter
+     * @param modelParam   {@link ModelParams}
+     * @return value of parameter
+     * @throws UnsupportedOperationException if hal or model do not support this API.
+     *         {@link SoundTriggerManager#queryParameter} should be checked first.
+     * @throws IllegalArgumentException if invalid model handle or parameter is passed.
+     *         {@link SoundTriggerManager#queryParameter} should be checked first.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
+    public int getParameter(@NonNull UUID soundModelId,
+            @ModelParams int modelParam)
+            throws UnsupportedOperationException, IllegalArgumentException {
+        try {
+            return mSoundTriggerService.getParameter(new ParcelUuid(soundModelId), modelParam);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Determine if parameter control is supported for the given model handle.
+     * This method should be checked prior to calling {@link SoundTriggerManager#setParameter} or
+     * {@link SoundTriggerManager#getParameter}.
+     *
+     * @param soundModelId handle of model to get parameter
+     * @param modelParam {@link ModelParams}
+     * @return supported range of parameter, null if not supported
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
+    @Nullable
+    public ModelParamRange queryParameter(@Nullable UUID soundModelId,
+            @ModelParams int modelParam) {
+        try {
+            return mSoundTriggerService.queryParameter(new ParcelUuid(soundModelId),
+                    modelParam);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/media/java/android/media/tv/tuner/DvrSettings.java b/media/java/android/media/tv/tuner/DvrSettings.java
new file mode 100644
index 0000000..76160dc
--- /dev/null
+++ b/media/java/android/media/tv/tuner/DvrSettings.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerConstants.DataFormat;
+import android.media.tv.tuner.TunerConstants.DvrSettingsType;
+
+/**
+ * DVR settings.
+ *
+ * @hide
+ */
+public class DvrSettings {
+    private int mStatusMask;
+    private int mLowThreshold;
+    private int mHighThreshold;
+    private int mPacketSize;
+
+    @DataFormat
+    private int mDataFormat;
+    @DvrSettingsType
+    private int mType;
+
+    private DvrSettings(int statusMask, int lowThreshold, int highThreshold, int packetSize,
+            @DataFormat int dataFormat, @DvrSettingsType int type) {
+        mStatusMask = statusMask;
+        mLowThreshold = lowThreshold;
+        mHighThreshold = highThreshold;
+        mPacketSize = packetSize;
+        mDataFormat = dataFormat;
+        mType = type;
+    }
+
+    /**
+     * Creates a new builder.
+     */
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for DvrSettings.
+     */
+    public static final class Builder {
+        private int mStatusMask;
+        private int mLowThreshold;
+        private int mHighThreshold;
+        private int mPacketSize;
+        @DataFormat
+        private int mDataFormat;
+        @DvrSettingsType
+        private int mType;
+
+        /**
+         * Sets status mask.
+         */
+        public Builder setStatusMask(int statusMask) {
+            this.mStatusMask = statusMask;
+            return this;
+        }
+
+        /**
+         * Sets low threshold.
+         */
+        public Builder setLowThreshold(int lowThreshold) {
+            this.mLowThreshold = lowThreshold;
+            return this;
+        }
+
+        /**
+         * Sets high threshold.
+         */
+        public Builder setHighThreshold(int highThreshold) {
+            this.mHighThreshold = highThreshold;
+            return this;
+        }
+
+        /**
+         * Sets packet size.
+         */
+        public Builder setPacketSize(int packetSize) {
+            this.mPacketSize = packetSize;
+            return this;
+        }
+
+        /**
+         * Sets data format.
+         */
+        public Builder setDataFormat(@DataFormat int dataFormat) {
+            this.mDataFormat = dataFormat;
+            return this;
+        }
+
+        /**
+         * Sets settings type.
+         */
+        public Builder setType(@DvrSettingsType int type) {
+            this.mType = type;
+            return this;
+        }
+
+        /**
+         * Builds a DvrSettings instance.
+         */
+        public DvrSettings build() {
+            return new DvrSettings(
+                    mStatusMask, mLowThreshold, mHighThreshold, mPacketSize, mDataFormat, mType);
+        }
+    }
+}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 6537f6f..4c93101 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -368,6 +368,7 @@
 
         private native boolean nativeAttachFilter(Filter filter);
         private native boolean nativeDetachFilter(Filter filter);
+        private native int nativeConfigureDvr(DvrSettings settings);
         private native boolean nativeStartDvr();
         private native boolean nativeStopDvr();
         private native boolean nativeFlushDvr();
@@ -380,6 +381,9 @@
         public boolean detachFilter(Filter filter) {
             return nativeDetachFilter(filter);
         }
+        public int configure(DvrSettings settings) {
+            return nativeConfigureDvr(settings);
+        }
         public boolean start() {
             return nativeStartDvr();
         }
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index f2d5e93..261b2de 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -101,6 +101,13 @@
     public static final int FILTER_SETTINGS_TLV = Constants.DemuxFilterMainType.TLV;
     public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP;
 
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({DVR_SETTINGS_RECORD, DVR_SETTINGS_PLAYBACK})
+    public @interface DvrSettingsType {}
+
+    public static final int DVR_SETTINGS_RECORD = Constants.DvrType.RECORD;
+    public static final int DVR_SETTINGS_PLAYBACK = Constants.DvrType.PLAYBACK;
+
     private TunerConstants() {
     }
 }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index a0be12e..9304450 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -27,6 +27,7 @@
 
 using ::android::hardware::Void;
 using ::android::hardware::hidl_vec;
+using ::android::hardware::tv::tuner::V1_0::DataFormat;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
@@ -35,10 +36,13 @@
 using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
 using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_0::DvrSettings;
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSettings;
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
 using ::android::hardware::tv::tuner::V1_0::ITuner;
+using ::android::hardware::tv::tuner::V1_0::PlaybackSettings;
+using ::android::hardware::tv::tuner::V1_0::RecordSettings;
 using ::android::hardware::tv::tuner::V1_0::Result;
 
 struct fields_t {
@@ -93,6 +97,14 @@
     mDvr = env->NewWeakGlobalRef(dvr);
 }
 
+/////////////// Dvr ///////////////////////
+
+Dvr::Dvr(sp<IDvr> sp, jweak obj) : mDvrSp(sp), mDvrObj(obj) {}
+
+sp<IDvr> Dvr::getIDvr() {
+    return mDvrSp;
+}
+
 /////////////// FilterCallback ///////////////////////
 //TODO: implement filter callback
 Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& /*filterEvent*/) {
@@ -391,14 +403,14 @@
             return NULL;
         }
     }
-    sp<IDvr> dvrSp;
+    sp<IDvr> iDvrSp;
     sp<DvrCallback> callback = new DvrCallback();
     mDemux->openDvr(type, bufferSize, callback,
             [&](Result, const sp<IDvr>& dvr) {
-                dvrSp = dvr;
+                iDvrSp = dvr;
             });
 
-    if (dvrSp == NULL) {
+    if (iDvrSp == NULL) {
         return NULL;
     }
 
@@ -408,7 +420,7 @@
                     env->FindClass("android/media/tv/tuner/Tuner$Dvr"),
                     gFields.dvrInitID,
                     mObject);
-
+    sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj);
     dvrSp->incStrong(dvrObj);
     env->SetLongField(dvrObj, gFields.dvrContext, (jlong)dvrSp.get());
 
@@ -485,8 +497,51 @@
     return (Filter *)env->GetLongField(filter, gFields.filterContext);
 }
 
-static sp<IDvr> getDvr(JNIEnv *env, jobject dvr) {
-    return (IDvr *)env->GetLongField(dvr, gFields.dvrContext);
+static DvrSettings getDvrSettings(JNIEnv *env, jobject settings) {
+    DvrSettings dvrSettings;
+    jclass clazz = env->FindClass("android/media/tv/tuner/DvrSettings");
+    uint32_t statusMask =
+            static_cast<uint32_t>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mStatusMask", "I")));
+    uint32_t lowThreshold =
+            static_cast<uint32_t>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mLowThreshold", "I")));
+    uint32_t highThreshold =
+            static_cast<uint32_t>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mHighThreshold", "I")));
+    uint8_t packetSize =
+            static_cast<uint8_t>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mPacketSize", "I")));
+    DataFormat dataFormat =
+            static_cast<DataFormat>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mDataFormat", "I")));
+    DvrType type =
+            static_cast<DvrType>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mType", "I")));
+    if (type == DvrType::RECORD) {
+        RecordSettings recordSettings {
+                .statusMask = static_cast<unsigned char>(statusMask),
+                .lowThreshold = lowThreshold,
+                .highThreshold = highThreshold,
+                .dataFormat = dataFormat,
+                .packetSize = packetSize,
+        };
+        dvrSettings.record(recordSettings);
+    } else if (type == DvrType::PLAYBACK) {
+        PlaybackSettings PlaybackSettings {
+                .statusMask = statusMask,
+                .lowThreshold = lowThreshold,
+                .highThreshold = highThreshold,
+                .dataFormat = dataFormat,
+                .packetSize = packetSize,
+        };
+        dvrSettings.playback(PlaybackSettings);
+    }
+    return dvrSettings;
+}
+
+static sp<Dvr> getDvr(JNIEnv *env, jobject dvr) {
+    return (Dvr *)env->GetLongField(dvr, gFields.dvrContext);
 }
 
 static void android_media_tv_Tuner_native_init(JNIEnv *env) {
@@ -730,7 +785,7 @@
 }
 
 static bool android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
-    sp<IDvr> dvrSp = getDvr(env, dvr);
+    sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
     sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
     if (dvrSp == NULL || filterSp == NULL) {
         return false;
@@ -740,7 +795,7 @@
 }
 
 static bool android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) {
-    sp<IDvr> dvrSp = getDvr(env, dvr);
+    sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
     sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
     if (dvrSp == NULL || filterSp == NULL) {
         return false;
@@ -749,8 +804,18 @@
     return result == Result::SUCCESS;
 }
 
+static int android_media_tv_Tuner_configure_dvr(JNIEnv *env, jobject dvr, jobject settings) {
+    sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
+    if (dvrSp == NULL) {
+        ALOGD("Failed to configure dvr: dvr not found");
+        return (int)Result::INVALID_STATE;
+    }
+    Result result = dvrSp->configure(getDvrSettings(env, settings));
+    return (int)result;
+}
+
 static bool android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) {
-    sp<IDvr> dvrSp = getDvr(env, dvr);
+    sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
     if (dvrSp == NULL) {
         ALOGD("Failed to start dvr: dvr not found");
         return false;
@@ -759,7 +824,7 @@
 }
 
 static bool android_media_tv_Tuner_stop_dvr(JNIEnv *env, jobject dvr) {
-    sp<IDvr> dvrSp = getDvr(env, dvr);
+    sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
     if (dvrSp == NULL) {
         ALOGD("Failed to stop dvr: dvr not found");
         return false;
@@ -768,7 +833,7 @@
 }
 
 static bool android_media_tv_Tuner_flush_dvr(JNIEnv *env, jobject dvr) {
-    sp<IDvr> dvrSp = getDvr(env, dvr);
+    sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
     if (dvrSp == NULL) {
         ALOGD("Failed to flush dvr: dvr not found");
         return false;
@@ -818,6 +883,8 @@
             (void *)android_media_tv_Tuner_attach_filter },
     { "nativeDetachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)Z",
             (void *)android_media_tv_Tuner_detach_filter },
+    { "nativeConfigureDvr", "(Landroid/media/tv/tuner/DvrSettings;)I",
+            (void *)android_media_tv_Tuner_configure_dvr },
     { "nativeStartDvr", "()Z", (void *)android_media_tv_Tuner_start_dvr },
     { "nativeStopDvr", "()Z", (void *)android_media_tv_Tuner_stop_dvr },
     { "nativeFlushDvr", "()Z", (void *)android_media_tv_Tuner_flush_dvr },
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 467acb8..9f9fb27 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -77,6 +77,13 @@
     jweak mDvr;
 };
 
+struct Dvr : public RefBase {
+    Dvr(sp<IDvr> sp, jweak obj);
+    sp<IDvr> getIDvr();
+    sp<IDvr> mDvrSp;
+    jweak mDvrObj;
+};
+
 struct FilterCallback : public IFilterCallback {
     virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
     virtual Return<void> onFilterStatus(const DemuxFilterStatus status);
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 41ab670..5ba5c01 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -17,6 +17,7 @@
         "libnativehelper",
         "libaudioclient",
         "libaudioutils",
+        "libaudiofoundation",
     ],
 
     version_script: "exports.lds",
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index c8f0ff1..79e4d8a 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -38,7 +38,7 @@
 // kPlayOnCallingThread = true prior to R.
 // Changing to false means calls to play() are almost instantaneous instead of taking around
 // ~10ms to launch the AudioTrack. It is perhaps 100x faster.
-static constexpr bool kPlayOnCallingThread = false;
+static constexpr bool kPlayOnCallingThread = true;
 
 // Amount of time for a StreamManager thread to wait before closing.
 static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND;
@@ -170,6 +170,7 @@
                     if (stream->getSoundID() == soundID) {
                         ALOGV("%s: found soundID %d in restart queue", __func__, soundID);
                         newStream = stream;
+                        fromAvailableQueue = false;
                         break;
                     } else if (newStream == nullptr) {
                         ALOGV("%s: found stream in restart queue", __func__);
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index bffc6b9..03bd61a 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -35,6 +35,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
+import com.android.systemui.statusbar.car.CarShadeControllerImpl;
 import com.android.systemui.statusbar.car.CarStatusBar;
 import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -119,7 +120,7 @@
             KeyguardEnvironmentImpl keyguardEnvironment);
 
     @Binds
-    abstract ShadeController provideShadeController(CarStatusBar statusBar);
+    abstract ShadeController provideShadeController(CarShadeControllerImpl shadeController);
 
     @Provides
     @Singleton
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarShadeControllerImpl.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarShadeControllerImpl.java
new file mode 100644
index 0000000..d1d352a
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarShadeControllerImpl.java
@@ -0,0 +1,85 @@
+/*
+ * 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.car;
+
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.car.notification.CarNotificationView;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.ShadeControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
+/** Car specific implementation of {@link com.android.systemui.statusbar.phone.ShadeController}. */
+@Singleton
+public class CarShadeControllerImpl extends ShadeControllerImpl {
+
+    @Inject
+    public CarShadeControllerImpl(CommandQueue commandQueue,
+            StatusBarStateController statusBarStateController,
+            StatusBarWindowController statusBarWindowController,
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+            WindowManager windowManager,
+            Lazy<StatusBar> statusBarLazy,
+            Lazy<AssistManager> assistManagerLazy,
+            Lazy<BubbleController> bubbleControllerLazy) {
+        super(commandQueue, statusBarStateController, statusBarWindowController,
+                statusBarKeyguardViewManager, windowManager,
+                statusBarLazy, assistManagerLazy, bubbleControllerLazy);
+    }
+
+    @Override
+    public void animateCollapsePanels(int flags, boolean force, boolean delayed,
+            float speedUpFactor) {
+        super.animateCollapsePanels(flags, force, delayed, speedUpFactor);
+        if (!getCarStatusBar().isPanelExpanded()
+                || getCarNotificationView().getVisibility() == View.INVISIBLE) {
+            return;
+        }
+
+        mStatusBarWindowController.setStatusBarFocusable(false);
+        getCarStatusBar().getStatusBarWindowViewController().cancelExpandHelper();
+        getStatusBarView().collapsePanel(true /* animate */, delayed, speedUpFactor);
+
+        getCarStatusBar().animateNotificationPanel(getCarStatusBar().getClosingVelocity(), true);
+
+        if (!getCarStatusBar().isTracking()) {
+            mStatusBarWindowController.setPanelVisible(false);
+            getCarNotificationView().setVisibility(View.INVISIBLE);
+        }
+
+        getCarStatusBar().setPanelExpanded(false);
+    }
+
+    private CarStatusBar getCarStatusBar() {
+        return (CarStatusBar) mStatusBarLazy.get();
+    }
+
+    private CarNotificationView getCarNotificationView() {
+        return getCarStatusBar().getCarNotificationView();
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index c8532e0..0d6701d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -118,6 +118,7 @@
 import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarComponent;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -175,6 +176,7 @@
     private final Object mQueueLock = new Object();
     private final CarNavigationBarController mCarNavigationBarController;
     private final Lazy<PowerManagerHelper> mPowerManagerHelperLazy;
+    private final ShadeController mShadeController;
     private final CarServiceProvider mCarServiceProvider;
 
     private DeviceProvisionedController mDeviceProvisionedController;
@@ -308,6 +310,7 @@
             LightsOutNotifController lightsOutNotifController,
             StatusBarNotificationActivityStarter.Builder
                     statusBarNotificationActivityStarterBuilder,
+            ShadeController shadeController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             ViewMediatorCallback viewMediatorCallback,
             DismissCallbackRegistry dismissCallbackRegistry,
@@ -385,6 +388,7 @@
                 dividerOptional,
                 lightsOutNotifController,
                 statusBarNotificationActivityStarterBuilder,
+                shadeController,
                 superStatusBarViewFactory,
                 statusBarKeyguardViewManager,
                 viewMediatorCallback,
@@ -392,6 +396,7 @@
         mScrimController = scrimController;
         mLockscreenLockIconController = lockscreenLockIconController;
         mDeviceProvisionedController = deviceProvisionedController;
+        mShadeController = shadeController;
         mCarServiceProvider = carServiceProvider;
         mPowerManagerHelperLazy = powerManagerHelperLazy;
         mFullscreenUserSwitcherLazy = fullscreenUserSwitcherLazy;
@@ -506,7 +511,7 @@
                     @Override
                     protected void close() {
                         if (mPanelExpanded) {
-                            animateCollapsePanels();
+                            mShadeController.animateCollapsePanels();
                         }
                     }
                 });
@@ -516,7 +521,7 @@
                     @Override
                     protected void close() {
                         if (mPanelExpanded) {
-                            animateCollapsePanels();
+                            mShadeController.animateCollapsePanels();
                         }
                     }
                 });
@@ -551,7 +556,7 @@
         mNotificationClickHandlerFactory.registerClickListener((launchResult, alertEntry) -> {
             if (launchResult == ActivityManager.START_TASK_TO_FRONT
                     || launchResult == ActivityManager.START_SUCCESS) {
-                animateCollapsePanels();
+                mShadeController.animateCollapsePanels();
             }
         });
         CarNotificationListener carNotificationListener = new CarNotificationListener();
@@ -712,25 +717,16 @@
         setPanelExpanded(true);
     }
 
-    @Override
-    public void animateCollapsePanels(int flags, boolean force, boolean delayed,
-            float speedUpFactor) {
-        super.animateCollapsePanels(flags, force, delayed, speedUpFactor);
-        if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) {
-            return;
-        }
-        mStatusBarWindowController.setStatusBarFocusable(false);
-        mStatusBarWindowViewController.cancelExpandHelper();
-        mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
+    public CarNotificationView getCarNotificationView() {
+        return mNotificationView;
+    }
 
-        animateNotificationPanel(mClosingVelocity, true);
+    public float getClosingVelocity() {
+        return mClosingVelocity;
+    }
 
-        if (!mIsTracking) {
-            mStatusBarWindowController.setPanelVisible(false);
-            mNotificationView.setVisibility(View.INVISIBLE);
-        }
-
-        setPanelExpanded(false);
+    public boolean isTracking() {
+        return mIsTracking;
     }
 
     private void maybeCompleteAnimation(MotionEvent event) {
@@ -749,7 +745,7 @@
      * close the notification shade completely with a velocity. If the animation is to close the
      * notification shade this method also makes the view invisible after animation ends.
      */
-    private void animateNotificationPanel(float velocity, boolean isClosing) {
+    void animateNotificationPanel(float velocity, boolean isClosing) {
         float to = 0;
         if (!isClosing) {
             to = mNotificationView.getHeight();
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index eff60fa..ff4dc9c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -78,6 +78,7 @@
 import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBarComponent;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -183,6 +184,7 @@
             LightsOutNotifController lightsOutNotifController,
             StatusBarNotificationActivityStarter.Builder
                     statusBarNotificationActivityStarterBuilder,
+            ShadeController shadeController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             ViewMediatorCallback viewMediatorCallback,
             DismissCallbackRegistry dismissCallbackRegistry,
@@ -259,6 +261,7 @@
                 superStatusBarViewFactory,
                 lightsOutNotifController,
                 statusBarNotificationActivityStarterBuilder,
+                shadeController,
                 statusBarKeyguardViewManager,
                 viewMediatorCallback,
                 dismissCallbackRegistry,
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 80240af..ade292f 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -40,6 +40,8 @@
     <string name="wifi_security_short_sae" translatable="false">WPA3</string>
     <!-- Do not translate.  Concise terminology for wifi with WPA2/WPA3 transition security -->
     <string name="wifi_security_short_psk_sae" translatable="false">WPA2/WPA3</string>
+    <!-- Do not translate.  Concise terminology for Wi-Fi with None/OWE transition mode security -->
+    <string name="wifi_security_short_none_owe" translatable="false">None/OWE</string>
     <!-- Do not translate.  Concise terminology for wifi with OWE security -->
     <string name="wifi_security_short_owe" translatable="false">OWE</string>
     <!-- Do not translate.  Concise terminology for wifi with 802.1x EAP Suite-B-192 security -->
@@ -70,6 +72,8 @@
     <string name="wifi_security_sae" translatable="false">WPA3-Personal</string>
     <!-- Do not translate.  Terminology for wifi with WPA2/WPA3 Transition mode security -->
     <string name="wifi_security_psk_sae" translatable="false">WPA2/WPA3-Personal</string>
+    <!-- Do not translate.  Terminology for Wi-Fi with None/OWE transition mode security -->
+    <string name="wifi_security_none_owe" translatable="false">None/Enhanced Open</string>
     <!-- Do not translate.  Terminology for wifi with OWE security -->
     <string name="wifi_security_owe" translatable="false">Enhanced Open</string>
     <!-- Do not translate.  Concise terminology for wifi with 802.1x EAP Suite-B-192 security -->
@@ -957,7 +961,7 @@
     <!-- Title for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
     <string name="accessibility_display_daltonizer_preference_title">Color correction</string>
     <!-- Subtitle for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_display_daltonizer_preference_subtitle">This feature is experimental and may affect performance.</string>
+    <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps people with color blindness to see more accurate colors</string>
     <!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
     <string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 96aee51..a2bd210 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -155,8 +155,8 @@
     public boolean disconnect(BluetoothDevice device) {
         if (mService == null) return false;
         // Downgrade priority as user is disconnecting the headset.
-        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
-            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
         }
         return mService.disconnect(device);
     }
@@ -179,23 +179,29 @@
     }
 
     public boolean isPreferred(BluetoothDevice device) {
-        if (mService == null) return false;
-        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+        if (mService == null) {
+            return false;
+        }
+        return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
     }
 
     public int getPreferred(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
-        return mService.getPriority(device);
+        if (mService == null) {
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+        }
+        return mService.getConnectionPolicy(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
-        if (mService == null) return;
+        if (mService == null) {
+            return;
+        }
         if (preferred) {
-            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
         }
     }
     boolean isA2dpPlaying() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 55765dd..9c896c8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -124,8 +124,8 @@
             return false;
         }
         // Downgrade priority as user is disconnecting the headset.
-        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
         }
         return mService.disconnect(device);
     }
@@ -141,14 +141,14 @@
         if (mService == null) {
             return false;
         }
-        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+        return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
     }
 
     public int getPreferred(BluetoothDevice device) {
         if (mService == null) {
-            return BluetoothProfile.PRIORITY_OFF;
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
         }
-        return mService.getPriority(device);
+        return mService.getConnectionPolicy(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -156,11 +156,11 @@
             return;
         }
         if (preferred) {
-            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 0666596..747ceb1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -721,12 +721,8 @@
 
         refresh();
 
-        if (bondState == BluetoothDevice.BOND_BONDED) {
-            if (mDevice.isBluetoothDock()) {
-                onBondingDockConnect();
-            } else if (mDevice.isBondingInitiatedLocally()) {
-                connect(false);
-            }
+        if (bondState == BluetoothDevice.BOND_BONDED && mDevice.isBondingInitiatedLocally()) {
+            connect(false);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 9f7b718..560cb3b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -120,8 +120,8 @@
             return false;
         }
         // Downgrade priority as user is disconnecting the headset.
-        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
         }
         return mService.disconnect(device);
     }
@@ -165,14 +165,14 @@
         if (mService == null) {
             return false;
         }
-        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+        return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
     }
 
     public int getPreferred(BluetoothDevice device) {
         if (mService == null) {
-            return BluetoothProfile.PRIORITY_OFF;
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
         }
-        return mService.getPriority(device);
+        return mService.getConnectionPolicy(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -180,11 +180,11 @@
             return;
         }
         if (preferred) {
-            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index ebaeb74..58655a2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -153,8 +153,8 @@
     public boolean disconnect(BluetoothDevice device) {
         if (mService == null) return false;
         // Downgrade priority as user is disconnecting the hearing aid.
-        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
-            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
         }
         return mService.disconnect(device);
     }
@@ -177,23 +177,29 @@
     }
 
     public boolean isPreferred(BluetoothDevice device) {
-        if (mService == null) return false;
-        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+        if (mService == null) {
+            return false;
+        }
+        return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
     }
 
     public int getPreferred(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
-        return mService.getPriority(device);
+        if (mService == null) {
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+        }
+        return mService.getConnectionPolicy(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
-        if (mService == null) return;
+        if (mService == null) {
+            return;
+        }
         if (preferred) {
-            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 860b77d..a372e23 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -135,8 +135,8 @@
             return false;
         }
         // Downgrade priority as user is disconnecting the headset.
-        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
-            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
         }
         return mService.disconnect(device);
     }
@@ -154,15 +154,15 @@
         if (mService == null) {
             return false;
         }
-        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+        return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
     }
 
     @Override
     public int getPreferred(BluetoothDevice device) {
         if (mService == null) {
-            return BluetoothProfile.PRIORITY_OFF;
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
         }
-        return mService.getPriority(device);
+        return mService.getConnectionPolicy(device);
     }
 
     @Override
@@ -171,11 +171,11 @@
             return;
         }
         if (preferred) {
-            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 6d874ab..975a1e6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -116,23 +116,27 @@
     }
 
     public boolean isPreferred(BluetoothDevice device) {
-        if (mService == null) return false;
-        return mService.getPriority(device) != BluetoothProfile.PRIORITY_OFF;
+        if (mService == null) {
+            return false;
+        }
+        return mService.getConnectionPolicy(device) != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
     }
 
     public int getPreferred(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
-        return mService.getPriority(device);
+        if (mService == null) {
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+        }
+        return mService.getConnectionPolicy(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
         if (mService == null) return;
         if (preferred) {
-            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index d4dda32..95139a1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -123,8 +123,8 @@
             return false;
         }
         // Downgrade priority as user is disconnecting.
-        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
         }
         return mService.disconnect(device);
     }
@@ -140,14 +140,14 @@
         if (mService == null) {
             return false;
         }
-        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+        return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
     }
 
     public int getPreferred(BluetoothDevice device) {
         if (mService == null) {
-            return BluetoothProfile.PRIORITY_OFF;
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
         }
-        return mService.getPriority(device);
+        return mService.getConnectionPolicy(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -155,11 +155,11 @@
             return;
         }
         if (preferred) {
-            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index b2a9a6a..31a0eea 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -119,8 +119,8 @@
         if (mService == null) {
             return false;
         }
-        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
         }
         return mService.disconnect(device);
     }
@@ -136,14 +136,14 @@
         if (mService == null) {
             return false;
         }
-        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+        return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
     }
 
     public int getPreferred(BluetoothDevice device) {
         if (mService == null) {
-            return BluetoothProfile.PRIORITY_OFF;
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
         }
-        return mService.getPriority(device);
+        return mService.getConnectionPolicy(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -151,11 +151,11 @@
             return;
         }
         if (preferred) {
-            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
index 7162121..387bae1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
@@ -1,8 +1,7 @@
 # Default reviewers for this and subdirectories.
-asapperstein@google.com
-asargent@google.com
-eisenbach@google.com
-jackqdyulei@google.com
 siyuanh@google.com
+hughchen@google.com
+timhypeng@google.com
+robertluo@google.com
 
-# Emergency approvers in case the above are not available
\ No newline at end of file
+# Emergency approvers in case the above are not available
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
index e1e5dbe..8e3f3ed 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
@@ -57,7 +57,7 @@
     }
 
     public int getPreferred(BluetoothDevice device) {
-        return BluetoothProfile.PRIORITY_OFF; // Settings app doesn't handle OPP
+        return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; // Settings app doesn't handle OPP
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index a2da4fb..4ea0df6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -151,14 +151,14 @@
         if (mService == null) {
             return false;
         }
-        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+        return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
     }
 
     public int getPreferred(BluetoothDevice device) {
         if (mService == null) {
-            return BluetoothProfile.PRIORITY_OFF;
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
         }
-        return mService.getPriority(device);
+        return mService.getConnectionPolicy(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -166,11 +166,11 @@
             return;
         }
         if (preferred) {
-            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 9b733f2..0ca4d61 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -119,8 +119,8 @@
         if (mService == null) {
             return false;
         }
-        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
         }
         return mService.disconnect(device);
     }
@@ -136,14 +136,14 @@
         if (mService == null) {
             return false;
         }
-        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+        return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
     }
 
     public int getPreferred(BluetoothDevice device) {
         if (mService == null) {
-            return BluetoothProfile.PRIORITY_OFF;
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
         }
-        return mService.getPriority(device);
+        return mService.getConnectionPolicy(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
@@ -151,11 +151,11 @@
             return;
         }
         if (preferred) {
-            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
             }
         } else {
-            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+            mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 328bfb2..49e214b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1004,6 +1004,10 @@
             return concise ? context.getString(R.string.wifi_security_short_psk_sae) :
                     context.getString(R.string.wifi_security_psk_sae);
         }
+        if (mIsOweTransitionMode) {
+            return concise ? context.getString(R.string.wifi_security_short_none_owe) :
+                    context.getString(R.string.wifi_security_none_owe);
+        }
 
         switch(security) {
             case SECURITY_EAP:
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 325366e..2a70506 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -1683,6 +1683,18 @@
         assertThat(pskSaeTransitionModeAp.matches(saeScanResult)).isFalse();
     }
 
+    @Test
+    public void testGetSecurityString_oweTransitionMode_shouldReturnCorrectly() {
+        when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+        when(mMockWifiManager.isEnhancedOpenSupported()).thenReturn(true);
+        AccessPoint oweTransitionModeAp = getOweTransitionModeAp();
+
+        assertThat(oweTransitionModeAp.getSecurityString(true /* concise */))
+                .isEqualTo(mContext.getString(R.string.wifi_security_short_none_owe));
+        assertThat(oweTransitionModeAp.getSecurityString(false /* concise */))
+                .isEqualTo(mContext.getString(R.string.wifi_security_none_owe));
+    }
+
     private AccessPoint getPskSaeTransitionModeAp() {
         ScanResult scanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID),
                 TEST_BSSID, DEFAULT_RSSI);
@@ -1692,4 +1704,13 @@
                 .setScanResults(new ArrayList<ScanResult>(Arrays.asList(scanResult)))
                 .build();
     }
+
+    private AccessPoint getOweTransitionModeAp() {
+        ScanResult scanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID),
+                TEST_BSSID, DEFAULT_RSSI);
+        scanResult.capabilities = "[OWE_TRANSITION]";
+        return new TestAccessPointBuilder(mContext)
+                .setScanResults(new ArrayList<ScanResult>(Arrays.asList(scanResult)))
+                .build();
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 4731e68..086b20f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -62,6 +62,8 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -766,6 +768,23 @@
                 }
             } catch (Throwable t) {
                 Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
+                if (t instanceof IOException) {
+                    // we failed to create a directory, so log the permissions and existence
+                    // state for the settings file and directory
+                    logSettingsDirectoryInformation(destination.getBaseFile());
+                    if (t.getMessage().contains("Couldn't create directory")) {
+                        // attempt to create the directory with Files.createDirectories, which
+                        // throws more informative errors than File.mkdirs.
+                        Path parentPath = destination.getBaseFile().getParentFile().toPath();
+                        try {
+                            Files.createDirectories(parentPath);
+                            Slog.i(LOG_TAG, "Successfully created " + parentPath);
+                        } catch (Throwable t2) {
+                            Slog.e(LOG_TAG, "Failed to write " + parentPath
+                                    + " with Files.writeDirectories", t2);
+                        }
+                    }
+                }
                 destination.failWrite(out);
             } finally {
                 IoUtils.closeQuietly(out);
@@ -779,6 +798,33 @@
         }
     }
 
+    private static void logSettingsDirectoryInformation(File settingsFile) {
+        File parent = settingsFile.getParentFile();
+        Slog.i(LOG_TAG, "directory info for directory/file " + settingsFile
+                + " with stacktrace ", new Exception());
+        File ancestorDir = parent;
+        while (ancestorDir != null) {
+            if (!ancestorDir.exists()) {
+                Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
+                        + " does not exist");
+                ancestorDir = ancestorDir.getParentFile();
+            } else {
+                Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
+                        + " exists");
+                Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
+                        + " permissions: r: " + ancestorDir.canRead() + " w: "
+                        + ancestorDir.canWrite() + " x: " + ancestorDir.canExecute());
+                File ancestorParent = ancestorDir.getParentFile();
+                if (ancestorParent != null) {
+                    Slog.i(LOG_TAG, "ancestor's parent directory " + ancestorParent
+                            + " permissions: r: " + ancestorParent.canRead() + " w: "
+                            + ancestorParent.canWrite() + " x: " + ancestorParent.canExecute());
+                }
+                break;
+            }
+        }
+    }
+
     static void writeSingleSetting(int version, XmlSerializer serializer, String id,
             String name, String value, String defaultValue, String packageName,
             String tag, boolean defaultSysSet) throws IOException {
@@ -853,6 +899,7 @@
             in = new AtomicFile(mStatePersistFile).openRead();
         } catch (FileNotFoundException fnfe) {
             Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
+            logSettingsDirectoryInformation(mStatePersistFile);
             addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
             return;
         }
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 2a5bdc7..404e791 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -127,7 +127,7 @@
         "SystemUI-proto",
         "metrics-helper-lib",
         "androidx.test.rules", "hamcrest-library",
-        "mockito-target-inline-minus-junit4",
+        "mockito-target-extended-minus-junit4",
         "testables",
         "truth-prebuilt",
         "dagger2-2.19",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6f68038..3514704 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -325,6 +325,14 @@
                 android:permission="android.permission.BIND_WALLPAPER"
                 android:exported="true" />
 
+        <activity
+            android:name=".bubbles.BubbleOverflowActivity"
+            android:theme="@style/BubbleOverflow"
+            android:excludeFromRecents="true"
+            android:documentLaunchMode="always"
+            android:resizeableActivity="true">
+        </activity>
+
         <activity android:name=".tuner.TunerActivity"
                   android:enabled="false"
                   android:icon="@drawable/tuner"
diff --git a/packages/SystemUI/plugin_core/Android.bp b/packages/SystemUI/plugin_core/Android.bp
index 42d6762..581fef7 100644
--- a/packages/SystemUI/plugin_core/Android.bp
+++ b/packages/SystemUI/plugin_core/Android.bp
@@ -16,4 +16,8 @@
     sdk_version: "current",
     name: "PluginCoreLib",
     srcs: ["src/**/*.java"],
+
+    // Enforce that the library is built against java 8 so that there are
+    // no compatibility issues with launcher
+    java_version: "1.8",
 }
diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
new file mode 100644
index 0000000..4cee746
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ 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
+  -->
+<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/bubble_overflow_recycler"
+    android:scrollbars="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 6becd21..79629e4 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -154,12 +154,5 @@
     <declare-styleable name="CaptionsToggleImageButton">
         <attr name="optedOut" format="boolean" />
     </declare-styleable>
-
-    <!-- Theme attributes used to style the appearance of expanded Bubbles -->
-    <declare-styleable name="BubbleExpandedView">
-        <attr name="android:colorBackgroundFloating" />
-        <attr name="android:dialogCornerRadius" />
-    </declare-styleable>
-
 </resources>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 926d016..3ff8243 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -15,6 +15,7 @@
 -->
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="BubbleOverflow" parent="@android:style/Theme.NoTitleBar"></style>
 
     <style name="ClearAllButtonDefaultMargins">
         <item name="android:layout_marginStart">0dp</item>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 2afcb12..d68fe15 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -42,6 +42,7 @@
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -288,6 +289,7 @@
     public void onTuningChanged(String key, String newValue) {
         if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
             ArraySet<String> icons = StatusBarIconController.getIconBlacklist(newValue);
+            setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index 72a4030..4749add 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -280,6 +280,7 @@
                     R.layout.size_compat_mode_hint, null /* root */);
             PopupWindow popupWindow = new PopupWindow(popupView,
                     LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+            popupWindow.setWindowLayoutType(mWinParams.type);
             popupWindow.setElevation(getResources().getDimension(R.dimen.bubble_elevation));
             popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
             popupWindow.setClippingEnabled(false);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1938194..db5ff3f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -109,8 +109,6 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import dagger.Lazy;
-
 /**
  * Bubbles are a special type of content that can "float" on top of other apps or System UI.
  * Bubbles can be expanded to show more content.
@@ -147,7 +145,7 @@
     private BubbleExpandListener mExpandListener;
     @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
     private final NotificationGroupManager mNotificationGroupManager;
-    private final Lazy<ShadeController> mShadeController;
+    private final ShadeController mShadeController;
     private final RemoteInputUriController mRemoteInputUriController;
     private Handler mHandler = new Handler() {};
 
@@ -162,6 +160,10 @@
     // Saves notification keys of user created "fake" bubbles so that we can allow notifications
     // like these to bubble by default. Doesn't persist across reboots, not a long-term solution.
     private final HashSet<String> mUserCreatedBubbles;
+    // If we're auto-bubbling bubbles via a whitelist, we need to track which notifs from that app
+    // have been "demoted" back to a notification so that we don't auto-bubbles those again.
+    // Doesn't persist across reboots, not a long-term solution.
+    private final HashSet<String> mUserBlockedBubbles;
 
     // Bubbles get added to the status bar view
     private final StatusBarWindowController mStatusBarWindowController;
@@ -243,7 +245,7 @@
     public BubbleController(Context context,
             StatusBarWindowController statusBarWindowController,
             StatusBarStateController statusBarStateController,
-            Lazy<ShadeController> shadeController,
+            ShadeController shadeController,
             BubbleData data,
             ConfigurationController configurationController,
             NotificationInterruptionStateProvider interruptionStateProvider,
@@ -261,7 +263,7 @@
     public BubbleController(Context context,
             StatusBarWindowController statusBarWindowController,
             StatusBarStateController statusBarStateController,
-            Lazy<ShadeController> shadeController,
+            ShadeController shadeController,
             BubbleData data,
             @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
             ConfigurationController configurationController,
@@ -272,6 +274,7 @@
             NotificationEntryManager entryManager,
             RemoteInputUriController remoteInputUriController) {
         mContext = context;
+        mShadeController = shadeController;
         mNotificationInterruptionStateProvider = interruptionStateProvider;
         mNotifUserManager = notifUserManager;
         mZenModeController = zenModeController;
@@ -319,7 +322,6 @@
                     }
                 });
 
-        mShadeController = shadeController;
         mStatusBarWindowController = statusBarWindowController;
         mStatusBarStateListener = new StatusBarStateListener();
         statusBarStateController.addCallback(mStatusBarStateListener);
@@ -348,6 +350,7 @@
                 });
 
         mUserCreatedBubbles = new HashSet<>();
+        mUserBlockedBubbles = new HashSet<>();
 
         mScreenshotHelper = new ScreenshotHelper(context);
     }
@@ -579,10 +582,11 @@
         if (DEBUG_EXPERIMENTS || DEBUG_BUBBLE_CONTROLLER) {
             Log.d(TAG, "onUserCreatedBubble: " + entry.getKey());
         }
-        mShadeController.get().collapsePanel(true);
+        mShadeController.collapsePanel(true);
         entry.setFlagBubble(true);
         updateBubble(entry, true /* suppressFlyout */, false /* showInShade */);
         mUserCreatedBubbles.add(entry.getKey());
+        mUserBlockedBubbles.remove(entry.getKey());
     }
 
     /**
@@ -598,6 +602,12 @@
         entry.setFlagBubble(false);
         removeBubble(entry.getKey(), DISMISS_BLOCKED);
         mUserCreatedBubbles.remove(entry.getKey());
+        if (BubbleExperimentConfig.isPackageWhitelistedToAutoBubble(
+                mContext, entry.getSbn().getPackageName())) {
+            // This package is whitelist but user demoted the bubble, let's save it so we don't
+            // auto-bubble for the whitelist again.
+            mUserBlockedBubbles.add(entry.getKey());
+        }
     }
 
     /**
@@ -727,8 +737,9 @@
         @Override
         public void onPendingEntryAdded(NotificationEntry entry) {
             boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
+            boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
             boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
-                    mContext, entry, previouslyUserCreated);
+                    mContext, entry, previouslyUserCreated, userBlocked);
 
             if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
                     && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
@@ -743,8 +754,9 @@
         @Override
         public void onPreEntryUpdated(NotificationEntry entry) {
             boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
+            boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
             boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
-                    mContext, entry, previouslyUserCreated);
+                    mContext, entry, previouslyUserCreated, userBlocked);
 
             boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
                     && (canLaunchInActivityView(mContext, entry) || wasAdjusted);
@@ -878,12 +890,12 @@
 
             if (DEBUG_BUBBLE_CONTROLLER) {
                 Log.d(TAG, "[BubbleData]");
-                Log.d(TAG, formatBubblesString(mBubbleData.getBubbles(),
+                Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getBubbles(),
                         mBubbleData.getSelectedBubble()));
 
                 if (mStackView != null) {
                     Log.d(TAG, "[BubbleStackView]");
-                    Log.d(TAG, formatBubblesString(mStackView.getBubblesOnScreen(),
+                    Log.d(TAG, BubbleDebugConfig.formatBubblesString(mStackView.getBubblesOnScreen(),
                             mStackView.getExpandedBubble()));
                 }
             }
@@ -972,23 +984,6 @@
         pw.println();
     }
 
-    static String formatBubblesString(List<Bubble> bubbles, Bubble selected) {
-        StringBuilder sb = new StringBuilder();
-        for (Bubble bubble : bubbles) {
-            if (bubble == null) {
-                sb.append("   <null> !!!!!\n");
-            } else {
-                boolean isSelected = (bubble == selected);
-                sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n",
-                        ((isSelected) ? "->" : "  "),
-                        bubble.getLastActivity(),
-                        (bubble.isOngoing() ? 1 : 0),
-                        bubble.getKey()));
-            }
-        }
-        return sb.toString();
-    }
-
     /**
      * This task stack listener is responsible for responding to tasks moved to the front
      * which are on the default (main) display. When this happens, expanded bubbles must be
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 034bff3..b7df5ba 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -48,6 +48,7 @@
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
+import com.android.systemui.R;
 
 /**
  * Keeps track of active bubbles.
@@ -57,8 +58,6 @@
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleData" : TAG_BUBBLES;
 
-    private static final int MAX_BUBBLES = 5;
-
     private static final Comparator<Bubble> BUBBLES_BY_SORT_KEY_DESCENDING =
             Comparator.comparing(BubbleData::sortKey).reversed();
 
@@ -115,6 +114,7 @@
     private final List<Bubble> mBubbles;
     private Bubble mSelectedBubble;
     private boolean mExpanded;
+    private final int mMaxBubbles;
 
     // State tracked during an operation -- keeps track of what listener events to dispatch.
     private Update mStateChange;
@@ -144,6 +144,7 @@
         mContext = context;
         mBubbles = new ArrayList<>();
         mStateChange = new Update(mBubbles);
+        mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
     }
 
     public boolean hasBubbles() {
@@ -329,7 +330,7 @@
     }
 
     private void trim() {
-        if (mBubbles.size() > MAX_BUBBLES) {
+        if (mBubbles.size() > mMaxBubbles) {
             mBubbles.stream()
                     // sort oldest first (ascending lastActivity)
                     .sorted(Comparator.comparingLong(Bubble::getLastActivity))
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
index a912ecc..3190662 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.bubbles;
 
+import java.util.List;
+
 /**
  * Common class for the various debug {@link android.util.Log} output configuration in the Bubbles
  * package.
@@ -38,5 +40,23 @@
     static final boolean DEBUG_BUBBLE_STACK_VIEW = false;
     static final boolean DEBUG_BUBBLE_EXPANDED_VIEW = false;
     static final boolean DEBUG_EXPERIMENTS = true;
+    static final boolean DEBUG_OVERFLOW = false;
 
+    static String formatBubblesString(List<Bubble> bubbles, Bubble selected) {
+        StringBuilder sb = new StringBuilder();
+        for (Bubble bubble : bubbles) {
+            if (bubble == null) {
+                sb.append("   <null> !!!!!\n");
+            } else {
+                boolean isSelected = (selected != null && bubble == selected);
+                String arrow = isSelected ? "=>" : "  ";
+                sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n",
+                        arrow,
+                        bubble.getLastActivity(),
+                        (bubble.isOngoing() ? 1 : 0),
+                        bubble.getKey()));
+            }
+        }
+        return sb.toString();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index efc955d..63d036d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -275,17 +275,15 @@
     }
 
     void applyThemeAttrs() {
-        TypedArray ta = getContext().obtainStyledAttributes(R.styleable.BubbleExpandedView);
-        int bgColor = ta.getColor(
-                R.styleable.BubbleExpandedView_android_colorBackgroundFloating, Color.WHITE);
-        float cornerRadius = ta.getDimension(
-                R.styleable.BubbleExpandedView_android_dialogCornerRadius, 0);
+        final TypedArray ta = mContext.obtainStyledAttributes(
+                new int[] {
+                        android.R.attr.colorBackgroundFloating,
+                        android.R.attr.dialogCornerRadius});
+        int bgColor = ta.getColor(0, Color.WHITE);
+        float cornerRadius = ta.getDimensionPixelSize(1, 0);
         ta.recycle();
 
-        // Update triangle color.
         mPointerDrawable.setTint(bgColor);
-
-        // Update ActivityView cornerRadius
         if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
             mActivityView.setCornerRadius(cornerRadius);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 8299f22..21471ec 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -143,7 +143,7 @@
      * @return whether an adjustment was made.
      */
     static boolean adjustForExperiments(Context context, NotificationEntry entry,
-            boolean previouslyUserCreated) {
+            boolean previouslyUserCreated, boolean userBlocked) {
         Notification.BubbleMetadata metadata = null;
         boolean addedMetadata = false;
         boolean whiteListedToAutoBubble =
@@ -205,7 +205,9 @@
             }
         }
 
-        boolean bubbleForWhitelist = whiteListedToAutoBubble && (addedMetadata || hasMetadata);
+        boolean bubbleForWhitelist = !userBlocked
+                && whiteListedToAutoBubble
+                && (addedMetadata || hasMetadata);
         if ((previouslyUserCreated && addedMetadata) || bubbleForWhitelist) {
             // Update to a previous bubble (or new autobubble), set its flag now.
             if (DEBUG_EXPERIMENTS) {
@@ -224,7 +226,11 @@
         // Use the icon of the person if available
         List<Person> personList = getPeopleFromNotification(entry);
         if (personList.size() > 0) {
-            icon = personList.get(0).getIcon();
+            final Person person = personList.get(0);
+
+            if (person != null) {
+                icon = person.getIcon();
+            }
         }
         if (icon == null) {
             icon = notification.getLargeIcon() != null
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
new file mode 100644
index 0000000..018b631
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -0,0 +1,69 @@
+package com.android.systemui.bubbles;
+
+import android.app.Activity;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.os.Bundle;
+
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.systemui.R;
+
+/**
+ * Activity for showing aged out bubbles.
+ * Must be public to be accessible to androidx...AppComponentFactory
+ */
+public class BubbleOverflowActivity extends Activity {
+    private RecyclerView mRecyclerView;
+    private int mMaxBubbles;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.bubble_overflow_activity);
+        setBackgroundColor();
+
+        mMaxBubbles = getResources().getInteger(R.integer.bubbles_max_rendered);
+        mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
+        mRecyclerView.setLayoutManager(
+                new GridLayoutManager(getApplicationContext(), /* numberOfColumns */ mMaxBubbles));
+    }
+
+    void setBackgroundColor() {
+        final TypedArray ta = getApplicationContext().obtainStyledAttributes(
+                new int[] {android.R.attr.colorBackgroundFloating});
+        int bgColor = ta.getColor(0, Color.WHITE);
+        ta.recycle();
+        findViewById(android.R.id.content).setBackgroundColor(bgColor);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    public void onRestart() {
+        super.onRestart();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+    }
+
+    public void onDestroy() {
+        super.onStop();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 4593164..df79310 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -22,8 +22,6 @@
 import com.android.systemui.keyguard.WorkLockActivity;
 import com.android.systemui.settings.BrightnessDialog;
 import com.android.systemui.tuner.TunerActivity;
-import com.android.systemui.usb.UsbDebuggingActivity;
-import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity;
 
 import dagger.Binds;
 import dagger.Module;
@@ -58,17 +56,4 @@
     @IntoMap
     @ClassKey(BrightnessDialog.class)
     public abstract Activity bindBrightnessDialog(BrightnessDialog activity);
-
-    /** Inject into UsbDebuggingActivity. */
-    @Binds
-    @IntoMap
-    @ClassKey(UsbDebuggingActivity.class)
-    public abstract Activity bindUsbDebuggingActivity(UsbDebuggingActivity activity);
-
-    /** Inject into UsbDebuggingSecondaryUserActivity. */
-    @Binds
-    @IntoMap
-    @ClassKey(UsbDebuggingSecondaryUserActivity.class)
-    public abstract Activity bindUsbDebuggingSecondaryUserActivity(
-            UsbDebuggingSecondaryUserActivity activity);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 606605a..6ea3f4e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -20,6 +20,7 @@
 
 import android.app.INotificationManager;
 import android.content.Context;
+import android.content.pm.IPackageManager;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.display.NightDisplayListener;
 import android.os.Handler;
@@ -150,6 +151,13 @@
     /** */
     @Singleton
     @Provides
+    public IPackageManager provideIPackageManager() {
+        return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+    }
+
+    /** */
+    @Singleton
+    @Provides
     public LayoutInflater providerLayoutInflater(Context context) {
         return LayoutInflater.from(context);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index f44eae7..ccb6c2f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -39,7 +39,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.ShadeControllerImpl;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.Optional;
@@ -82,7 +82,7 @@
             KeyguardEnvironmentImpl keyguardEnvironment);
 
     @Binds
-    abstract ShadeController provideShadeController(StatusBar statusBar);
+    abstract ShadeController provideShadeController(ShadeControllerImpl shadeController);
 
     @Singleton
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java b/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java
index ea1def0..d2fe394 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java
@@ -28,10 +28,12 @@
  * and triaging purposes.
  */
 public class DozeEvent extends RichEvent {
-    public static final int TOTAL_EVENT_TYPES = 19;
-
-    public DozeEvent(int logLevel, int type, String reason) {
-        super(logLevel, type, reason);
+    /**
+     * Initializes a doze event
+     */
+    public DozeEvent init(@EventType int type, String reason) {
+        super.init(DEBUG, type, reason);
+        return this;
     }
 
     /**
@@ -89,21 +91,6 @@
         }
     }
 
-    /**
-     * Builds a DozeEvent.
-     */
-    public static class DozeEventBuilder extends RichEvent.Builder<DozeEventBuilder> {
-        @Override
-        public DozeEventBuilder getBuilder() {
-            return this;
-        }
-
-        @Override
-        public RichEvent build() {
-            return new DozeEvent(mLogLevel, mType, mReason);
-        }
-    }
-
     @IntDef({PICKUP_WAKEUP, PULSE_START, PULSE_FINISH, NOTIFICATION_PULSE, DOZING, FLING,
             EMERGENCY_CALL, KEYGUARD_BOUNCER_CHANGED, SCREEN_ON, SCREEN_OFF, MISSED_TICK,
             TIME_TICK_SCHEDULED, KEYGUARD_VISIBILITY_CHANGE, DOZE_STATE_CHANGED, WAKE_DISPLAY,
@@ -132,6 +119,7 @@
     public static final int PULSE_DROPPED = 16;
     public static final int PULSE_DISABLED_BY_PROX = 17;
     public static final int SENSOR_TRIGGERED = 18;
+    public static final int TOTAL_EVENT_TYPES = 19;
 
     public static final int TOTAL_REASONS = 10;
     @IntDef({PULSE_REASON_NONE, PULSE_REASON_INTENT, PULSE_REASON_NOTIFICATION,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 2e4466d..fe50421 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,9 +35,11 @@
  *      dependency DumpController DozeLog
  */
 @Singleton
-public class DozeLog extends SysuiLog {
+public class DozeLog extends SysuiLog<DozeEvent> {
     private static final String TAG = "DozeLog";
 
+    private DozeEvent mRecycledEvent;
+
     private boolean mPulsing;
     private long mSince;
     private SummaryStats mPickupPulseNearVibrationStats;
@@ -73,8 +75,8 @@
      * Appends pickup wakeup event to the logs
      */
     public void tracePickupWakeUp(boolean withinVibrationThreshold) {
-        if (log(DozeEvent.PICKUP_WAKEUP,
-                "withinVibrationThreshold=" + withinVibrationThreshold)) {
+        log(DozeEvent.PICKUP_WAKEUP, "withinVibrationThreshold=" + withinVibrationThreshold);
+        if (mEnabled) {
             (withinVibrationThreshold ? mPickupPulseNearVibrationStats
                     : mPickupPulseNotNearVibrationStats).append();
         }
@@ -85,27 +87,24 @@
      * @param reason why the pulse started
      */
     public void tracePulseStart(@DozeEvent.Reason int reason) {
-        if (log(DozeEvent.PULSE_START, DozeEvent.reasonToString(reason))) {
-            mPulsing = true;
-        }
+        log(DozeEvent.PULSE_START, DozeEvent.reasonToString(reason));
+        if (mEnabled) mPulsing = true;
     }
 
     /**
      * Appends pulse finished event to the logs
      */
     public void tracePulseFinish() {
-        if (log(DozeEvent.PULSE_FINISH)) {
-            mPulsing = false;
-        }
+        log(DozeEvent.PULSE_FINISH);
+        if (mEnabled) mPulsing = false;
     }
 
     /**
      * Appends pulse event to the logs
      */
     public void traceNotificationPulse() {
-        if (log(DozeEvent.NOTIFICATION_PULSE)) {
-            mNotificationPulseStats.append();
-        }
+        log(DozeEvent.NOTIFICATION_PULSE);
+        if (mEnabled) mNotificationPulseStats.append();
     }
 
     /**
@@ -113,9 +112,8 @@
      * @param dozing true if dozing, else false
      */
     public void traceDozing(boolean dozing) {
-        if (log(DozeEvent.DOZING, "dozing=" + dozing)) {
-            mPulsing = false;
-        }
+        log(DozeEvent.DOZING, "dozing=" + dozing);
+        if (mEnabled) mPulsing = false;
     }
 
     /**
@@ -133,9 +131,8 @@
      * Appends emergency call event to the logs
      */
     public void traceEmergencyCall() {
-        if (log(DozeEvent.EMERGENCY_CALL)) {
-            mEmergencyCallStats.append();
-        }
+        log(DozeEvent.EMERGENCY_CALL);
+        if (mEnabled) mEmergencyCallStats.append();
     }
 
     /**
@@ -150,7 +147,8 @@
      * Appends screen-on event to the logs
      */
     public void traceScreenOn() {
-        if (log(DozeEvent.SCREEN_ON, "pulsing=" + mPulsing)) {
+        log(DozeEvent.SCREEN_ON, "pulsing=" + mPulsing);
+        if (mEnabled) {
             (mPulsing ? mScreenOnPulsingStats : mScreenOnNotPulsingStats).append();
             mPulsing = false;
         }
@@ -188,10 +186,8 @@
      * @param showing whether the keyguard is now showing
      */
     public void traceKeyguard(boolean showing) {
-        if (log(DozeEvent.KEYGUARD_VISIBILITY_CHANGE, "showing=" + showing)
-                && !showing) {
-            mPulsing = false;
-        }
+        log(DozeEvent.KEYGUARD_VISIBILITY_CHANGE, "showing=" + showing);
+        if (mEnabled && !showing) mPulsing = false;
     }
 
     /**
@@ -217,12 +213,11 @@
      * @param reason why proximity result was triggered
      */
     public void traceProximityResult(boolean near, long millis, @DozeEvent.Reason int reason) {
-        if (log(DozeEvent.PROXIMITY_RESULT,
+        log(DozeEvent.PROXIMITY_RESULT,
                 " reason=" + DozeEvent.reasonToString(reason)
-                + " near=" + near
-                + " millis=" + millis)) {
-            mProxStats[reason][near ? 0 : 1].append();
-        }
+                        + " near=" + near
+                        + " millis=" + millis);
+        if (mEnabled) mProxStats[reason][near ? 0 : 1].append();
     }
 
     /**
@@ -250,15 +245,16 @@
         }
     }
 
-    private boolean log(@DozeEvent.EventType int eventType) {
-        return log(eventType, "");
+    private void log(@DozeEvent.EventType int eventType) {
+        log(eventType, "");
     }
 
-    private boolean log(@DozeEvent.EventType int eventType, String msg) {
-        return super.log(new DozeEvent.DozeEventBuilder()
-                .setType(eventType)
-                .setReason(msg)
-                .build());
+    private void log(@DozeEvent.EventType int eventType, String msg) {
+        if (mRecycledEvent != null) {
+            mRecycledEvent = log(mRecycledEvent.init(eventType, msg));
+        } else {
+            mRecycledEvent = log(new DozeEvent().init(eventType, msg));
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8d08b28..beba203 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -210,8 +210,7 @@
     private AlarmManager mAlarmManager;
     private AudioManager mAudioManager;
     private StatusBarManager mStatusBarManager;
-    private final StatusBarWindowController mStatusBarWindowController =
-            Dependency.get(StatusBarWindowController.class);
+    private final StatusBarWindowController mStatusBarWindowController;
     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
 
     private boolean mSystemReady;
@@ -688,12 +687,14 @@
             FalsingManager falsingManager,
             LockPatternUtils lockPatternUtils,
             BroadcastDispatcher broadcastDispatcher,
+            StatusBarWindowController statusBarWindowController,
             Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManagerLazy,
             DismissCallbackRegistry dismissCallbackRegistry) {
         super(context);
         mFalsingManager = falsingManager;
         mLockPatternUtils = lockPatternUtils;
         mBroadcastDispatcher = broadcastDispatcher;
+        mStatusBarWindowController = statusBarWindowController;
         mStatusBarKeyguardViewManagerLazy = statusBarKeyguardViewManagerLazy;
         mDismissCallbackRegistry = dismissCallbackRegistry;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/log/Event.java b/packages/SystemUI/src/com/android/systemui/log/Event.java
index 92862a2..7bc1abf 100644
--- a/packages/SystemUI/src/com/android/systemui/log/Event.java
+++ b/packages/SystemUI/src/com/android/systemui/log/Event.java
@@ -37,20 +37,28 @@
     public static final int INFO = 4;
     public static final int WARN = 5;
     public static final int ERROR = 6;
+    public static final @Level int DEFAULT_LOG_LEVEL = DEBUG;
 
     private long mTimestamp;
-    private @Level int mLogLevel = DEBUG;
-    protected String mMessage;
+    private @Level int mLogLevel = DEFAULT_LOG_LEVEL;
+    private String mMessage = "";
 
-    public Event(String message) {
-        mTimestamp = System.currentTimeMillis();
-        mMessage = message;
+    /**
+     * initialize an event with a message
+     */
+    public Event init(String message) {
+        init(DEFAULT_LOG_LEVEL, message);
+        return this;
     }
 
-    public Event(@Level int logLevel, String message) {
+    /**
+     * initialize an event with a logLevel and message
+     */
+    public Event init(@Level int logLevel, String message) {
         mTimestamp = System.currentTimeMillis();
         mLogLevel = logLevel;
         mMessage = message;
+        return this;
     }
 
     public String getMessage() {
@@ -64,4 +72,13 @@
     public @Level int getLogLevel() {
         return mLogLevel;
     }
+
+    /**
+     * Recycle this event
+     */
+    void recycle() {
+        mTimestamp = -1;
+        mLogLevel = DEFAULT_LOG_LEVEL;
+        mMessage = "";
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
index acf761ed..470f2b0 100644
--- a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
@@ -23,23 +23,21 @@
  * Events are stored in {@link SysuiLog} and can be printed in a dumpsys.
  */
 public abstract class RichEvent extends Event {
-    private final int mType;
-    private final String mReason;
+    private int mType;
 
     /**
-     * Create a rich event that includes an event type that matches with an index in the array
+     * Initializes a rich event that includes an event type that matches with an index in the array
      * getEventLabels().
      */
-    public RichEvent(@Event.Level int logLevel, int type, String reason) {
-        super(logLevel, null);
+    public RichEvent init(@Event.Level int logLevel, int type, String reason) {
         final int numEvents = getEventLabels().length;
         if (type < 0 || type >= numEvents) {
             throw new IllegalArgumentException("Unsupported event type. Events only supported"
                     + " from 0 to " + (numEvents - 1) + ", but given type=" + type);
         }
         mType = type;
-        mReason = reason;
-        mMessage = getEventLabels()[mType] + " " + mReason;
+        super.init(logLevel, getEventLabels()[mType] + " " + reason);
+        return this;
     }
 
     /**
@@ -49,25 +47,43 @@
      */
     public abstract String[] getEventLabels();
 
-    public int getType() {
-        return mType;
+    @Override
+    public void recycle() {
+        super.recycle();
+        mType = -1;
     }
 
-    public String getReason() {
-        return mReason;
+    public int getType() {
+        return mType;
     }
 
     /**
      * Builder to build a RichEvent.
      * @param <B> Log specific builder that is extending this builder
+     * @param <E> Type of event we'll be building
      */
-    public abstract static class Builder<B extends Builder<B>> {
+    public abstract static class Builder<B extends Builder<B, E>, E extends RichEvent> {
         public static final int UNINITIALIZED = -1;
 
+        public final SysuiLog mLog;
         private B mBuilder = getBuilder();
-        protected int mType = UNINITIALIZED;
+        protected int mType;
         protected String mReason;
-        protected @Level int mLogLevel = VERBOSE;
+        protected @Level int mLogLevel;
+
+        public Builder(SysuiLog sysuiLog) {
+            mLog = sysuiLog;
+            reset();
+        }
+
+        /**
+         * Reset this builder's parameters so it can be reused to build another RichEvent.
+         */
+        public void reset() {
+            mType = UNINITIALIZED;
+            mReason = null;
+            mLogLevel = VERBOSE;
+        }
 
         /**
          * Get the log-specific builder.
@@ -75,9 +91,9 @@
         public abstract B getBuilder();
 
         /**
-         * Build the log-specific event.
+         * Build the log-specific event given an event to populate.
          */
-        public abstract RichEvent build();
+        public abstract E build(E e);
 
         /**
          * Optional - set the log level. Defaults to DEBUG.
diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
index 0f71d22..4e15668 100644
--- a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
@@ -20,6 +20,7 @@
 import android.os.SystemProperties;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.DumpController;
 import com.android.systemui.Dumpable;
@@ -39,23 +40,26 @@
  * To manually view the logs via adb:
  *      adb shell dumpsys activity service com.android.systemui/.SystemUIService \
  *      dependency DumpController <SysuiLogId>
+ *
+ * Logs can be disabled by setting the following SystemProperty and then restarting the device:
+ *      adb shell setprop persist.sysui.log.enabled.<id> true/false && adb reboot
+ *
+ * @param <E> Type of event we'll be logging
  */
-public class SysuiLog implements Dumpable {
+public class SysuiLog<E extends Event> implements Dumpable {
     public static final SimpleDateFormat DATE_FORMAT =
             new SimpleDateFormat("MM-dd HH:mm:ss", Locale.US);
 
-    private final Object mDataLock = new Object();
+    protected final Object mDataLock = new Object();
     private final String mId;
     private final int mMaxLogs;
     protected boolean mEnabled;
     protected boolean mLogToLogcatEnabled;
 
-    @VisibleForTesting protected ArrayDeque<Event> mTimeline;
+    @VisibleForTesting protected ArrayDeque<E> mTimeline;
 
     /**
      * Creates a SysuiLog
-     * To enable or disable logs, set the system property and then restart the device:
-     *      adb shell setprop sysui.log.enabled.<id> true/false && adb reboot
      * @param dumpController where to register this logger's dumpsys
      * @param id user-readable tag for this logger
      * @param maxDebugLogs maximum number of logs to retain when {@link sDebuggable} is true
@@ -79,23 +83,20 @@
         dumpController.registerDumpable(mId, this);
     }
 
-    public SysuiLog(DumpController dumpController, String id) {
-        this(dumpController, id, DEFAULT_MAX_DEBUG_LOGS, DEFAULT_MAX_LOGS);
-    }
-
     /**
      * Logs an event to the timeline which can be printed by the dumpsys.
      * May also log to logcat if enabled.
-     * @return true if event was logged, else false
+     * @return the last event that was discarded from the Timeline (can be recycled)
      */
-    public boolean log(Event event) {
+    public E log(E event) {
         if (!mEnabled) {
-            return false;
+            return null;
         }
 
+        E recycledEvent = null;
         synchronized (mDataLock) {
             if (mTimeline.size() >= mMaxLogs) {
-                mTimeline.removeFirst();
+                recycledEvent = mTimeline.removeFirst();
             }
 
             mTimeline.add(event);
@@ -121,13 +122,18 @@
                     break;
             }
         }
-        return true;
+
+        if (recycledEvent != null) {
+            recycledEvent.recycle();
+        }
+
+        return recycledEvent;
     }
 
     /**
      * @return user-readable string of the given event with timestamp
      */
-    public String eventToTimestampedString(Event event) {
+    private String eventToTimestampedString(Event event) {
         StringBuilder sb = new StringBuilder();
         sb.append(SysuiLog.DATE_FORMAT.format(event.getTimestamp()));
         sb.append(" ");
@@ -142,9 +148,7 @@
         return event.getMessage();
     }
 
-    /**
-     * only call on this method if you have the mDataLock
-     */
+    @GuardedBy("mDataLock")
     private void dumpTimelineLocked(PrintWriter pw) {
         pw.println("\tTimeline:");
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 9fe9703..d79e383 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -92,7 +92,7 @@
         boolean nightMode = (mContext.getResources().getConfiguration().uiMode
                         & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
 
-        if (isAuto) {
+        if (isAuto && !powerSave) {
             state.secondaryLabel = mContext.getResources().getString(nightMode
                     ? R.string.quick_settings_dark_mode_secondary_label_until_sunrise
                     : R.string.quick_settings_dark_mode_secondary_label_on_at_sunset);
@@ -123,7 +123,7 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return new Intent(Settings.ACTION_DISPLAY_SETTINGS);
+        return new Intent(Settings.ACTION_DARK_THEME_SETTINGS);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 9dcfb6a..9a64b30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
 
 import java.util.ArrayList;
+import java.util.List;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -89,11 +90,22 @@
         }
         final RankingMap currentRanking = getCurrentRanking();
         mMainHandler.post(() -> {
+            // There's currently a race condition between the calls to getActiveNotifications() and
+            // getCurrentRanking(). It's possible for the ranking that we store here to not contain
+            // entries for every notification in getActiveNotifications(). To prevent downstream
+            // crashes, we temporarily fill in these missing rankings with stubs.
+            // See b/146011844 for long-term fix
+            final List<Ranking> newRankings = new ArrayList<>();
+            for (StatusBarNotification sbn : notifications) {
+                newRankings.add(getRankingOrTemporaryStandIn(currentRanking, sbn.getKey()));
+            }
+            final RankingMap completeMap = new RankingMap(newRankings.toArray(new Ranking[0]));
+
             for (StatusBarNotification sbn : notifications) {
                 if (mDownstreamListener != null) {
-                    mDownstreamListener.onNotificationPosted(sbn, currentRanking);
+                    mDownstreamListener.onNotificationPosted(sbn, completeMap);
                 }
-                mEntryManager.addNotification(sbn, currentRanking);
+                mEntryManager.addNotification(sbn, completeMap);
             }
         });
         NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
@@ -192,6 +204,35 @@
         }
     }
 
+    private static Ranking getRankingOrTemporaryStandIn(RankingMap rankingMap, String key) {
+        Ranking ranking = new Ranking();
+        if (!rankingMap.getRanking(key, ranking)) {
+            ranking.populate(
+                    key,
+                    0,
+                    false,
+                    0,
+                    0,
+                    0,
+                    null,
+                    null,
+                    null,
+                    new ArrayList<>(),
+                    new ArrayList<>(),
+                    false,
+                    0,
+                    false,
+                    0,
+                    false,
+                    new ArrayList<>(),
+                    new ArrayList<>(),
+                    false,
+                    false
+            );
+        }
+        return ranking;
+    }
+
     public interface NotificationSettingsListener {
 
         default void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) { }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 4204f68..1648196 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -37,7 +37,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.Utils;
 
@@ -49,8 +48,6 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import dagger.Lazy;
-
 /**
  * NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
  * on their group structure. For example, if a notification becomes bundled with another,
@@ -75,9 +72,6 @@
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final NotificationEntryManager mEntryManager;
 
-    // Lazy
-    private final Lazy<ShadeController> mShadeController;
-
     /**
      * {@code true} if notifications not part of a group should by default be rendered in their
      * expanded state. If {@code false}, then only the first notification will be expanded if
@@ -105,7 +99,6 @@
             VisualStabilityManager visualStabilityManager,
             StatusBarStateController statusBarStateController,
             NotificationEntryManager notificationEntryManager,
-            Lazy<ShadeController> shadeController,
             KeyguardBypassController bypassController,
             BubbleController bubbleController,
             DynamicPrivacyController privacyController) {
@@ -117,7 +110,6 @@
         mVisualStabilityManager = visualStabilityManager;
         mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
         mEntryManager = notificationEntryManager;
-        mShadeController = shadeController;
         Resources res = context.getResources();
         mAlwaysExpandNonGroupedNotification =
                 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 8ecf2b8..c8b34f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -26,6 +26,8 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.StatusBar;
 
+import java.util.Optional;
+
 /**
  * Click handler for generic clicks on notifications. Clicks on specific areas (expansion caret,
  * app ops icon, etc) are handled elsewhere.
@@ -33,11 +35,11 @@
 public final class NotificationClicker implements View.OnClickListener {
     private static final String TAG = "NotificationClicker";
 
-    private final StatusBar mStatusBar;
+    private final Optional<StatusBar> mStatusBar;
     private final BubbleController mBubbleController;
     private final NotificationActivityStarter mNotificationActivityStarter;
 
-    public NotificationClicker(StatusBar statusBar,
+    public NotificationClicker(Optional<StatusBar> statusBar,
             BubbleController bubbleController,
             NotificationActivityStarter notificationActivityStarter) {
         mStatusBar = statusBar;
@@ -52,7 +54,8 @@
             return;
         }
 
-        mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK");
+        mStatusBar.ifPresent(statusBar -> statusBar.wakeUpIfDozing(
+                SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK"));
 
         final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
         final StatusBarNotification sbn = row.getEntry().getSbn();
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 7a58097..d81743a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -267,14 +267,13 @@
             NotificationEntry entry = mPendingNotifications.get(key);
             entry.abortTask();
             mPendingNotifications.remove(key);
-            mNotifLog.log(NotifEvent.INFLATION_ABORTED, entry.getSbn(), null,
-                    "PendingNotification aborted. " + reason);
+            mNotifLog.log(NotifEvent.INFLATION_ABORTED, entry, "PendingNotification aborted"
+                    + " reason=" + reason);
         }
         NotificationEntry addedEntry = getActiveNotificationUnfiltered(key);
         if (addedEntry != null) {
             addedEntry.abortTask();
-            mNotifLog.log(NotifEvent.INFLATION_ABORTED, addedEntry.getSbn(),
-                    null, reason);
+            mNotifLog.log(NotifEvent.INFLATION_ABORTED, addedEntry.getKey() + " " + reason);
         }
     }
 
@@ -501,7 +500,7 @@
 
         abortExistingInflation(key, "addNotification");
         mPendingNotifications.put(key, entry);
-        mNotifLog.log(NotifEvent.NOTIF_ADDED, entry.getSbn());
+        mNotifLog.log(NotifEvent.NOTIF_ADDED, entry);
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
             listener.onPendingEntryAdded(entry);
         }
@@ -536,7 +535,7 @@
         entry.setSbn(notification);
         mGroupManager.onEntryUpdated(entry, oldSbn);
 
-        mNotifLog.log(NotifEvent.NOTIF_UPDATED, entry.getSbn(), entry.getRanking());
+        mNotifLog.log(NotifEvent.NOTIF_UPDATED, entry);
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
             listener.onPreEntryUpdated(entry);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java
index 21a4b4f..a1cfb54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java
@@ -29,7 +29,6 @@
 import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.util.ArrayMap;
-import android.util.Log;
 
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
@@ -40,6 +39,8 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider;
+import com.android.systemui.statusbar.notification.logging.NotifEvent;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.time.SystemClock;
 
@@ -59,8 +60,8 @@
 @MainThread
 @Singleton
 public class NotifListBuilderImpl implements NotifListBuilder {
-
     private final SystemClock mSystemClock;
+    private final NotifLog mNotifLog;
 
     private final List<ListEntry> mNotifList = new ArrayList<>();
 
@@ -86,9 +87,10 @@
     private final List<ListEntry> mReadOnlyNotifList = Collections.unmodifiableList(mNotifList);
 
     @Inject
-    public NotifListBuilderImpl(SystemClock systemClock) {
+    public NotifListBuilderImpl(SystemClock systemClock, NotifLog notifLog) {
         Assert.isMainThread();
         mSystemClock = systemClock;
+        mNotifLog = notifLog;
     }
 
     /**
@@ -193,7 +195,8 @@
                     Assert.isMainThread();
                     mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
 
-                    Log.i(TAG, "Build request received from NotifCollection");
+                    mNotifLog.log(NotifEvent.ON_BUILD_LIST, "Request received from "
+                            + "NotifCollection");
                     mAllEntries = entries;
                     buildList();
                 }
@@ -202,8 +205,7 @@
     private void onFilterInvalidated(NotifFilter filter) {
         Assert.isMainThread();
 
-        // TODO: Convert these log statements (here and elsewhere) into timeline logging
-        Log.i(TAG, String.format(
+        mNotifLog.log(NotifEvent.FILTER_INVALIDATED, String.format(
                 "Filter \"%s\" invalidated; pipeline state is %d",
                 filter.getName(),
                 mPipelineState.getState()));
@@ -214,7 +216,7 @@
     private void onPromoterInvalidated(NotifPromoter filter) {
         Assert.isMainThread();
 
-        Log.i(TAG, String.format(
+        mNotifLog.log(NotifEvent.PROMOTER_INVALIDATED, String.format(
                 "NotifPromoter \"%s\" invalidated; pipeline state is %d",
                 filter.getName(),
                 mPipelineState.getState()));
@@ -225,7 +227,7 @@
     private void onSectionsProviderInvalidated(SectionsProvider provider) {
         Assert.isMainThread();
 
-        Log.i(TAG, String.format(
+        mNotifLog.log(NotifEvent.SECTIONS_PROVIDER_INVALIDATED, String.format(
                 "Sections provider \"%s\" invalidated; pipeline state is %d",
                 provider.getName(),
                 mPipelineState.getState()));
@@ -236,7 +238,7 @@
     private void onNotifComparatorInvalidated(NotifComparator comparator) {
         Assert.isMainThread();
 
-        Log.i(TAG, String.format(
+        mNotifLog.log(NotifEvent.COMPARATOR_INVALIDATED, String.format(
                 "Comparator \"%s\" invalidated; pipeline state is %d",
                 comparator.getName(),
                 mPipelineState.getState()));
@@ -254,7 +256,7 @@
      * if we detect that behavior, we should crash instantly.
      */
     private void buildList() {
-        Log.i(TAG, "Starting notif list build #" + mIterationCount + "...");
+        mNotifLog.log(NotifEvent.START_BUILD_LIST, "Run #" + mIterationCount + "...");
 
         mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
         mPipelineState.setState(STATE_BUILD_STARTED);
@@ -288,15 +290,16 @@
         freeEmptyGroups();
 
         // Step 5: Dispatch the new list, first to any listeners and then to the view layer
-        Log.i(TAG, "List finalized, is:\n" + dumpList(mNotifList));
-        Log.i(TAG, "Dispatching final list to listeners...");
+        mNotifLog.log(NotifEvent.DISPATCH_FINAL_LIST, "List finalized, is:\n"
+                + dumpList(mNotifList));
         dispatchOnBeforeRenderList(mReadOnlyNotifList);
         if (mOnRenderListListener != null) {
             mOnRenderListListener.onRenderList(mReadOnlyNotifList);
         }
 
         // Step 6: We're done!
-        Log.i(TAG, "Notif list build #" + mIterationCount + " completed");
+        mNotifLog.log(NotifEvent.LIST_BUILD_COMPLETE,
+                "Notif list build #" + mIterationCount + " completed");
         mPipelineState.setState(STATE_IDLE);
         mIterationCount++;
     }
@@ -354,7 +357,7 @@
                     if (existingSummary == null) {
                         group.setSummary(entry);
                     } else {
-                        Log.w(TAG, String.format(
+                        mNotifLog.log(NotifEvent.WARN, String.format(
                                 "Duplicate summary for group '%s': '%s' vs. '%s'",
                                 group.getKey(),
                                 existingSummary.getKey(),
@@ -377,7 +380,8 @@
 
                 final String topLevelKey = entry.getKey();
                 if (mGroups.containsKey(topLevelKey)) {
-                    Log.wtf(TAG, "Duplicate non-group top-level key: " + topLevelKey);
+                    mNotifLog.log(NotifEvent.WARN,
+                            "Duplicate non-group top-level key: " + topLevelKey);
                 } else {
                     entry.setParent(ROOT_ENTRY);
                     out.add(entry);
@@ -539,7 +543,7 @@
     private void logParentingChanges() {
         for (NotificationEntry entry : mAllEntries) {
             if (entry.getParent() != entry.getPreviousParent()) {
-                Log.i(TAG, String.format(
+                mNotifLog.log(NotifEvent.PARENT_CHANGED, String.format(
                         "%s: parent changed from %s to %s",
                         entry.getKey(),
                         entry.getPreviousParent() == null
@@ -550,7 +554,7 @@
         }
         for (GroupEntry group : mGroups.values()) {
             if (group.getParent() != group.getPreviousParent()) {
-                Log.i(TAG, String.format(
+                mNotifLog.log(NotifEvent.PARENT_CHANGED, String.format(
                         "%s: parent changed from %s to %s",
                         group.getKey(),
                         group.getPreviousParent() == null
@@ -607,17 +611,17 @@
 
         if (filter != entry.mExcludingFilter) {
             if (entry.mExcludingFilter == null) {
-                Log.i(TAG, String.format(
+                mNotifLog.log(NotifEvent.FILTER_CHANGED, String.format(
                         "%s: filtered out by '%s'",
                         entry.getKey(),
                         filter.getName()));
             } else if (filter == null) {
-                Log.i(TAG, String.format(
+                mNotifLog.log(NotifEvent.FILTER_CHANGED, String.format(
                         "%s: no longer filtered out (previous filter was '%s')",
                         entry.getKey(),
                         entry.mExcludingFilter.getName()));
             } else {
-                Log.i(TAG, String.format(
+                mNotifLog.log(NotifEvent.FILTER_CHANGED, String.format(
                         "%s: filter changed: '%s' -> '%s'",
                         entry.getKey(),
                         entry.mExcludingFilter,
@@ -648,23 +652,22 @@
 
         if (promoter != entry.mNotifPromoter) {
             if (entry.mNotifPromoter == null) {
-                Log.i(TAG, String.format(
+                mNotifLog.log(NotifEvent.PROMOTER_CHANGED, String.format(
                         "%s: Entry promoted to top level by '%s'",
                         entry.getKey(),
                         promoter.getName()));
             } else if (promoter == null) {
-                Log.i(TAG, String.format(
+                mNotifLog.log(NotifEvent.PROMOTER_CHANGED, String.format(
                         "%s: Entry is no longer promoted to top level (previous promoter was '%s')",
                         entry.getKey(),
                         entry.mNotifPromoter.getName()));
             } else {
-                Log.i(TAG, String.format(
+                mNotifLog.log(NotifEvent.PROMOTER_CHANGED, String.format(
                         "%s: Top-level promoter changed: '%s' -> '%s'",
                         entry.getKey(),
                         entry.mNotifPromoter,
                         promoter));
             }
-
             entry.mNotifPromoter = promoter;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
index 511aafc..b68cb0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.notification.collection.coordinator;
 
 import android.Manifest;
-import android.app.AppGlobals;
 import android.app.Notification;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
@@ -43,10 +42,13 @@
     private static final String TAG = "DeviceProvisionedCoordinator";
 
     private final DeviceProvisionedController mDeviceProvisionedController;
+    private final IPackageManager mIPackageManager;
 
     @Inject
-    public DeviceProvisionedCoordinator(DeviceProvisionedController deviceProvisionedController) {
+    public DeviceProvisionedCoordinator(DeviceProvisionedController deviceProvisionedController,
+            IPackageManager packageManager) {
         mDeviceProvisionedController = deviceProvisionedController;
+        mIPackageManager = packageManager;
     }
 
     @Override
@@ -56,7 +58,7 @@
         notifListBuilder.addFilter(mNotifFilter);
     }
 
-    protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+    private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
         @Override
         public boolean shouldFilterOut(NotificationEntry entry, long now) {
             return !mDeviceProvisionedController.isDeviceProvisioned()
@@ -70,17 +72,16 @@
      * marking them as relevant for setup are allowed to show when device is unprovisioned
      */
     private boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
-        final boolean hasPermission = checkUidPermission(AppGlobals.getPackageManager(),
+        final boolean hasPermission = checkUidPermission(
                 Manifest.permission.NOTIFICATION_DURING_SETUP,
                 sbn.getUid()) == PackageManager.PERMISSION_GRANTED;
         return hasPermission
                 && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
     }
 
-    private static int checkUidPermission(IPackageManager packageManager, String permission,
-            int uid) {
+    private int checkUidPermission(String permission, int uid) {
         try {
-            return packageManager.checkUidPermission(permission, uid);
+            return mIPackageManager.checkUidPermission(permission, uid);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
index 4803cf4..378599b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
@@ -24,7 +24,6 @@
 
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.dagger.qualifiers.BgHandler;
 import com.android.systemui.dagger.qualifiers.MainHandler;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
@@ -52,12 +51,11 @@
  */
 @Singleton
 public class ForegroundCoordinator implements Coordinator {
-    private static final String TAG = "ForegroundNotificationCoordinator";
+    private static final String TAG = "ForegroundCoordinator";
 
     private final ForegroundServiceController mForegroundServiceController;
     private final AppOpsController mAppOpsController;
     private final Handler mMainHandler;
-    private final Handler mBgHandler;
 
     private NotifCollection mNotifCollection;
 
@@ -65,12 +63,10 @@
     public ForegroundCoordinator(
             ForegroundServiceController foregroundServiceController,
             AppOpsController appOpsController,
-            @MainHandler Handler mainHandler,
-            @BgHandler Handler bgHandler) {
+            @MainHandler Handler mainHandler) {
         mForegroundServiceController = foregroundServiceController;
         mAppOpsController = appOpsController;
         mMainHandler = mainHandler;
-        mBgHandler = bgHandler;
     }
 
     @Override
@@ -93,7 +89,7 @@
     /**
      * Filters out notifications that represent foreground services that are no longer running.
      */
-    protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+    private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
         @Override
         public boolean shouldFilterOut(NotificationEntry entry, long now) {
             StatusBarNotification sbn = entry.getSbn();
@@ -120,7 +116,8 @@
      * Extends the lifetime of foreground notification services such that they show for at least
      * five seconds
      */
-    private final NotifLifetimeExtender mForegroundLifetimeExtender = new NotifLifetimeExtender() {
+    private final NotifLifetimeExtender mForegroundLifetimeExtender =
+            new NotifLifetimeExtender() {
         private static final int MIN_FGS_TIME_MS = 5000;
         private OnEndLifetimeExtensionCallback mEndCallback;
         private Map<String, Runnable> mEndRunnables = new HashMap<>();
@@ -154,8 +151,8 @@
                         }
                     };
                     mEndRunnables.put(entry.getKey(), runnable);
-                    mBgHandler.postDelayed(runnable, MIN_FGS_TIME_MS
-                            - (currTime - entry.getSbn().getPostTime()));
+                    mMainHandler.postDelayed(runnable,
+                            MIN_FGS_TIME_MS - (currTime - entry.getSbn().getPostTime()));
                 }
             }
 
@@ -166,7 +163,7 @@
         public void cancelLifetimeExtension(NotificationEntry entry) {
             if (mEndRunnables.containsKey(entry.getKey())) {
                 Runnable endRunnable = mEndRunnables.remove(entry.getKey());
-                mBgHandler.removeCallbacks(endRunnable);
+                mMainHandler.removeCallbacks(endRunnable);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index 6daf3fc..4413dc4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -51,7 +51,7 @@
  */
 @Singleton
 public class KeyguardCoordinator implements Coordinator {
-    private static final String TAG = "KeyguardNotificationCoordinator";
+    private static final String TAG = "KeyguardCoordinator";
 
     private final Context mContext;
     private final Handler mMainHandler;
@@ -86,7 +86,7 @@
         notifListBuilder.addFilter(mNotifFilter);
     }
 
-    protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+    private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
         @Override
         public boolean shouldFilterOut(NotificationEntry entry, long now) {
             final StatusBarNotification sbn = entry.getSbn();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index c390f96..24e7a79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -26,7 +26,10 @@
 import javax.inject.Singleton;
 
 /**
- * Filters out NotificationEntries based on its Ranking.
+ * Filters out NotificationEntries based on its Ranking and dozing state.
+ * We check the NotificationEntry's Ranking for:
+ *  - whether the notification's app is suspended or hiding its notifications
+ *  - whether DND settings are hiding notifications from ambient display or the notification list
  */
 @Singleton
 public class RankingCoordinator implements Coordinator {
@@ -51,7 +54,7 @@
      * NotifListBuilder invalidates the notification list each time the ranking is updated,
      * so we don't need to explicitly invalidate this filter on ranking update.
      */
-    protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+    private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
         @Override
         public boolean shouldFilterOut(NotificationEntry entry, long now) {
             // App suspended from Ranking
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
index 8ebbca2..3b06220 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
@@ -17,10 +17,12 @@
 package com.android.systemui.statusbar.notification.logging;
 
 import android.annotation.IntDef;
-import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 
 import com.android.systemui.log.RichEvent;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -31,103 +33,71 @@
  * here to mitigate memory usage.
  */
 public class NotifEvent extends RichEvent {
-    public static final int TOTAL_EVENT_TYPES = 11;
-
     /**
-     * Creates a NotifEvent with an event type that matches with an index in the array
-     * getSupportedEvents() and {@link EventType}.
-     *
-     * The status bar notification and ranking objects are stored as shallow copies of the current
-     * state of the event when this event occurred.
+     * Initializes a rich event that includes an event type that matches with an index in the array
+     * getEventLabels().
      */
-    public NotifEvent(int logLevel, int type, String reason, StatusBarNotification sbn,
-            Ranking ranking) {
-        super(logLevel, type, reason);
-        mMessage += getExtraInfo(sbn, ranking);
-    }
-
-    private String getExtraInfo(StatusBarNotification sbn, Ranking ranking) {
-        StringBuilder extraInfo = new StringBuilder();
-
+    public NotifEvent init(@EventType int type, StatusBarNotification sbn,
+            NotificationListenerService.Ranking ranking, String reason) {
+        StringBuilder extraInfo = new StringBuilder(reason);
         if (sbn != null) {
-            extraInfo.append(" Sbn=");
-            extraInfo.append(sbn);
+            extraInfo.append(" " + sbn.getKey());
         }
 
         if (ranking != null) {
             extraInfo.append(" Ranking=");
-            extraInfo.append(ranking);
+            extraInfo.append(ranking.getRank());
         }
-
-        return extraInfo.toString();
+        super.init(INFO, type, extraInfo.toString());
+        return this;
     }
 
     /**
-     * Event labels for NotifEvents
-     * Index corresponds to the {@link EventType}
+     * Event labels for ListBuilderEvents
+     * Index corresponds to an # in {@link EventType}
      */
     @Override
     public String[] getEventLabels() {
-        final String[] events = new String[]{
-                "NotifAdded",
-                "NotifRemoved",
-                "NotifUpdated",
-                "Filter",
-                "Sort",
-                "FilterAndSort",
-                "NotifVisibilityChanged",
-                "LifetimeExtended",
-                "RemoveIntercepted",
-                "InflationAborted",
-                "Inflated"
-        };
-
-        if (events.length != TOTAL_EVENT_TYPES) {
-            throw new IllegalStateException("NotifEvents events.length should match "
-                    + TOTAL_EVENT_TYPES
-                    + " events.length=" + events.length
-                    + " TOTAL_EVENT_LENGTH=" + TOTAL_EVENT_TYPES);
-        }
-        return events;
+        assert (TOTAL_EVENT_LABELS == (TOTAL_NEM_EVENT_TYPES + TOTAL_LIST_BUILDER_EVENT_TYPES));
+        return EVENT_LABELS;
     }
 
     /**
-     * Builds a NotifEvent.
+     * @return if this event occurred in {@link NotifListBuilder}
      */
-    public static class NotifEventBuilder extends RichEvent.Builder<NotifEventBuilder> {
-        private StatusBarNotification mSbn;
-        private Ranking mRanking;
-
-        @Override
-        public NotifEventBuilder getBuilder() {
-            return this;
-        }
-
-        /**
-         * Stores the status bar notification object. A shallow copy is stored in the NotifEvent's
-         * constructor.
-         */
-        public NotifEventBuilder setSbn(StatusBarNotification sbn) {
-            mSbn = sbn;
-            return this;
-        }
-
-        /**
-         * Stores the ranking object. A shallow copy is stored in the NotifEvent's
-         * constructor.
-         */
-        public NotifEventBuilder setRanking(Ranking ranking) {
-            mRanking = ranking;
-            return this;
-        }
-
-        @Override
-        public RichEvent build() {
-            return new NotifEvent(mLogLevel, mType, mReason, mSbn, mRanking);
-        }
+    static boolean isListBuilderEvent(@EventType int type) {
+        return isBetweenInclusive(type, 0, TOTAL_LIST_BUILDER_EVENT_TYPES);
     }
 
-    @IntDef({NOTIF_ADDED,
+    /**
+     * @return if this event occurred in {@link NotificationEntryManager}
+     */
+    static boolean isNemEvent(@EventType int type) {
+        return isBetweenInclusive(type, TOTAL_LIST_BUILDER_EVENT_TYPES,
+                TOTAL_LIST_BUILDER_EVENT_TYPES + TOTAL_NEM_EVENT_TYPES);
+    }
+
+    private static boolean isBetweenInclusive(int x, int a, int b) {
+        return x >= a && x <= b;
+    }
+
+    @IntDef({
+            // NotifListBuilder events:
+            WARN,
+            ON_BUILD_LIST,
+            START_BUILD_LIST,
+            DISPATCH_FINAL_LIST,
+            LIST_BUILD_COMPLETE,
+            FILTER_INVALIDATED,
+            PROMOTER_INVALIDATED,
+            SECTIONS_PROVIDER_INVALIDATED,
+            COMPARATOR_INVALIDATED,
+            PARENT_CHANGED,
+            FILTER_CHANGED,
+            PROMOTER_CHANGED,
+
+            // NotificationEntryManager events:
+            NOTIF_ADDED,
             NOTIF_REMOVED,
             NOTIF_UPDATED,
             FILTER,
@@ -139,22 +109,72 @@
             INFLATION_ABORTED,
             INFLATED
     })
-
-    /**
-     * Types of NotifEvents
-     */
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
-    public static final int NOTIF_ADDED = 0;
-    public static final int NOTIF_REMOVED = 1;
-    public static final int NOTIF_UPDATED = 2;
-    public static final int FILTER = 3;
-    public static final int SORT = 4;
-    public static final int FILTER_AND_SORT = 5;
-    public static final int NOTIF_VISIBILITY_CHANGED = 6;
-    public static final int LIFETIME_EXTENDED = 7;
+
+    private static final String[] EVENT_LABELS =
+            new String[]{
+                    // NotifListBuilder labels:
+                    "Warning",
+                    "OnBuildList",
+                    "StartBuildList",
+                    "DispatchFinalList",
+                    "ListBuildComplete",
+                    "FilterInvalidated",
+                    "PromoterInvalidated",
+                    "SectionsProviderInvalidated",
+                    "ComparatorInvalidated",
+                    "ParentChanged",
+                    "FilterChanged",
+                    "PromoterChanged",
+
+                    // NEM event labels:
+                    "NotifAdded",
+                    "NotifRemoved",
+                    "NotifUpdated",
+                    "Filter",
+                    "Sort",
+                    "FilterAndSort",
+                    "NotifVisibilityChanged",
+                    "LifetimeExtended",
+                    "RemoveIntercepted",
+                    "InflationAborted",
+                    "Inflated"
+            };
+
+    private static final int TOTAL_EVENT_LABELS = EVENT_LABELS.length;
+
+    /**
+     * Events related to {@link NotifListBuilder}
+     */
+    public static final int WARN = 0;
+    public static final int ON_BUILD_LIST = 1;
+    public static final int START_BUILD_LIST = 2;
+    public static final int DISPATCH_FINAL_LIST = 3;
+    public static final int LIST_BUILD_COMPLETE = 4;
+    public static final int FILTER_INVALIDATED = 5;
+    public static final int PROMOTER_INVALIDATED = 6;
+    public static final int SECTIONS_PROVIDER_INVALIDATED = 7;
+    public static final int COMPARATOR_INVALIDATED = 8;
+    public static final int PARENT_CHANGED = 9;
+    public static final int FILTER_CHANGED = 10;
+    public static final int PROMOTER_CHANGED = 11;
+    private static final int TOTAL_LIST_BUILDER_EVENT_TYPES = 12;
+
+    /**
+     * Events related to {@link NotificationEntryManager}
+     */
+    public static final int NOTIF_ADDED = TOTAL_LIST_BUILDER_EVENT_TYPES + 0;
+    public static final int NOTIF_REMOVED = TOTAL_LIST_BUILDER_EVENT_TYPES + 1;
+    public static final int NOTIF_UPDATED = TOTAL_LIST_BUILDER_EVENT_TYPES + 2;
+    public static final int FILTER = TOTAL_LIST_BUILDER_EVENT_TYPES + 3;
+    public static final int SORT = TOTAL_LIST_BUILDER_EVENT_TYPES + 4;
+    public static final int FILTER_AND_SORT = TOTAL_LIST_BUILDER_EVENT_TYPES + 5;
+    public static final int NOTIF_VISIBILITY_CHANGED = TOTAL_LIST_BUILDER_EVENT_TYPES + 6;
+    public static final int LIFETIME_EXTENDED = TOTAL_LIST_BUILDER_EVENT_TYPES + 7;
     // unable to remove notif - removal intercepted by {@link NotificationRemoveInterceptor}
-    public static final int REMOVE_INTERCEPTED = 8;
-    public static final int INFLATION_ABORTED = 9;
-    public static final int INFLATED = 10;
+    public static final int REMOVE_INTERCEPTED = TOTAL_LIST_BUILDER_EVENT_TYPES + 8;
+    public static final int INFLATION_ABORTED = TOTAL_LIST_BUILDER_EVENT_TYPES + 9;
+    public static final int INFLATED = TOTAL_LIST_BUILDER_EVENT_TYPES + 10;
+    private static final int TOTAL_NEM_EVENT_TYPES = 11;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
index 1292831..299d628 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.logging;
 
+import android.os.SystemProperties;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 
@@ -33,93 +34,82 @@
  *      dependency DumpController NotifLog
  */
 @Singleton
-public class NotifLog extends SysuiLog {
+public class NotifLog extends SysuiLog<NotifEvent> {
     private static final String TAG = "NotifLog";
+    private static final boolean SHOW_NEM_LOGS =
+            SystemProperties.getBoolean("persist.sysui.log.notif.nem", true);
+    private static final boolean SHOW_LIST_BUILDER_LOGS =
+            SystemProperties.getBoolean("persist.sysui.log.notif.listbuilder", true);
+
     private static final int MAX_DOZE_DEBUG_LOGS = 400;
     private static final int MAX_DOZE_LOGS = 50;
 
+    private NotifEvent mRecycledEvent;
+
     @Inject
     public NotifLog(DumpController dumpController) {
         super(dumpController, TAG, MAX_DOZE_DEBUG_LOGS, MAX_DOZE_LOGS);
     }
 
     /**
-     * Logs a {@link NotifEvent} with a notification, ranking and message
+     * Logs a {@link NotifEvent} with a notification, ranking and message.
+     * Uses the last recycled event if available.
      * @return true if successfully logged, else false
      */
-    public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn,
-            Ranking ranking, String msg) {
-        return log(new NotifEvent.NotifEventBuilder()
-                .setType(eventType)
-                .setSbn(sbn)
-                .setRanking(ranking)
-                .setReason(msg)
-                .build());
+    public void log(@NotifEvent.EventType int eventType,
+            StatusBarNotification sbn, Ranking ranking, String msg) {
+        if (!mEnabled
+                || (NotifEvent.isListBuilderEvent(eventType) && !SHOW_LIST_BUILDER_LOGS)
+                || (NotifEvent.isNemEvent(eventType) && !SHOW_NEM_LOGS)) {
+            return;
+        }
+
+        if (mRecycledEvent != null) {
+            mRecycledEvent = log(mRecycledEvent.init(eventType, sbn, ranking, msg));
+        } else {
+            mRecycledEvent = log(new NotifEvent().init(eventType, sbn, ranking, msg));
+        }
     }
 
     /**
-     * Logs a {@link NotifEvent}
-     * @return true if successfully logged, else false
+     * Logs a {@link NotifEvent} with no extra information aside from the event type
      */
-    public boolean log(@NotifEvent.EventType int eventType) {
-        return log(eventType, null, null, null);
+    public void log(@NotifEvent.EventType int eventType) {
+        log(eventType, null, null, "");
     }
 
     /**
      * Logs a {@link NotifEvent} with a message
-     * @return true if successfully logged, else false
      */
-    public boolean log(@NotifEvent.EventType int eventType, String msg) {
-        return log(eventType, null, null, msg);
+    public void log(@NotifEvent.EventType int eventType, String msg) {
+        log(eventType, null, null, msg);
     }
 
     /**
-     * Logs a {@link NotifEvent} with a notification
-     * @return true if successfully logged, else false
+     * Logs a {@link NotifEvent} with a entry
      */
-    public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn) {
-        return log(eventType, sbn, null, "");
+    public void log(@NotifEvent.EventType int eventType, NotificationEntry entry) {
+        log(eventType, entry.getSbn(), entry.getRanking(), "");
     }
 
     /**
-     * Logs a {@link NotifEvent} with a notification
-     * @return true if successfully logged, else false
+     * Logs a {@link NotifEvent} with a NotificationEntry and message
      */
-    public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn, String msg) {
-        return log(eventType, sbn, null, msg);
+    public void log(@NotifEvent.EventType int eventType, NotificationEntry entry, String msg) {
+        log(eventType, entry.getSbn(), entry.getRanking(), msg);
     }
 
     /**
-     * Logs a {@link NotifEvent} with a ranking
-     * @return true if successfully logged, else false
+     * Logs a {@link NotifEvent} with a notification and message
      */
-    public boolean log(@NotifEvent.EventType int eventType, Ranking ranking) {
-        return log(eventType, null, ranking, "");
+    public void log(@NotifEvent.EventType int eventType, StatusBarNotification sbn, String msg) {
+        log(eventType, sbn, null, msg);
     }
 
     /**
-     * Logs a {@link NotifEvent} with a notification and ranking
-     * @return true if successfully logged, else false
+     * Logs a {@link NotifEvent} with a ranking and message
      */
-    public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn,
-            Ranking ranking) {
-        return log(eventType, sbn, ranking, "");
-    }
-
-    /**
-     * Logs a {@link NotifEvent} with a notification entry
-     * @return true if successfully logged, else false
-     */
-    public boolean log(@NotifEvent.EventType int eventType, NotificationEntry entry) {
-        return log(eventType, entry.getSbn(), entry.getRanking(), "");
-    }
-
-    /**
-     * Logs a {@link NotifEvent} with a notification entry
-     * @return true if successfully logged, else false
-     */
-    public boolean log(@NotifEvent.EventType int eventType, NotificationEntry entry,
-            String msg) {
-        return log(eventType, entry.getSbn(), entry.getRanking(), msg);
+    public void log(@NotifEvent.EventType int eventType, Ranking ranking, String msg) {
+        log(eventType, null, ranking, msg);
     }
 }
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 43af3aa..71342c5 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
@@ -5519,7 +5519,7 @@
 
         if (viewsToRemove.isEmpty()) {
             if (closeShade) {
-                mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
             }
             return;
         }
@@ -5581,7 +5581,7 @@
                     setDismissAllInProgress(false);
                     onAnimationComplete.run();
                 });
-                mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
             } else {
                 setDismissAllInProgress(false);
                 onAnimationComplete.run();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 865d7e7..250f730 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -41,6 +41,7 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -136,6 +137,7 @@
     private final Handler mHandler;
     private final KeyguardBypassController mKeyguardBypassController;
     private PowerManager.WakeLock mWakeLock;
+    private final ShadeController mShadeController;
     private final KeyguardUpdateMonitor mUpdateMonitor;
     private final DozeParameters mDozeParameters;
     private final KeyguardStateController mKeyguardStateController;
@@ -159,20 +161,23 @@
     @Inject
     public BiometricUnlockController(Context context, DozeScrimController dozeScrimController,
             KeyguardViewMediator keyguardViewMediator, ScrimController scrimController,
-            StatusBar statusBar, KeyguardStateController keyguardStateController, Handler handler,
+            StatusBar statusBar, ShadeController shadeController,
+            StatusBarWindowController statusBarWindowController,
+            KeyguardStateController keyguardStateController, Handler handler,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             @MainResources Resources resources,
             KeyguardBypassController keyguardBypassController, DozeParameters dozeParameters,
             MetricsLogger metricsLogger, DumpController dumpController) {
         mContext = context;
         mPowerManager = context.getSystemService(PowerManager.class);
+        mShadeController = shadeController;
         mUpdateMonitor = keyguardUpdateMonitor;
         mDozeParameters = dozeParameters;
         mUpdateMonitor.registerCallback(this);
         mMediaManager = Dependency.get(NotificationMediaManager.class);
         Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver);
         Dependency.get(ScreenLifecycle.class).addObserver(mScreenObserver);
-        mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
+        mStatusBarWindowController = statusBarWindowController;
         mDozeScrimController = dozeScrimController;
         mKeyguardViewMediator = keyguardViewMediator;
         mScrimController = scrimController;
@@ -358,8 +363,8 @@
         if (mMode == MODE_SHOW_BOUNCER) {
             mStatusBarKeyguardViewManager.showBouncer(false);
         }
-        mStatusBarKeyguardViewManager.animateCollapsePanels(
-                BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR);
+        mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
+                false /* delayed */, BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR);
         mPendingShowBouncer = false;
     }
 
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 0703d8c..ebe2117 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -164,6 +164,7 @@
     private int mDisabledFlags1;
     private int mDisabledFlags2;
     private final Lazy<StatusBar> mStatusBarLazy;
+    private final ShadeController mShadeController;
     private Recents mRecents;
     private StatusBar mStatusBar;
     private final Divider mDivider;
@@ -216,7 +217,7 @@
             mNavigationBarView.getRotationButtonController().setRotateSuggestionButtonState(false);
 
             // Hide the notifications panel when quick step starts
-            mStatusBarLazy.get().collapsePanel(true /* animate */);
+            mShadeController.collapsePanel(true /* animate */);
         }
 
         @Override
@@ -272,6 +273,7 @@
             BroadcastDispatcher broadcastDispatcher,
             CommandQueue commandQueue, Divider divider,
             Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy,
+            ShadeController shadeController,
             @MainHandler Handler mainHandler) {
         mAccessibilityManagerWrapper = accessibilityManagerWrapper;
         mDeviceProvisionedController = deviceProvisionedController;
@@ -280,6 +282,7 @@
         mAssistManager = assistManager;
         mSysUiFlagsContainer = sysUiFlagsContainer;
         mStatusBarLazy = statusBarLazy;
+        mShadeController = shadeController;
         mAssistantAvailable = mAssistManager.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
         mOverviewProxyService = overviewProxyService;
         mNavigationModeController = navigationModeController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
index deea3f1..2fa6795 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -32,12 +32,24 @@
      */
     void instantExpandNotificationsPanel();
 
+    /** See {@link #animateCollapsePanels(int, boolean)}. */
+    void animateCollapsePanels();
+
+    /** See {@link #animateCollapsePanels(int, boolean)}. */
+    void animateCollapsePanels(int flags);
+
     /**
      * Collapse the shade animated, showing the bouncer when on {@link StatusBarState#KEYGUARD} or
      * dismissing {@link StatusBar} when on {@link StatusBarState#SHADE}.
      */
     void animateCollapsePanels(int flags, boolean force);
 
+    /** See {@link #animateCollapsePanels(int, boolean)}. */
+    void animateCollapsePanels(int flags, boolean force, boolean delayed);
+
+    /** See {@link #animateCollapsePanels(int, boolean)}. */
+    void animateCollapsePanels(int flags, boolean force, boolean delayed, float speedUpFactor);
+
     /**
      * If the notifications panel is not fully expanded, collapse it animated.
      *
@@ -61,11 +73,9 @@
     void addPostCollapseAction(Runnable action);
 
     /**
-     * Notify the shade controller that the current user changed
-     *
-     * @param newUserId userId of the new user
+     * Run all of the runnables added by {@link #addPostCollapseAction}.
      */
-    void setLockscreenUser(int newUserId);
+    void runPostCollapseRunnables();
 
     /**
      * If secure with redaction: Show bouncer, go to unlocked shade.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
new file mode 100644
index 0000000..57e7014
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -0,0 +1,236 @@
+/*
+ * 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.phone;
+
+import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.StatusBarState;
+
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
+/** An implementation of {@link com.android.systemui.statusbar.phone.ShadeController}. */
+@Singleton
+public class ShadeControllerImpl implements ShadeController {
+
+    private static final String TAG = "ShadeControllerImpl";
+    private static final boolean SPEW = false;
+
+    private final CommandQueue mCommandQueue;
+    private final StatusBarStateController mStatusBarStateController;
+    protected final StatusBarWindowController mStatusBarWindowController;
+    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    private final int mDisplayId;
+    protected final Lazy<StatusBar> mStatusBarLazy;
+    private final Lazy<AssistManager> mAssistManagerLazy;
+    private final Lazy<BubbleController> mBubbleControllerLazy;
+
+    private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
+
+    @Inject
+    public ShadeControllerImpl(
+            CommandQueue commandQueue,
+            StatusBarStateController statusBarStateController,
+            StatusBarWindowController statusBarWindowController,
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+            WindowManager windowManager,
+            Lazy<StatusBar> statusBarLazy,
+            Lazy<AssistManager> assistManagerLazy,
+            Lazy<BubbleController> bubbleControllerLazy
+    ) {
+        mCommandQueue = commandQueue;
+        mStatusBarStateController = statusBarStateController;
+        mStatusBarWindowController = statusBarWindowController;
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+        mDisplayId = windowManager.getDefaultDisplay().getDisplayId();
+        // TODO: Remove circular reference to StatusBar when possible.
+        mStatusBarLazy = statusBarLazy;
+        mAssistManagerLazy = assistManagerLazy;
+        mBubbleControllerLazy = bubbleControllerLazy;
+    }
+
+    @Override
+    public void instantExpandNotificationsPanel() {
+        // Make our window larger and the panel expanded.
+        getStatusBar().makeExpandedVisible(true /* force */);
+        getNotificationPanelView().expand(false /* animate */);
+        mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
+    }
+
+    @Override
+    public void animateCollapsePanels() {
+        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+    }
+
+    @Override
+    public void animateCollapsePanels(int flags) {
+        animateCollapsePanels(flags, false /* force */, false /* delayed */,
+                1.0f /* speedUpFactor */);
+    }
+
+    @Override
+    public void animateCollapsePanels(int flags, boolean force) {
+        animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */);
+    }
+
+    @Override
+    public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
+        animateCollapsePanels(flags, force, delayed, 1.0f /* speedUpFactor */);
+    }
+
+    @Override
+    public void animateCollapsePanels(int flags, boolean force, boolean delayed,
+            float speedUpFactor) {
+        if (!force && mStatusBarStateController.getState() != StatusBarState.SHADE) {
+            runPostCollapseRunnables();
+            return;
+        }
+        if (SPEW) {
+            Log.d(TAG, "animateCollapse():"
+                    + " mExpandedVisible=" + getStatusBar().isExpandedVisible()
+                    + " flags=" + flags);
+        }
+
+        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
+            getStatusBar().postHideRecentApps();
+        }
+
+        // TODO(b/62444020): remove when this bug is fixed
+        Log.v(TAG, "mStatusBarWindow: " + getStatusBarWindowView() + " canPanelBeCollapsed(): "
+                + getNotificationPanelView().canPanelBeCollapsed());
+        if (getStatusBarWindowView() != null && getNotificationPanelView().canPanelBeCollapsed()) {
+            // release focus immediately to kick off focus change transition
+            mStatusBarWindowController.setStatusBarFocusable(false);
+
+            getStatusBar().getStatusBarWindowViewController().cancelExpandHelper();
+            getStatusBarView().collapsePanel(true /* animate */, delayed, speedUpFactor);
+        } else {
+            mBubbleControllerLazy.get().collapseStack();
+        }
+    }
+
+
+    @Override
+    public boolean closeShadeIfOpen() {
+        if (!getNotificationPanelView().isFullyCollapsed()) {
+            mCommandQueue.animateCollapsePanels(
+                    CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
+            getStatusBar().visibilityChanged(false);
+            mAssistManagerLazy.get().hideAssist();
+        }
+        return false;
+    }
+
+    @Override
+    public void postOnShadeExpanded(Runnable executable) {
+        getNotificationPanelView().getViewTreeObserver().addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        if (getStatusBar().getStatusBarWindow().getHeight()
+                                != getStatusBar().getStatusBarHeight()) {
+                            getNotificationPanelView().getViewTreeObserver()
+                                    .removeOnGlobalLayoutListener(this);
+                            getNotificationPanelView().post(executable);
+                        }
+                    }
+                });
+    }
+
+    @Override
+    public void addPostCollapseAction(Runnable action) {
+        mPostCollapseRunnables.add(action);
+    }
+
+    @Override
+    public void runPostCollapseRunnables() {
+        ArrayList<Runnable> clonedList = new ArrayList<>(mPostCollapseRunnables);
+        mPostCollapseRunnables.clear();
+        int size = clonedList.size();
+        for (int i = 0; i < size; i++) {
+            clonedList.get(i).run();
+        }
+        mStatusBarKeyguardViewManager.readyForKeyguardDone();
+    }
+
+    @Override
+    public void goToLockedShade(View startingChild) {
+        // TODO: Move this code out of StatusBar into ShadeController.
+        getStatusBar().goToLockedShade(startingChild);
+    }
+
+    @Override
+    public boolean collapsePanel() {
+        if (!getNotificationPanelView().isFullyCollapsed()) {
+            // close the shade if it was open
+            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+                    true /* force */, true /* delayed */);
+            getStatusBar().visibilityChanged(false);
+
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public void collapsePanel(boolean animate) {
+        if (animate) {
+            boolean willCollapse = collapsePanel();
+            if (!willCollapse) {
+                runPostCollapseRunnables();
+            }
+        } else if (!getPresenter().isPresenterFullyCollapsed()) {
+            getStatusBar().instantCollapseNotificationPanel();
+            getStatusBar().visibilityChanged(false);
+        } else {
+            runPostCollapseRunnables();
+        }
+    }
+
+    private StatusBar getStatusBar() {
+        return mStatusBarLazy.get();
+    }
+
+    private NotificationPresenter getPresenter() {
+        return getStatusBar().getPresenter();
+    }
+
+    protected StatusBarWindowView getStatusBarWindowView() {
+        return getStatusBar().getStatusBarWindow();
+    }
+
+    protected PhoneStatusBarView getStatusBarView() {
+        return (PhoneStatusBarView) getStatusBar().getStatusBarView();
+    }
+
+    private NotificationPanelView getNotificationPanelView() {
+        return getStatusBar().getPanel();
+    }
+}
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 709143d..a9d7601 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -107,7 +107,6 @@
 import android.view.ThreadedRenderer;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -232,7 +231,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.util.ArrayList;
 import java.util.Map;
 import java.util.Optional;
 
@@ -245,8 +243,7 @@
         ActivityStarter, KeyguardStateController.Callback,
         OnHeadsUpChangedListener, CommandQueue.Callbacks,
         ColorExtractor.OnColorsChangedListener, ConfigurationListener,
-        StatusBarStateController.StateListener, ShadeController,
-        ActivityLaunchAnimator.Callback {
+        StatusBarStateController.StateListener, ActivityLaunchAnimator.Callback {
     public static final boolean MULTIUSER_DEBUG = false;
 
     public static final boolean ENABLE_CHILD_NOTIFICATIONS
@@ -387,6 +384,7 @@
     private final Optional<Divider> mDividerOptional;
     private final StatusBarNotificationActivityStarter.Builder
             mStatusBarNotificationActivityStarterBuilder;
+    private final ShadeController mShadeController;
     private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
     private final LightsOutNotifController mLightsOutNotifController;
     private final DismissCallbackRegistry mDismissCallbackRegistry;
@@ -409,7 +407,6 @@
     private boolean mExpandedVisible;
 
     private final int[] mAbsPos = new int[2];
-    private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
 
     private final NotificationGutsManager mGutsManager;
     private final NotificationLogger mNotificationLogger;
@@ -683,6 +680,7 @@
             LightsOutNotifController lightsOutNotifController,
             StatusBarNotificationActivityStarter.Builder
                     statusBarNotificationActivityStarterBuilder,
+            ShadeController shadeController,
             SuperStatusBarViewFactory superStatusBarViewFactory,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             ViewMediatorCallback viewMediatorCallback,
@@ -754,6 +752,7 @@
         mRemoteInputUriController = remoteInputUriController;
         mDividerOptional = dividerOptional;
         mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
+        mShadeController = shadeController;
         mSuperStatusBarViewFactory = superStatusBarViewFactory;
         mLightsOutNotifController =  lightsOutNotifController;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
@@ -1240,7 +1239,7 @@
                 mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
                 mNotificationAlertingManager, rowBinder, mKeyguardStateController,
                 mKeyguardIndicationController,
-                this /* statusBar */, mCommandQueue);
+                this /* statusBar */, mShadeController, mCommandQueue);
 
         mNotificationListController =
                 new NotificationListController(
@@ -1264,7 +1263,7 @@
         mRemoteInputUriController.attach(mEntryManager);
 
         rowBinder.setNotificationClicker(new NotificationClicker(
-                this, mBubbleController, mNotificationActivityStarter));
+                Optional.of(this), mBubbleController, mNotificationActivityStarter));
 
         mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
         mNotificationListController.bind();
@@ -1318,7 +1317,7 @@
             mRemoteInputManager.checkRemoteInputOutside(event);
             if (event.getAction() == MotionEvent.ACTION_DOWN) {
                 if (mExpandedVisible) {
-                    animateCollapsePanels();
+                    mShadeController.animateCollapsePanels();
                 }
             }
             return mStatusBarWindow.onTouchEvent(event);
@@ -1419,6 +1418,10 @@
         return mStatusBarWindow;
     }
 
+    public StatusBarWindowViewController getStatusBarWindowViewController() {
+        return mStatusBarWindowViewController;
+    }
+
     protected ViewGroup getBouncerContainer() {
         return mStatusBarWindow;
     }
@@ -1574,7 +1577,7 @@
 
         if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
             if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
-                animateCollapsePanels();
+                mShadeController.animateCollapsePanels();
             }
         }
 
@@ -1598,7 +1601,7 @@
         if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
             updateQsExpansionEnabled();
             if ((state1 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
-                animateCollapsePanels();
+                mShadeController.animateCollapsePanels();
             }
         }
     }
@@ -1840,7 +1843,7 @@
                 && !mActivityLaunchAnimator.isLaunchForActivity()) {
             onClosingFinished();
         } else {
-            collapsePanel(true /* animate */);
+            mShadeController.collapsePanel(true /* animate */);
         }
     }
 
@@ -1888,7 +1891,7 @@
                     animateExpandSettingsPanel((String) m.obj);
                     break;
                 case MSG_CLOSE_PANELS:
-                    animateCollapsePanels();
+                    mShadeController.animateCollapsePanels();
                     break;
                 case MSG_LAUNCH_TRANSITION_TIMEOUT:
                     onLaunchTransitionTimeout();
@@ -1985,20 +1988,13 @@
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
     }
 
-    public void animateCollapsePanels() {
-        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-    }
-
-    private final Runnable mAnimateCollapsePanels = this::animateCollapsePanels;
-
     public void postAnimateCollapsePanels() {
-        mHandler.post(mAnimateCollapsePanels);
+        mHandler.post(mShadeController::animateCollapsePanels);
     }
 
     public void postAnimateForceCollapsePanels() {
-        mHandler.post(() -> {
-            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
-        });
+        mHandler.post(() -> mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE,
+                true /* force */));
     }
 
     public void postAnimateOpenPanels() {
@@ -2008,67 +2004,35 @@
     @Override
     public void togglePanel() {
         if (mPanelExpanded) {
-            animateCollapsePanels();
+            mShadeController.animateCollapsePanels();
         } else {
             animateExpandNotificationsPanel();
         }
     }
 
-    public void animateCollapsePanels(int flags) {
-        animateCollapsePanels(flags, false /* force */, false /* delayed */,
+    @Override
+    public void animateCollapsePanels(int flags, boolean force) {
+        mShadeController.animateCollapsePanels(flags, force, false /* delayed */,
                 1.0f /* speedUpFactor */);
     }
 
-    @Override
-    public void animateCollapsePanels(int flags, boolean force) {
-        animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */);
-    }
-
-    public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
-        animateCollapsePanels(flags, force, delayed, 1.0f /* speedUpFactor */);
-    }
-
-    public void animateCollapsePanels(int flags, boolean force, boolean delayed,
-            float speedUpFactor) {
-        if (!force && mState != StatusBarState.SHADE) {
-            runPostCollapseRunnables();
-            return;
-        }
-        if (SPEW) {
-            Log.d(TAG, "animateCollapse():"
-                    + " mExpandedVisible=" + mExpandedVisible
-                    + " flags=" + flags);
-        }
-
-        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
-            if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
-                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
-                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
-            }
-        }
-
-        // TODO(b/62444020): remove when this bug is fixed
-        Log.v(TAG, "mStatusBarWindow: " + mStatusBarWindow + " canPanelBeCollapsed(): "
-                + mNotificationPanel.canPanelBeCollapsed());
-        if (mStatusBarWindow != null && mNotificationPanel.canPanelBeCollapsed()) {
-            // release focus immediately to kick off focus change transition
-            mStatusBarWindowController.setStatusBarFocusable(false);
-
-            mStatusBarWindowViewController.cancelExpandHelper();
-            mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
-        } else {
-            mBubbleController.collapseStack();
+    /**
+     * Called by {@link ShadeController} when it calls
+     * {@link ShadeController#animateCollapsePanels(int, boolean, boolean, float)}.
+     */
+    void postHideRecentApps() {
+        if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
+            mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
+            mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
         }
     }
 
-    private void runPostCollapseRunnables() {
-        ArrayList<Runnable> clonedList = new ArrayList<>(mPostCollapseRunnables);
-        mPostCollapseRunnables.clear();
-        int size = clonedList.size();
-        for (int i = 0; i < size; i++) {
-            clonedList.get(i).run();
-        }
-        mStatusBarKeyguardViewManager.readyForKeyguardDone();
+    public boolean isExpandedVisible() {
+        return mExpandedVisible;
+    }
+
+    public boolean isPanelExpanded() {
+        return mPanelExpanded;
     }
 
     /**
@@ -2147,7 +2111,7 @@
         mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
                 true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
 
-        runPostCollapseRunnables();
+        mShadeController.runPostCollapseRunnables();
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
         if (!mNotificationActivityStarter.isCollapsingToShowActivityOverLockscreen()) {
             showBouncerIfKeyguard();
@@ -2672,12 +2636,12 @@
             }
             if (dismissShade) {
                 if (mExpandedVisible && !mBouncerShowing) {
-                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
-                            true /* delayed*/);
+                    mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+                            true /* force */, true /* delayed*/);
                 } else {
 
                     // Do it after DismissAction has been processed to conserve the needed ordering.
-                    mHandler.post(this::runPostCollapseRunnables);
+                    mHandler.post(mShadeController::runPostCollapseRunnables);
                 }
             } else if (isInLaunchTransition() && mNotificationPanel.isLaunchTransitionFinished()) {
 
@@ -2709,7 +2673,7 @@
                     if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
                         flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
                     }
-                    animateCollapsePanels(flags);
+                    mShadeController.animateCollapsePanels(flags);
                 }
             }
             else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
@@ -2803,7 +2767,11 @@
         mScreenPinningRequest.onConfigurationChanged();
     }
 
-    @Override
+    /**
+     * Notify the shade controller that the current user changed
+     *
+     * @param newUserId userId of the new user
+     */
     public void setLockscreenUser(int newUserId) {
         if (mLockscreenWallpaper != null) {
             mLockscreenWallpaper.setCurrentUser(newUserId);
@@ -3145,7 +3113,7 @@
     private void updatePanelExpansionForKeyguard() {
         if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode()
                 != BiometricUnlockController.MODE_WAKE_AND_UNLOCK && !mBouncerShowing) {
-            instantExpandNotificationsPanel();
+            mShadeController.instantExpandNotificationsPanel();
         } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
             instantCollapseNotificationPanel();
         }
@@ -3160,10 +3128,6 @@
         mPresenter.updateMediaMetaData(true /* metaDataChanged */, true);
     }
 
-    public void addPostCollapseAction(Runnable r) {
-        mPostCollapseRunnables.add(r);
-    }
-
     public boolean isInLaunchTransition() {
         return mNotificationPanel.isLaunchTransitionRunning()
                 || mNotificationPanel.isLaunchTransitionFinished();
@@ -3396,7 +3360,7 @@
 
     public boolean onMenuPressed() {
         if (shouldUnlockOnMenuPressed()) {
-            animateCollapsePanels(
+            mShadeController.animateCollapsePanels(
                     CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
             return true;
         }
@@ -3426,7 +3390,7 @@
         }
         if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
             if (mNotificationPanel.canPanelBeCollapsed()) {
-                animateCollapsePanels();
+                mShadeController.animateCollapsePanels();
             } else {
                 mBubbleController.performBackPressIfNeeded();
             }
@@ -3440,7 +3404,7 @@
 
     public boolean onSpacePressed() {
         if (mDeviceInteractive && mState != StatusBarState.SHADE) {
-            animateCollapsePanels(
+            mShadeController.animateCollapsePanels(
                     CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
             return true;
         }
@@ -3454,43 +3418,9 @@
         }
     }
 
-    @Override
-    public void instantExpandNotificationsPanel() {
-        // Make our window larger and the panel expanded.
-        makeExpandedVisible(true);
-        mNotificationPanel.expand(false /* animate */);
-        mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
-    }
-
-    @Override
-    public boolean closeShadeIfOpen() {
-        if (!mNotificationPanel.isFullyCollapsed()) {
-            mCommandQueue.animateCollapsePanels(
-                    CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
-            visibilityChanged(false);
-            mAssistManagerLazy.get().hideAssist();
-        }
-        return false;
-    }
-
-    @Override
-    public void postOnShadeExpanded(Runnable executable) {
-        mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
-                new ViewTreeObserver.OnGlobalLayoutListener() {
-                    @Override
-                    public void onGlobalLayout() {
-                        if (getStatusBarWindow().getHeight() != getStatusBarHeight()) {
-                            mNotificationPanel.getViewTreeObserver()
-                                    .removeOnGlobalLayoutListener(this);
-                            mNotificationPanel.post(executable);
-                        }
-                    }
-                });
-    }
-
-    private void instantCollapseNotificationPanel() {
+    void instantCollapseNotificationPanel() {
         mNotificationPanel.instantCollapse();
-        runPostCollapseRunnables();
+        mShadeController.runPostCollapseRunnables();
     }
 
     @Override
@@ -3576,11 +3506,11 @@
     }
 
     public void onTrackingStarted() {
-        runPostCollapseRunnables();
+        mShadeController.runPostCollapseRunnables();
     }
 
     public void onClosingFinished() {
-        runPostCollapseRunnables();
+        mShadeController.runPostCollapseRunnables();
         if (!mPresenter.isPresenterFullyCollapsed()) {
             // if we set it not to be focusable when collapsing, we have to undo it when we aborted
             // the closing
@@ -3641,7 +3571,7 @@
      *
      * @param expandView The view to expand after going to the shade.
      */
-    public void goToLockedShade(View expandView) {
+    void goToLockedShade(View expandView) {
         if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
             return;
         }
@@ -3700,7 +3630,7 @@
             mStatusBarWindowViewController.cancelCurrentTouch();
         }
         if (mPanelExpanded && mState == StatusBarState.SHADE) {
-            animateCollapsePanels();
+            mShadeController.animateCollapsePanels();
         }
     }
 
@@ -4067,7 +3997,7 @@
                 Settings.Secure.putInt(mContext.getContentResolver(),
                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
                 if (BANNER_ACTION_SETUP.equals(action)) {
-                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+                    mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
                             true /* force */);
                     mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -4078,35 +4008,6 @@
         }
     };
 
-    @Override
-    public void collapsePanel(boolean animate) {
-        if (animate) {
-            boolean willCollapse = collapsePanel();
-            if (!willCollapse) {
-                runPostCollapseRunnables();
-            }
-        } else if (!mPresenter.isPresenterFullyCollapsed()) {
-            instantCollapseNotificationPanel();
-            visibilityChanged(false);
-        } else {
-            runPostCollapseRunnables();
-        }
-    }
-
-    @Override
-    public boolean collapsePanel() {
-        if (!mNotificationPanel.isFullyCollapsed()) {
-            // close the shade if it was open
-            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
-                    true /* delayed */);
-            visibilityChanged(false);
-
-            return true;
-        } else {
-            return false;
-        }
-    }
-
     private final NotificationListener mNotificationListener;
 
     public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
@@ -4217,7 +4118,7 @@
                 action.run();
             }).start();
 
-            return collapsePanel();
+            return mShadeController.collapsePanel();
         }, afterKeyguardGone);
     }
 
@@ -4277,7 +4178,7 @@
         return options.toBundle();
     }
 
-    protected void visibilityChanged(boolean visible) {
+    void visibilityChanged(boolean visible) {
         if (mVisible != visible) {
             mVisible = visible;
             if (!visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index dac4e58..f51174b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -48,7 +48,6 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.RemoteInputController;
@@ -921,12 +920,6 @@
         mStatusBar.keyguardGoingAway();
     }
 
-    public void animateCollapsePanels(float speedUpFactor) {
-        mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */,
-                false /* delayed */, speedUpFactor);
-    }
-
-
     /**
      * Called when cancel button in bouncer is pressed.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index 312c85f..e31c53a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -164,6 +164,7 @@
             LightsOutNotifController lightsOutNotifController,
             StatusBarNotificationActivityStarter.Builder
                     statusBarNotificationActivityStarterBuilder,
+            ShadeController shadeController,
             SuperStatusBarViewFactory superStatusBarViewFactory,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             ViewMediatorCallback viewMediatorCallback,
@@ -237,6 +238,7 @@
                 dividerOptional,
                 lightsOutNotifController,
                 statusBarNotificationActivityStarterBuilder,
+                shadeController,
                 superStatusBarViewFactory,
                 statusBarKeyguardViewManager,
                 viewMediatorCallback,
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 1988b42..3123f8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -524,7 +524,7 @@
         private final ActivityIntentHelper mActivityIntentHelper;
         private final BubbleController mBubbleController;
         private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
-        private ShadeController mShadeController;
+        private final ShadeController mShadeController;
         private NotificationPresenter mNotificationPresenter;
         private ActivityLaunchAnimator mActivityLaunchAnimator;
         private StatusBar mStatusBar;
@@ -553,6 +553,7 @@
                 @BgHandler Handler backgroundHandler,
                 ActivityIntentHelper activityIntentHelper,
                 BubbleController bubbleController,
+                ShadeController shadeController,
                 SuperStatusBarViewFactory superStatusBarViewFactory) {
             mContext = context;
             mCommandQueue = commandQueue;
@@ -577,13 +578,13 @@
             mBackgroundHandler = backgroundHandler;
             mActivityIntentHelper = activityIntentHelper;
             mBubbleController = bubbleController;
+            mShadeController = shadeController;
             mSuperStatusBarViewFactory = superStatusBarViewFactory;
         }
 
-        /** Sets the status bar to use as {@link StatusBar} and {@link ShadeController}. */
+        /** Sets the status bar to use as {@link StatusBar}. */
         public Builder setStatusBar(StatusBar statusBar) {
             mStatusBar = statusBar;
-            mShadeController = statusBar;
             return this;
         }
 
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 2649166..8fc624d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -88,7 +88,6 @@
 
     private static final String TAG = "StatusBarNotificationPresenter";
 
-    private final ShadeController mShadeController = Dependency.get(ShadeController.class);
     private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
     private final KeyguardStateController mKeyguardStateController;
     private final NotificationViewHierarchyManager mViewHierarchyManager =
@@ -116,6 +115,7 @@
     private final Context mContext;
     private final KeyguardIndicationController mKeyguardIndicationController;
     private final StatusBar mStatusBar;
+    private final ShadeController mShadeController;
     private final CommandQueue mCommandQueue;
 
     private final AccessibilityManager mAccessibilityManager;
@@ -145,6 +145,7 @@
             KeyguardStateController keyguardStateController,
             KeyguardIndicationController keyguardIndicationController,
             StatusBar statusBar,
+            ShadeController shadeController,
             CommandQueue commandQueue) {
         mContext = context;
         mKeyguardStateController = keyguardStateController;
@@ -154,6 +155,7 @@
         mKeyguardIndicationController = keyguardIndicationController;
         // TODO: use KeyguardStateController#isOccluded to remove this dependency
         mStatusBar = statusBar;
+        mShadeController = shadeController;
         mCommandQueue = commandQueue;
         mAboveShelfObserver = new AboveShelfObserver(stackScroller);
         mActivityLaunchAnimator = activityLaunchAnimator;
@@ -387,7 +389,7 @@
         }
         updateNotificationViews();
         mMediaManager.clearCurrentMediaNotification();
-        mShadeController.setLockscreenUser(newUserId);
+        mStatusBar.setLockscreenUser(newUserId);
         updateMediaMetaData(true, false);
     }
 
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 2012b57..6193a8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -50,8 +50,6 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import dagger.Lazy;
-
 /**
  */
 @Singleton
@@ -62,9 +60,9 @@
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     private final ActivityStarter mActivityStarter;
-    private final Lazy<ShadeController> mShadeControllerLazy;
     private final Context mContext;
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    private final ShadeController mShadeController;
     private final ActivityIntentHelper mActivityIntentHelper;
     private final NotificationGroupManager mGroupManager;
     private View mPendingWorkRemoteInputView;
@@ -83,16 +81,16 @@
             KeyguardStateController keyguardStateController,
             StatusBarStateController statusBarStateController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            ActivityStarter activityStarter, Lazy<ShadeController> shadeControllerLazy,
+            ActivityStarter activityStarter, ShadeController shadeController,
             CommandQueue commandQueue) {
         mContext = context;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+        mShadeController = shadeController;
         mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
                 new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
         mLockscreenUserManager = notificationLockscreenUserManager;
         mKeyguardStateController = keyguardStateController;
         mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
-        mShadeControllerLazy = shadeControllerLazy;
         mActivityStarter = activityStarter;
         mStatusBarStateController.addCallback(this);
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
@@ -167,8 +165,8 @@
                     });
                 }
             };
-            mShadeControllerLazy.get().postOnShadeExpanded(clickPendingViewRunnable);
-            mShadeControllerLazy.get().instantExpandNotificationsPanel();
+            mShadeController.postOnShadeExpanded(clickPendingViewRunnable);
+            mShadeController.instantExpandNotificationsPanel();
         }
     }
 
@@ -256,7 +254,7 @@
                 boolean handled = defaultHandler.handleClick();
 
                 // close the shade if it was open and maybe wait for activity start.
-                return handled && mShadeControllerLazy.get().closeShadeIfOpen();
+                return handled && mShadeController.closeShadeIfOpen();
             }, null, afterKeyguardGone);
             return true;
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
index f8929e0..eb86bcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
@@ -57,12 +57,9 @@
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
-
 /**
  * Controller for {@link StatusBarWindowView}.
  */
-//@Singleton
 public class StatusBarWindowViewController {
     private final InjectionInflationController mInjectionInflationController;
     private final NotificationWakeUpCoordinator mCoordinator;
@@ -80,7 +77,7 @@
     private final DozeParameters mDozeParameters;
     private final CommandQueue mCommandQueue;
     private final StatusBarWindowView mView;
-    private final Lazy<ShadeController> mShadeControllerLazy;
+    private final ShadeController mShadeController;
 
     private GestureDetector mGestureDetector;
     private View mBrightnessMirror;
@@ -114,7 +111,7 @@
             DozeLog dozeLog,
             DozeParameters dozeParameters,
             CommandQueue commandQueue,
-            Lazy<ShadeController> shadeControllerLazy,
+            ShadeController shadeController,
             DockManager dockManager,
             StatusBarWindowView statusBarWindowView) {
         mInjectionInflationController = injectionInflationController;
@@ -133,7 +130,7 @@
         mDozeParameters = dozeParameters;
         mCommandQueue = commandQueue;
         mView = statusBarWindowView;
-        mShadeControllerLazy = shadeControllerLazy;
+        mShadeController = shadeController;
         mDockManager = dockManager;
 
         // This view is not part of the newly inflated expanded status bar.
@@ -153,7 +150,7 @@
                 mBypassController,
                 mFalsingManager,
                 mPluginManager,
-                mShadeControllerLazy.get(),
+                mShadeController,
                 mNotificationLockscreenUserManager,
                 mNotificationEntryManager,
                 mKeyguardStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index b0cd90c..a6108a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -91,7 +91,8 @@
     @VisibleForTesting
     boolean mIsShowingIconGracefully = false;
     // Some specific carriers have 5GE network which is special LTE CA network.
-    private static final int NETWORK_TYPE_LTE_CA_5GE = TelephonyManager.MAX_NETWORK_TYPE + 1;
+    private static final int NETWORK_TYPE_LTE_CA_5GE =
+            TelephonyManager.getAllNetworkTypes().length + 1;
 
     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
     // need listener lists anymore.
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 2f13f39..367d4d2 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -16,19 +16,13 @@
 
 package com.android.systemui.usb;
 
-import android.app.Activity;
 import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.debug.IAdbManager;
-import android.hardware.usb.UsbManager;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.util.EventLog;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -42,25 +36,14 @@
 import com.android.internal.app.AlertActivity;
 import com.android.internal.app.AlertController;
 import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-
-import javax.inject.Inject;
 
 public class UsbDebuggingActivity extends AlertActivity
                                   implements DialogInterface.OnClickListener {
     private static final String TAG = "UsbDebuggingActivity";
 
     private CheckBox mAlwaysAllow;
-    private UsbDisconnectedReceiver mDisconnectedReceiver;
-    private final BroadcastDispatcher mBroadcastDispatcher;
     private String mKey;
 
-    @Inject
-    public UsbDebuggingActivity(BroadcastDispatcher broadcastDispatcher) {
-        super();
-        mBroadcastDispatcher = broadcastDispatcher;
-    }
-
     @Override
     public void onCreate(Bundle icicle) {
         Window window = getWindow();
@@ -70,10 +53,6 @@
 
         super.onCreate(icicle);
 
-        if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
-            mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
-        }
-
         Intent intent = getIntent();
         String fingerprints = intent.getStringExtra("fingerprints");
         mKey = intent.getStringExtra("key");
@@ -126,40 +105,6 @@
         super.onWindowAttributesChanged(params);
     }
 
-    private class UsbDisconnectedReceiver extends BroadcastReceiver {
-        private final Activity mActivity;
-        public UsbDisconnectedReceiver(Activity activity) {
-            mActivity = activity;
-        }
-
-        @Override
-        public void onReceive(Context content, Intent intent) {
-            String action = intent.getAction();
-            if (!UsbManager.ACTION_USB_STATE.equals(action)) {
-                return;
-            }
-            boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
-            if (!connected) {
-                mActivity.finish();
-            }
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
-        mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
-    }
-
-    @Override
-    protected void onStop() {
-        if (mDisconnectedReceiver != null) {
-            mBroadcastDispatcher.unregisterReceiver(mDisconnectedReceiver);
-        }
-        super.onStop();
-    }
-
     @Override
     public void onClick(DialogInterface dialog, int which) {
         boolean allow = (which == AlertDialog.BUTTON_POSITIVE);
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
index 032b72e..4214242 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
@@ -16,41 +16,19 @@
 
 package com.android.systemui.usb;
 
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
 import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.hardware.usb.UsbManager;
 import android.os.Bundle;
-import android.os.SystemProperties;
 
 import com.android.internal.app.AlertActivity;
 import com.android.internal.app.AlertController;
 import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-
-import javax.inject.Inject;
 
 public class UsbDebuggingSecondaryUserActivity extends AlertActivity
         implements DialogInterface.OnClickListener {
-    private UsbDisconnectedReceiver mDisconnectedReceiver;
-    private final BroadcastDispatcher mBroadcastDispatcher;
-
-    @Inject
-    public UsbDebuggingSecondaryUserActivity(BroadcastDispatcher broadcastDispatcher) {
-        mBroadcastDispatcher = broadcastDispatcher;
-    }
-
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
-            mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
-        }
-
         final AlertController.AlertParams ap = mAlertParams;
         ap.mTitle = getString(R.string.usb_debugging_secondary_user_title);
         ap.mMessage = getString(R.string.usb_debugging_secondary_user_message);
@@ -60,40 +38,6 @@
         setupAlert();
     }
 
-    private class UsbDisconnectedReceiver extends BroadcastReceiver {
-        private final Activity mActivity;
-        public UsbDisconnectedReceiver(Activity activity) {
-            mActivity = activity;
-        }
-
-        @Override
-        public void onReceive(Context content, Intent intent) {
-            String action = intent.getAction();
-            if (UsbManager.ACTION_USB_STATE.equals(action)) {
-                boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
-                if (!connected) {
-                    mActivity.finish();
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
-        mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
-    }
-
-    @Override
-    protected void onStop() {
-        if (mDisconnectedReceiver != null) {
-            mBroadcastDispatcher.unregisterReceiver(mDisconnectedReceiver);
-        }
-        super.onStop();
-    }
-
     @Override
     public void onClick(DialogInterface dialog, int which) {
         finish();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 5ed8b8f..e121001 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -21,7 +21,11 @@
 import android.provider.Settings.Global;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.plugins.VolumeDialogController.State;
 
@@ -37,7 +41,7 @@
     public static final int EVENT_DISMISS_DIALOG = 1; // (reason|int)
     public static final int EVENT_ACTIVE_STREAM_CHANGED = 2; // (stream|int)
     public static final int EVENT_EXPAND = 3; // (expand|bool)
-    public static final int EVENT_KEY = 4;
+    public static final int EVENT_KEY = 4; // (stream|int) (lastAudibleStreamVolume)
     public static final int EVENT_COLLECTION_STARTED = 5;
     public static final int EVENT_COLLECTION_STOPPED = 6;
     public static final int EVENT_ICON_CLICK = 7; // (stream|int) (icon_state|int)
@@ -49,7 +53,7 @@
     public static final int EVENT_ZEN_MODE_CHANGED = 13; // (mode|int)
     public static final int EVENT_SUPPRESSOR_CHANGED = 14;  // (component|string) (name|string)
     public static final int EVENT_MUTE_CHANGED = 15;  // (stream|int) (muted|bool)
-    public static final int EVENT_TOUCH_LEVEL_DONE = 16;  // (stream|int) (level|bool)
+    public static final int EVENT_TOUCH_LEVEL_DONE = 16;  // (stream|int) (level|int)
     public static final int EVENT_ZEN_CONFIG_CHANGED = 17; // (allow/disallow|string)
     public static final int EVENT_RINGER_TOGGLE = 18; // (ringer_mode)
     public static final int EVENT_SHOW_USB_OVERHEAT_ALARM = 19; // (reason|int) (keyguard|bool)
@@ -122,105 +126,365 @@
     public static final int ICON_STATE_MUTE = 2;
     public static final int ICON_STATE_VIBRATE = 3;
 
+    @VisibleForTesting
+    public enum VolumeDialogOpenEvent implements UiEventLogger.UiEventEnum {
+        //TODO zap the lock/unlock distinction
+        INVALID(0),
+        @UiEvent(doc = "The volume dialog was shown because the volume changed")
+        VOLUME_DIALOG_SHOW_VOLUME_CHANGED(128),
+        @UiEvent(doc = "The volume dialog was shown because the volume changed remotely")
+        VOLUME_DIALOG_SHOW_REMOTE_VOLUME_CHANGED(129),
+        @UiEvent(doc = "The volume dialog was shown because the usb high temperature alarm changed")
+        VOLUME_DIALOG_SHOW_USB_TEMP_ALARM_CHANGED(130);
+
+        private final int mId;
+        VolumeDialogOpenEvent(int id) {
+            mId = id;
+        }
+        public int getId() {
+            return mId;
+        }
+        static VolumeDialogOpenEvent fromReasons(int reason) {
+            switch (reason) {
+                case SHOW_REASON_VOLUME_CHANGED:
+                    return VOLUME_DIALOG_SHOW_VOLUME_CHANGED;
+                case SHOW_REASON_REMOTE_VOLUME_CHANGED:
+                    return VOLUME_DIALOG_SHOW_REMOTE_VOLUME_CHANGED;
+                case SHOW_REASON_USB_OVERHEAD_ALARM_CHANGED:
+                    return VOLUME_DIALOG_SHOW_USB_TEMP_ALARM_CHANGED;
+            }
+            return INVALID;
+        }
+    }
+
+    @VisibleForTesting
+    public enum VolumeDialogCloseEvent implements UiEventLogger.UiEventEnum {
+        INVALID(0),
+        @UiEvent(doc = "The volume dialog was dismissed because of a touch outside the dialog")
+        VOLUME_DIALOG_DISMISS_TOUCH_OUTSIDE(134),
+        @UiEvent(doc = "The system asked the volume dialog to close, e.g. for a navigation bar "
+                 + "touch, or ActivityManager ACTION_CLOSE_SYSTEM_DIALOGS broadcast.")
+        VOLUME_DIALOG_DISMISS_SYSTEM(135),
+        @UiEvent(doc = "The volume dialog was dismissed because it timed out")
+        VOLUME_DIALOG_DISMISS_TIMEOUT(136),
+        @UiEvent(doc = "The volume dialog was dismissed because the screen turned off")
+        VOLUME_DIALOG_DISMISS_SCREEN_OFF(137),
+        @UiEvent(doc = "The volume dialog was dismissed because the settings icon was clicked")
+        VOLUME_DIALOG_DISMISS_SETTINGS(138),
+        // reserving 139 for DISMISS_REASON_DONE_CLICKED which is currently unused
+        @UiEvent(doc = "The volume dialog was dismissed because the stream no longer exists")
+        VOLUME_DIALOG_DISMISS_STREAM_GONE(140),
+        // reserving 141 for DISMISS_REASON_OUTPUT_CHOOSER which is currently unused
+        @UiEvent(doc = "The volume dialog was dismissed because the usb high temperature alarm "
+                 + "changed")
+        VOLUME_DIALOG_DISMISS_USB_TEMP_ALARM_CHANGED(142);
+
+        private final int mId;
+        VolumeDialogCloseEvent(int id) {
+            mId = id;
+        }
+        public int getId() {
+            return mId;
+        }
+
+        static VolumeDialogCloseEvent fromReason(int reason) {
+            switch (reason) {
+                case DISMISS_REASON_TOUCH_OUTSIDE:
+                    return VOLUME_DIALOG_DISMISS_TOUCH_OUTSIDE;
+                case DISMISS_REASON_VOLUME_CONTROLLER:
+                    return VOLUME_DIALOG_DISMISS_SYSTEM;
+                case DISMISS_REASON_TIMEOUT:
+                    return VOLUME_DIALOG_DISMISS_TIMEOUT;
+                case DISMISS_REASON_SCREEN_OFF:
+                    return VOLUME_DIALOG_DISMISS_SCREEN_OFF;
+                case DISMISS_REASON_SETTINGS_CLICKED:
+                    return VOLUME_DIALOG_DISMISS_SETTINGS;
+                case DISMISS_STREAM_GONE:
+                    return VOLUME_DIALOG_DISMISS_STREAM_GONE;
+                case DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED:
+                    return VOLUME_DIALOG_DISMISS_USB_TEMP_ALARM_CHANGED;
+            }
+            return INVALID;
+        }
+    }
+
+    @VisibleForTesting
+    public enum VolumeDialogEvent implements UiEventLogger.UiEventEnum {
+        INVALID(0),
+        @UiEvent(doc = "The volume dialog settings icon was clicked")
+        VOLUME_DIALOG_SETTINGS_CLICK(143),
+        @UiEvent(doc = "The volume dialog details were expanded")
+        VOLUME_DIALOG_EXPAND_DETAILS(144),
+        @UiEvent(doc = "The volume dialog details were collapsed")
+        VOLUME_DIALOG_COLLAPSE_DETAILS(145),
+        @UiEvent(doc = "The active audio stream changed")
+        VOLUME_DIALOG_ACTIVE_STREAM_CHANGED(146),
+        @UiEvent(doc = "The audio stream was muted via icon")
+        VOLUME_DIALOG_MUTE_STREAM(147),
+        @UiEvent(doc = "The audio stream was unmuted via icon")
+        VOLUME_DIALOG_UNMUTE_STREAM(148),
+        @UiEvent(doc = "The audio stream was set to vibrate via icon")
+        VOLUME_DIALOG_TO_VIBRATE_STREAM(149),
+        @UiEvent(doc = "The audio stream was set to non-silent via slider")
+        VOLUME_DIALOG_SLIDER(150),
+        @UiEvent(doc = "The audio stream was set to silent via slider")
+        VOLUME_DIALOG_SLIDER_TO_ZERO(151),
+        @UiEvent(doc = "The audio volume was adjusted to silent via key")
+        VOLUME_KEY_TO_ZERO(152),
+        @UiEvent(doc = "The audio volume was adjusted to non-silent via key")
+        VOLUME_KEY(153),
+        @UiEvent(doc = "The ringer mode was toggled to silent")
+        RINGER_MODE_SILENT(154),
+        @UiEvent(doc = "The ringer mode was toggled to vibrate")
+        RINGER_MODE_VIBRATE(155),
+        @UiEvent(doc = "The ringer mode was toggled to normal")
+        RINGER_MODE_NORMAL(156),
+        @UiEvent(doc = "USB Overheat alarm was raised")
+        USB_OVERHEAT_ALARM(160),
+        @UiEvent(doc = "USB Overheat alarm was dismissed")
+        USB_OVERHEAT_ALARM_DISMISSED(161);
+
+        private final int mId;
+
+        VolumeDialogEvent(int id) {
+            mId = id;
+        }
+
+        public int getId() {
+            return mId;
+        }
+
+        static VolumeDialogEvent fromIconState(int iconState) {
+            switch (iconState) {
+                case ICON_STATE_UNMUTE:
+                    return VOLUME_DIALOG_UNMUTE_STREAM;
+                case ICON_STATE_MUTE:
+                    return VOLUME_DIALOG_MUTE_STREAM;
+                case ICON_STATE_VIBRATE:
+                    return VOLUME_DIALOG_TO_VIBRATE_STREAM;
+                default:
+                    return INVALID;
+            }
+        }
+
+        static VolumeDialogEvent fromSliderLevel(int level) {
+            return level == 0 ? VOLUME_DIALOG_SLIDER_TO_ZERO : VOLUME_DIALOG_SLIDER;
+        }
+
+        static VolumeDialogEvent fromKeyLevel(int level) {
+            return level == 0 ? VOLUME_KEY_TO_ZERO : VOLUME_KEY;
+        }
+
+        static VolumeDialogEvent fromRingerMode(int ringerMode) {
+            switch (ringerMode) {
+                case AudioManager.RINGER_MODE_SILENT:
+                    return RINGER_MODE_SILENT;
+                case AudioManager.RINGER_MODE_VIBRATE:
+                    return RINGER_MODE_VIBRATE;
+                case AudioManager.RINGER_MODE_NORMAL:
+                    return RINGER_MODE_NORMAL;
+                default:
+                    return INVALID;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public enum ZenModeEvent implements UiEventLogger.UiEventEnum {
+        INVALID(0),
+        @UiEvent(doc = "Zen (do not disturb) mode was toggled to off")
+        ZEN_MODE_OFF(156),
+        @UiEvent(doc = "Zen (do not disturb) mode was toggled to important interruptions only")
+        ZEN_MODE_IMPORTANT_ONLY(157),
+        @UiEvent(doc = "Zen (do not disturb) mode was toggled to alarms only")
+        ZEN_MODE_ALARMS_ONLY(158),
+        @UiEvent(doc = "Zen (do not disturb) mode was toggled to block all interruptions")
+        ZEN_MODE_NO_INTERRUPTIONS(159);
+
+        private final int mId;
+        ZenModeEvent(int id) {
+            mId = id;
+        }
+        public int getId() {
+            return mId;
+        }
+
+        static ZenModeEvent fromZenMode(int zenMode) {
+            switch (zenMode) {
+                case Global.ZEN_MODE_OFF: return ZEN_MODE_OFF;
+                case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return ZEN_MODE_IMPORTANT_ONLY;
+                case Global.ZEN_MODE_ALARMS: return ZEN_MODE_ALARMS_ONLY;
+                case Global.ZEN_MODE_NO_INTERRUPTIONS: return ZEN_MODE_NO_INTERRUPTIONS;
+                default: return INVALID;
+            }
+        }
+    }
+
     public static Callback sCallback;
+    @VisibleForTesting
+    static MetricsLogger sLegacyLogger = new MetricsLogger();
+    @VisibleForTesting
+    static UiEventLogger sUiEventLogger = new UiEventLoggerImpl();
 
     /**
-     * Logs an event to the system log and the event log.
+     * Logs an event to the system log, to sCallback if present, and to the logEvent destinations.
      * @param tag One of the EVENT_* codes above.
      * @param list Any additional event-specific arguments, documented above.
      */
     public static void writeEvent(int tag, Object... list) {
-        MetricsLogger logger = new MetricsLogger();
         final long time = System.currentTimeMillis();
-        final StringBuilder sb = new StringBuilder("writeEvent ").append(EVENT_TAGS[tag]);
-        if (list != null && list.length > 0) {
-            sb.append(" ");
-            switch (tag) {
-                case EVENT_SHOW_DIALOG:
-                    logger.visible(MetricsEvent.VOLUME_DIALOG);
-                    logger.histogram("volume_from_keyguard",
-                            (Boolean) list[1] ? 1 : 0);
-                    sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
-                    break;
-                case EVENT_EXPAND:
-                    logger.visibility(MetricsEvent.VOLUME_DIALOG_DETAILS,
-                            (Boolean) list[0]);
-                    sb.append(list[0]);
-                    break;
-                case EVENT_DISMISS_DIALOG:
-                    logger.hidden(MetricsEvent.VOLUME_DIALOG);
-                    sb.append(DISMISS_REASONS[(Integer) list[0]]);
-                    break;
-                case EVENT_ACTIVE_STREAM_CHANGED:
-                    logger.action(MetricsEvent.ACTION_VOLUME_STREAM,
-                            (Integer) list[0]);
-                    sb.append(AudioSystem.streamToString((Integer) list[0]));
-                    break;
-                case EVENT_ICON_CLICK:
-                    logger.action(MetricsEvent.ACTION_VOLUME_ICON,
-                            (Integer) list[0]);
-                    sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
-                            .append(iconStateToString((Integer) list[1]));
-                    break;
-                case EVENT_TOUCH_LEVEL_DONE:
-                    logger.action(MetricsEvent.ACTION_VOLUME_SLIDER,
-                            (Integer) list[1]);
-                    // fall through
-                case EVENT_TOUCH_LEVEL_CHANGED:
-                case EVENT_LEVEL_CHANGED:
-                case EVENT_MUTE_CHANGED:
-                    sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
-                            .append(list[1]);
-                    break;
-                case EVENT_KEY:
-                    logger.action(MetricsEvent.ACTION_VOLUME_KEY,
-                            (Integer) list[0]);
-                    sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
-                            .append(list[1]);
-                    break;
-                case EVENT_RINGER_TOGGLE:
-                    logger.action(MetricsEvent.ACTION_VOLUME_RINGER_TOGGLE, (Integer) list[0]);
-                    break;
-                case EVENT_SETTINGS_CLICK:
-                    logger.action(MetricsEvent.ACTION_VOLUME_SETTINGS);
-                    break;
-                case EVENT_EXTERNAL_RINGER_MODE_CHANGED:
-                    logger.action(MetricsEvent.ACTION_RINGER_MODE,
-                            (Integer) list[0]);
-                    // fall through
-                case EVENT_INTERNAL_RINGER_MODE_CHANGED:
-                    sb.append(ringerModeToString((Integer) list[0]));
-                    break;
-                case EVENT_ZEN_MODE_CHANGED:
-                    sb.append(zenModeToString((Integer) list[0]));
-                    break;
-                case EVENT_SUPPRESSOR_CHANGED:
-                    sb.append(list[0]).append(' ').append(list[1]);
-                    break;
-                case EVENT_SHOW_USB_OVERHEAT_ALARM:
-                    logger.visible(MetricsEvent.POWER_OVERHEAT_ALARM);
-                    logger.histogram("show_usb_overheat_alarm",
-                            (Boolean) list[1] ? 1 : 0);
-                    sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
-                    break;
-                case EVENT_DISMISS_USB_OVERHEAT_ALARM:
-                    logger.hidden(MetricsEvent.POWER_OVERHEAT_ALARM);
-                    logger.histogram("dismiss_usb_overheat_alarm",
-                            (Boolean) list[1] ? 1 : 0);
-                    sb.append(DISMISS_REASONS[(Integer) list[0]])
-                        .append(" keyguard=").append(list[1]);
-                    break;
-                default:
-                    sb.append(Arrays.asList(list));
-                    break;
-            }
-        }
-        Log.i(TAG, sb.toString());
+        Log.i(TAG, logEvent(tag, list));
         if (sCallback != null) {
             sCallback.writeEvent(time, tag, list);
         }
     }
 
+    /**
+     * Logs an event to the event log and UiEvent (Westworld) logging. Compare writeEvent, which
+     * adds more log destinations.
+     * @param tag One of the EVENT_* codes above.
+     * @param list Any additional event-specific arguments, documented above.
+     * @return String a readable description of the event.  Begins "writeEvent <tag_description>"
+     * if the tag is valid.
+     */
+    public static String logEvent(int tag, Object... list) {
+        if (tag >= EVENT_TAGS.length) {
+            return "";
+        }
+        final StringBuilder sb = new StringBuilder("writeEvent ").append(EVENT_TAGS[tag]);
+        // Handle events without extra data
+        if (list == null || list.length == 0) {
+            if (tag == EVENT_SETTINGS_CLICK) {
+                sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_SETTINGS);
+                sUiEventLogger.log(VolumeDialogEvent.VOLUME_DIALOG_SETTINGS_CLICK);
+            }
+            return sb.toString();
+        }
+        // Handle events with extra data. We've established list[0] exists.
+        sb.append(" ");
+        switch (tag) {
+            case EVENT_SHOW_DIALOG:
+                sLegacyLogger.visible(MetricsEvent.VOLUME_DIALOG);
+                if (list.length > 1) {
+                    final Integer reason = (Integer) list[0];
+                    final Boolean keyguard = (Boolean) list[1];
+                    sLegacyLogger.histogram("volume_from_keyguard", keyguard ? 1 : 0);
+                    sUiEventLogger.log(VolumeDialogOpenEvent.fromReasons(reason));
+                    sb.append(SHOW_REASONS[reason]).append(" keyguard=").append(keyguard);
+                }
+                break;
+            case EVENT_EXPAND: {
+                final Boolean expand = (Boolean) list[0];
+                sLegacyLogger.visibility(MetricsEvent.VOLUME_DIALOG_DETAILS, expand);
+                sUiEventLogger.log(expand ? VolumeDialogEvent.VOLUME_DIALOG_EXPAND_DETAILS
+                        : VolumeDialogEvent.VOLUME_DIALOG_COLLAPSE_DETAILS);
+                sb.append(expand);
+                break;
+            }
+            case EVENT_DISMISS_DIALOG: {
+                sLegacyLogger.hidden(MetricsEvent.VOLUME_DIALOG);
+                final Integer reason = (Integer) list[0];
+                sUiEventLogger.log(VolumeDialogCloseEvent.fromReason(reason));
+                sb.append(DISMISS_REASONS[reason]);
+                break;
+            }
+            case EVENT_ACTIVE_STREAM_CHANGED: {
+                final Integer stream = (Integer) list[0];
+                sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_STREAM, stream);
+                sUiEventLogger.log(VolumeDialogEvent.VOLUME_DIALOG_ACTIVE_STREAM_CHANGED);
+                sb.append(AudioSystem.streamToString(stream));
+                break;
+            }
+            case EVENT_ICON_CLICK:
+                if (list.length > 1) {
+                    final Integer stream = (Integer) list[0];
+                    sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_ICON, stream);
+                    final Integer iconState = (Integer) list[1];
+                    sUiEventLogger.log(VolumeDialogEvent.fromIconState(iconState));
+                    sb.append(AudioSystem.streamToString(stream)).append(' ')
+                            .append(iconStateToString(iconState));
+                }
+                break;
+            case EVENT_TOUCH_LEVEL_DONE: // (stream|int) (level|int)
+                if (list.length > 1) {
+                    final Integer level = (Integer) list[1];
+                    sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_SLIDER, level);
+                    sUiEventLogger.log(VolumeDialogEvent.fromSliderLevel(level));
+                }
+                // fall through
+            case EVENT_TOUCH_LEVEL_CHANGED:
+            case EVENT_LEVEL_CHANGED:
+            case EVENT_MUTE_CHANGED:  // (stream|int) (level|int)
+                if (list.length > 1) {
+                    sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
+                            .append(list[1]);
+                }
+                break;
+            case EVENT_KEY: // (stream|int) (lastAudibleStreamVolume)
+                if (list.length > 1) {
+                    final Integer stream = (Integer) list[0];
+                    sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_KEY, stream);
+                    final Integer level = (Integer) list[1];
+                    sUiEventLogger.log(VolumeDialogEvent.fromKeyLevel(level));
+                    sb.append(AudioSystem.streamToString(stream)).append(' ').append(level);
+                }
+                break;
+            case EVENT_RINGER_TOGGLE: {
+                final Integer ringerMode = (Integer) list[0];
+                sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_RINGER_TOGGLE, ringerMode);
+                sUiEventLogger.log(VolumeDialogEvent.fromRingerMode(ringerMode));
+                sb.append(ringerModeToString(ringerMode));
+                break;
+            }
+            case EVENT_EXTERNAL_RINGER_MODE_CHANGED: {
+                final Integer ringerMode = (Integer) list[0];
+                sLegacyLogger.action(MetricsEvent.ACTION_RINGER_MODE, ringerMode);
+            }
+                // fall through
+            case EVENT_INTERNAL_RINGER_MODE_CHANGED: {
+                final Integer ringerMode = (Integer) list[0];
+                sb.append(ringerModeToString(ringerMode));
+                break;
+            }
+            case EVENT_ZEN_MODE_CHANGED: {
+                final Integer zenMode = (Integer) list[0];
+                sb.append(zenModeToString(zenMode));
+                sUiEventLogger.log(ZenModeEvent.fromZenMode(zenMode));
+                break;
+            }
+            case EVENT_SUPPRESSOR_CHANGED:  // (component|string) (name|string)
+                if (list.length > 1) {
+                    sb.append(list[0]).append(' ').append(list[1]);
+                }
+                break;
+            case EVENT_SHOW_USB_OVERHEAT_ALARM:
+                sLegacyLogger.visible(MetricsEvent.POWER_OVERHEAT_ALARM);
+                sUiEventLogger.log(VolumeDialogEvent.USB_OVERHEAT_ALARM);
+                if (list.length > 1) {
+                    final Boolean keyguard = (Boolean) list[1];
+                    sLegacyLogger.histogram("show_usb_overheat_alarm", keyguard ? 1 : 0);
+                    final Integer reason = (Integer) list[0];
+                    sb.append(SHOW_REASONS[reason]).append(" keyguard=").append(keyguard);
+                }
+                break;
+            case EVENT_DISMISS_USB_OVERHEAT_ALARM:
+                sLegacyLogger.hidden(MetricsEvent.POWER_OVERHEAT_ALARM);
+                sUiEventLogger.log(VolumeDialogEvent.USB_OVERHEAT_ALARM_DISMISSED);
+                if (list.length > 1) {
+                    final Boolean keyguard = (Boolean) list[1];
+                    sLegacyLogger.histogram("dismiss_usb_overheat_alarm", keyguard ? 1 : 0);
+                    final Integer reason = (Integer) list[0];
+                    sb.append(DISMISS_REASONS[reason])
+                            .append(" keyguard=").append(keyguard);
+                }
+                break;
+            default:
+                sb.append(Arrays.asList(list));
+                break;
+        }
+        return sb.toString();
+    }
+
     public static void writeState(long time, State state) {
         if (sCallback != null) {
             sCallback.writeState(time, state);
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 81e2c22..e5f56d4 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -32,7 +32,8 @@
 
 LOCAL_JNI_SHARED_LIBRARIES := \
     libdexmakerjvmtiagent \
-    libmultiplejvmtiagentsinterferenceagent
+    libmultiplejvmtiagentsinterferenceagent \
+    libstaticjvmtiagent
 
 LOCAL_JAVA_LIBRARIES := \
     android.test.runner \
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 e0d31d0..2ccecec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -94,8 +94,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import dagger.Lazy;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -153,7 +151,7 @@
     @Mock
     private Resources mResources;
     @Mock
-    private Lazy<ShadeController> mShadeController;
+    private ShadeController mShadeController;
     @Mock
     private RemoteInputUriController mRemoteInputUriController;
 
@@ -719,7 +717,7 @@
         TestableBubbleController(Context context,
                 StatusBarWindowController statusBarWindowController,
                 StatusBarStateController statusBarStateController,
-                Lazy<ShadeController> shadeController,
+                ShadeController shadeController,
                 BubbleData data,
                 ConfigurationController configurationController,
                 NotificationInterruptionStateProvider interruptionStateProvider,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java
new file mode 100644
index 0000000..c827ac7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.glwallpaper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ImageRevealHelperTest extends SysuiTestCase {
+
+    static final int ANIMATION_DURATION = 500;
+    ImageRevealHelper mImageRevealHelper;
+    ImageRevealHelper.RevealStateListener mRevealStateListener;
+
+    @Before
+    public void setUp() throws Exception {
+        mRevealStateListener = new ImageRevealHelper.RevealStateListener() {
+            @Override
+            public void onRevealStateChanged() {
+                // no-op
+            }
+
+            @Override
+            public void onRevealStart(boolean animate) {
+                // no-op
+            }
+
+            @Override
+            public void onRevealEnd() {
+                // no-op
+            }
+        };
+        mImageRevealHelper = new ImageRevealHelper(mRevealStateListener);
+    }
+
+    @Test
+    public void testBiometricAuthUnlockAnimateImageRevealState_shouldNotBlackoutScreen() {
+        assertThat(mImageRevealHelper.getReveal()).isEqualTo(0f);
+
+        mImageRevealHelper.updateAwake(true /* awake */, ANIMATION_DURATION);
+        assertThat(mImageRevealHelper.getReveal()).isEqualTo(0f);
+
+        // When device unlock through Biometric, should not show reveal transition
+        mImageRevealHelper.updateAwake(false /* awake */, 0);
+        assertThat(mImageRevealHelper.getReveal()).isEqualTo(1f);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index cbfcfdd..a8a2b33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -68,15 +68,14 @@
 
         mDependency.injectTestDependency(FalsingManager.class, mFalsingManager);
         mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mUpdateMonitor);
-        mDependency.injectTestDependency(StatusBarWindowController.class,
-                mStatusBarWindowController);
 
         when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
 
         TestableLooper.get(this).runWithLooper(() -> {
             mViewMediator = new KeyguardViewMediator(
                     mContext, mFalsingManager, mLockPatternUtils, mBroadcastDispatcher,
-                    () -> mStatusBarKeyguardViewManager, mDismissCallbackRegistry);
+                    mStatusBarWindowController, () -> mStatusBarKeyguardViewManager,
+                    mDismissCallbackRegistry);
         });
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java
index 2f90641..4a90bb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java
@@ -57,7 +57,7 @@
 
     class TestableRichEvent extends RichEvent {
         TestableRichEvent(int logLevel, int type, String reason) {
-            super(logLevel, type, reason);
+            init(logLevel, type, reason);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
index 1e8ebea..e7b317e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
@@ -35,11 +35,12 @@
 @RunWith(AndroidTestingRunner.class)
 public class SysuiLogTest extends SysuiTestCase {
     private static final String TEST_ID = "TestLogger";
+    private static final String TEST_MSG = "msg";
     private static final int MAX_LOGS = 5;
 
     @Mock
     private DumpController mDumpController;
-    private SysuiLog mSysuiLog;
+    private SysuiLog<Event> mSysuiLog;
 
     @Before
     public void setup() {
@@ -48,35 +49,63 @@
 
     @Test
     public void testLogDisabled_noLogsWritten() {
-        mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, false, false);
-        assertEquals(mSysuiLog.mTimeline, null);
+        mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, false);
+        assertEquals(null, mSysuiLog.mTimeline);
 
-        mSysuiLog.log(new Event("msg"));
-        assertEquals(mSysuiLog.mTimeline, null);
+        mSysuiLog.log(createEvent(TEST_MSG));
+        assertEquals(null, mSysuiLog.mTimeline);
     }
 
     @Test
     public void testLogEnabled_logWritten() {
-        mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, true, false);
-        assertEquals(mSysuiLog.mTimeline.size(), 0);
+        mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
+        assertEquals(0, mSysuiLog.mTimeline.size());
 
-        mSysuiLog.log(new Event("msg"));
-        assertEquals(mSysuiLog.mTimeline.size(), 1);
+        mSysuiLog.log(createEvent(TEST_MSG));
+        assertEquals(1, mSysuiLog.mTimeline.size());
     }
 
     @Test
     public void testMaxLogs() {
-        mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, true, false);
+        mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
         assertEquals(mSysuiLog.mTimeline.size(), 0);
 
-        final String msg = "msg";
         for (int i = 0; i < MAX_LOGS + 1; i++) {
-            mSysuiLog.log(new Event(msg + i));
+            mSysuiLog.log(createEvent(TEST_MSG + i));
         }
 
-        assertEquals(mSysuiLog.mTimeline.size(), MAX_LOGS);
+        assertEquals(MAX_LOGS, mSysuiLog.mTimeline.size());
 
-        // check the first message (msg0) is deleted:
-        assertEquals(mSysuiLog.mTimeline.getFirst().getMessage(), msg + "1");
+        // check the first message (msg0) was replaced with msg1:
+        assertEquals(TEST_MSG + "1", mSysuiLog.mTimeline.getFirst().getMessage());
+    }
+
+    @Test
+    public void testRecycleLogs() {
+        // GIVEN a SysuiLog with one log
+        mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
+        Event e = createEvent(TEST_MSG); // msg
+        mSysuiLog.log(e); // Logs: [msg]
+
+        Event recycledEvent = null;
+        // WHEN we add MAX_LOGS after the first log
+        for (int i = 0; i < MAX_LOGS; i++) {
+            recycledEvent = mSysuiLog.log(createEvent(TEST_MSG + i));
+        }
+        // Logs: [msg1, msg2, msg3, msg4]
+
+        // THEN we see the recycledEvent is e
+        assertEquals(e, recycledEvent);
+    }
+
+    private Event createEvent(String msg) {
+        return new Event().init(msg);
+    }
+
+    public class TestSysuiLog extends SysuiLog<Event> {
+        protected TestSysuiLog(DumpController dumpController, String id, int maxLogs,
+                boolean enabled) {
+            super(dumpController, id, maxLogs, enabled, false);
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 39ce8c1..8a9a7a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -120,7 +120,6 @@
         ).when(mQSTileHost).createTile(anyString());
 
         FakeSystemClock clock = new FakeSystemClock();
-        clock.setAutoIncrement(false);
         mMainExecutor = new FakeExecutor(clock);
         mBgExecutor = new FakeExecutor(clock);
         mTileQueryHelper = new TileQueryHelper(mContext, mMainExecutor, mBgExecutor);
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 46a8dad..07d2e31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -53,7 +53,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.util.Assert;
 
 import com.google.android.collect.Lists;
@@ -79,7 +78,6 @@
     @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
     @Mock private NotificationGroupManager mGroupManager;
     @Mock private VisualStabilityManager mVisualStabilityManager;
-    @Mock private ShadeController mShadeController;
 
     private TestableLooper mTestableLooper;
     private Handler mHandler;
@@ -99,14 +97,12 @@
                 mLockscreenUserManager);
         mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
         mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
-        mDependency.injectTestDependency(ShadeController.class, mShadeController);
 
         mHelper = new NotificationTestHelper(mContext, mDependency);
 
         mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
                 mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
                 mock(StatusBarStateControllerImpl.class), mEntryManager,
-                () -> mShadeController,
                 mock(KeyguardBypassController.class),
                 mock(BubbleController.class),
                 mock(DynamicPrivacyController.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
index a25af84..bbabb11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
@@ -47,6 +47,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.SectionsProvider;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -76,6 +77,7 @@
     private NotifListBuilderImpl mListBuilder;
     private FakeSystemClock mSystemClock = new FakeSystemClock();
 
+    @Mock private NotifLog mNotifLog;
     @Mock private NotifCollection mNotifCollection;
     @Spy private OnBeforeTransformGroupsListener mOnBeforeTransformGroupsListener;
     @Spy private OnBeforeSortListener mOnBeforeSortListener;
@@ -97,7 +99,7 @@
         MockitoAnnotations.initMocks(this);
         Assert.sMainLooper = TestableLooper.get(this).getLooper();
 
-        mListBuilder = new NotifListBuilderImpl(mSystemClock);
+        mListBuilder = new NotifListBuilderImpl(mSystemClock, mNotifLog);
         mListBuilder.setOnRenderListListener(mOnRenderListListener);
 
         mListBuilder.attach(mNotifCollection);
@@ -690,7 +692,6 @@
         mListBuilder.addFilter(filter3);
 
         // GIVEN the SystemClock is set to a particular time:
-        mSystemClock.setAutoIncrement(true);
         mSystemClock.setUptimeMillis(47);
 
         // WHEN the pipeline is kicked off on a list of notifs
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java
new file mode 100644
index 0000000..a9413c7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.coordinator;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.ActivityManagerInternal;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DeviceProvisionedCoordinatorTest extends SysuiTestCase {
+    private static final int NOTIF_UID = 0;
+
+    private static final String SHOW_WHEN_UNPROVISIONED_FLAG =
+            Notification.EXTRA_ALLOW_DURING_SETUP;
+    private static final String SETUP_NOTIF_PERMISSION =
+            Manifest.permission.NOTIFICATION_DURING_SETUP;
+
+    private MockitoSession mMockitoSession;
+
+    @Mock private ActivityManagerInternal mActivityMangerInternal;
+    @Mock private IPackageManager mIPackageManager;
+    @Mock private DeviceProvisionedController mDeviceProvisionedController;
+    @Mock private NotifListBuilderImpl mNotifListBuilder;
+    private Notification mNotification;
+    private NotificationEntry mEntry;
+    private DeviceProvisionedCoordinator mDeviceProvisionedCoordinator;
+    private NotifFilter mDeviceProvisionedFilter;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mDeviceProvisionedCoordinator = new DeviceProvisionedCoordinator(
+                mDeviceProvisionedController, mIPackageManager);
+
+        mNotification = new Notification();
+        mEntry = new NotificationEntryBuilder()
+                .setNotification(mNotification)
+                .setUid(NOTIF_UID)
+                .build();
+
+        ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
+        mDeviceProvisionedCoordinator.attach(null, mNotifListBuilder);
+        verify(mNotifListBuilder, times(1)).addFilter(filterCaptor.capture());
+        mDeviceProvisionedFilter = filterCaptor.getValue();
+    }
+
+    @Test
+    public void deviceProvisioned() {
+        // GIVEN device is provisioned
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
+
+        // THEN don't filter out the notification
+        assertFalse(mDeviceProvisionedFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void deviceUnprovisioned() {
+        // GIVEN device is unprovisioned
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(false);
+
+        // THEN filter out the notification
+        assertTrue(mDeviceProvisionedFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void deviceUnprovisionedCanBypass() throws RemoteException {
+        // GIVEN device is unprovisioned
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(false);
+
+        // GIVEN notification has a flag to allow the notification during setup
+        Bundle extras = new Bundle();
+        extras.putBoolean(SHOW_WHEN_UNPROVISIONED_FLAG, true);
+        mNotification.extras = extras;
+
+        // GIVEN notification has the permission to display during setup
+        when(mIPackageManager.checkUidPermission(SETUP_NOTIF_PERMISSION, NOTIF_UID))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        // THEN don't filter out the notification
+        assertFalse(mDeviceProvisionedFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void deviceUnprovisionedTryBypassWithoutPermission() throws RemoteException {
+        // GIVEN device is unprovisioned
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(false);
+
+        // GIVEN notification has a flag to allow the notification during setup
+        Bundle extras = new Bundle();
+        extras.putBoolean(SHOW_WHEN_UNPROVISIONED_FLAG, true);
+        mNotification.extras = extras;
+
+        // GIVEN notification does NOT have permission to display during setup
+        when(mIPackageManager.checkUidPermission(SETUP_NOTIF_PERMISSION, NOTIF_UID))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+
+        // THEN filter out the notification
+        assertTrue(mDeviceProvisionedFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    private RankingBuilder getRankingForUnfilteredNotif() {
+        return new RankingBuilder()
+                .setKey(mEntry.getKey())
+                .setSuppressedVisualEffects(0)
+                .setSuspended(false);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
new file mode 100644
index 0000000..ffaa335
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.coordinator;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ForegroundCoordinatorTest extends SysuiTestCase {
+    private static final String TEST_PKG = "test_pkg";
+    private static final int NOTIF_USER_ID = 0;
+
+    @Mock private Handler mMainHandler;
+    @Mock private ForegroundServiceController mForegroundServiceController;
+    @Mock private AppOpsController mAppOpsController;
+    @Mock private NotifListBuilderImpl mNotifListBuilder;
+    @Mock private NotifCollection mNotifCollection;
+
+    private NotificationEntry mEntry;
+    private Notification mNotification;
+    private ForegroundCoordinator mForegroundCoordinator;
+    private NotifFilter mForegroundFilter;
+    private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mForegroundCoordinator = new ForegroundCoordinator(
+                mForegroundServiceController, mAppOpsController, mMainHandler);
+
+        mNotification = new Notification();
+        mEntry = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID))
+                .setNotification(mNotification)
+                .build();
+
+        ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
+        ArgumentCaptor<NotifLifetimeExtender> lifetimeExtenderCaptor =
+                ArgumentCaptor.forClass(NotifLifetimeExtender.class);
+
+        mForegroundCoordinator.attach(mNotifCollection, mNotifListBuilder);
+        verify(mNotifListBuilder, times(1)).addFilter(filterCaptor.capture());
+        verify(mNotifCollection, times(1)).addNotificationLifetimeExtender(
+                lifetimeExtenderCaptor.capture());
+
+        mForegroundFilter = filterCaptor.getValue();
+        mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue();
+    }
+
+    @Test
+    public void filterTest_disclosureUnnecessary() {
+        StatusBarNotification sbn = mEntry.getSbn();
+
+        // GIVEN the notification is a disclosure notification
+        when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(true);
+
+        // GIVEN the disclosure isn't needed for this user
+        when(mForegroundServiceController.isDisclosureNeededForUser(sbn.getUserId()))
+                .thenReturn(false);
+
+        // THEN filter out the notification
+        assertTrue(mForegroundFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void filterTest_systemAlertNotificationUnnecessary() {
+        StatusBarNotification sbn = mEntry.getSbn();
+
+        // GIVEN the notification is a system alert notification + not a disclosure notification
+        when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(true);
+        when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false);
+
+        // GIVEN the alert notification isn't needed for this user
+        final Bundle extras = new Bundle();
+        extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS,
+                new String[]{TEST_PKG});
+        mNotification.extras = extras;
+        when(mForegroundServiceController.isSystemAlertWarningNeeded(sbn.getUserId(), TEST_PKG))
+                .thenReturn(false);
+
+        // THEN filter out the notification
+        assertTrue(mForegroundFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void filterTest_doNotFilter() {
+        StatusBarNotification sbn = mEntry.getSbn();
+
+        // GIVEN the notification isn't a system alert notification nor a disclosure notification
+        when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(false);
+        when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false);
+
+        // THEN don't filter out the notification
+        assertFalse(mForegroundFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void extendLifetimeText_notForeground() {
+        // GIVEN the notification doesn't represent a foreground service
+        mNotification.flags = 0;
+
+        // THEN don't extend the lifetime
+        assertFalse(mForegroundNotifLifetimeExtender
+                .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
+    }
+
+    @Test
+    public void extendLifetimeText_foregroundNotifRecentlyPosted() {
+        // GIVEN the notification represents a foreground service that was just posted
+        mNotification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        mEntry = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID))
+                .setSbn(new StatusBarNotification(TEST_PKG, TEST_PKG, NOTIF_USER_ID, "",
+                        NOTIF_USER_ID, NOTIF_USER_ID, mNotification,
+                        new UserHandle(NOTIF_USER_ID), "", System.currentTimeMillis()))
+                .setNotification(mNotification)
+                .build();
+
+        // THEN extend the lifetime
+        assertTrue(mForegroundNotifLifetimeExtender
+                .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
+    }
+
+    @Test
+    public void extendLifetimeText_foregroundNotifOld() {
+        // GIVEN the notification represents a foreground service that was posted 10 seconds ago
+        mNotification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        mEntry = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID))
+                .setSbn(new StatusBarNotification(TEST_PKG, TEST_PKG, NOTIF_USER_ID, "",
+                        NOTIF_USER_ID, NOTIF_USER_ID, mNotification,
+                        new UserHandle(NOTIF_USER_ID), "",
+                        System.currentTimeMillis() - 10000))
+                .setNotification(mNotification)
+                .build();
+
+        // THEN don't extend the lifetime because the extended time exceeds
+        // ForegroundCoordinator.MIN_FGS_TIME_MS
+        assertFalse(mForegroundNotifLifetimeExtender
+                .shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
index 87b3783d..527370e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -24,6 +24,8 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.os.Handler;
@@ -40,12 +42,15 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -61,14 +66,16 @@
     @Mock private BroadcastDispatcher mBroadcastDispatcher;
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock private NotifListBuilderImpl mNotifListBuilder;
 
     private NotificationEntry mEntry;
-    private KeyguardCoordinator mKeyguardNotificationCoordinator;
+    private KeyguardCoordinator mKeyguardCoordinator;
+    private NotifFilter mKeyguardFilter;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mKeyguardNotificationCoordinator = new KeyguardCoordinator(
+        mKeyguardCoordinator = new KeyguardCoordinator(
                 mContext, mMainHandler, mKeyguardStateController, mLockscreenUserManager,
                 mBroadcastDispatcher, mStatusBarStateController,
                 mKeyguardUpdateMonitor);
@@ -76,6 +83,11 @@
         mEntry = new NotificationEntryBuilder()
                 .setUser(new UserHandle(NOTIF_USER_ID))
                 .build();
+
+        ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
+        mKeyguardCoordinator.attach(null, mNotifListBuilder);
+        verify(mNotifListBuilder, times(1)).addFilter(filterCaptor.capture());
+        mKeyguardFilter = filterCaptor.getValue();
     }
 
     @Test
@@ -84,7 +96,7 @@
         setupUnfilteredState();
 
         // THEN don't filter out the entry
-        assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+        assertFalse(mKeyguardFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -94,7 +106,7 @@
         when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(false);
 
         // THEN filter out the entry
-        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mKeyguardFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -104,7 +116,7 @@
         when(mKeyguardStateController.isShowing()).thenReturn(false);
 
         // THEN don't filter out the entry
-        assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+        assertFalse(mKeyguardFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -116,7 +128,7 @@
         when(mLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false);
 
         // THEN filter out the entry
-        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mKeyguardFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -128,7 +140,7 @@
         when(mKeyguardUpdateMonitor.isUserInLockdown(NOTIF_USER_ID)).thenReturn(true);
 
         // THEN filter out the entry
-        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mKeyguardFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -143,7 +155,7 @@
                 .thenReturn(false);
 
         // THEN filter out the entry
-        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mKeyguardFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -159,7 +171,7 @@
                 .setVisibilityOverride(VISIBILITY_SECRET).build());
 
         // THEN filter out the entry
-        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mKeyguardFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -174,7 +186,7 @@
                 .build());
 
         // THEN filter out the entry
-        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mKeyguardFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -199,7 +211,7 @@
         mEntry.setParent(group);
 
         // THEN don't filter out the entry
-        assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+        assertFalse(mKeyguardFilter.shouldFilterOut(mEntry, 0));
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
new file mode 100644
index 0000000..182e866
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.coordinator;
+
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class RankingCoordinatorTest extends SysuiTestCase {
+
+    @Mock private StatusBarStateController mStatusBarStateController;
+    @Mock private NotifListBuilderImpl mNotifListBuilder;
+    private NotificationEntry mEntry;
+    private RankingCoordinator mRankingCoordinator;
+    private NotifFilter mRankingFilter;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mRankingCoordinator = new RankingCoordinator(mStatusBarStateController);
+        mEntry = new NotificationEntryBuilder().build();
+
+        ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
+        mRankingCoordinator.attach(null, mNotifListBuilder);
+        verify(mNotifListBuilder, times(1)).addFilter(filterCaptor.capture());
+        mRankingFilter = filterCaptor.getValue();
+    }
+
+    @Test
+    public void testUnfilteredState() {
+        // GIVEN no suppressed visual effects + app not suspended
+        mEntry.setRanking(getRankingForUnfilteredNotif().build());
+
+        // THEN don't filter out the notification
+        assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void filterSuspended() {
+        // GIVEN the notification's app is suspended
+        mEntry.setRanking(getRankingForUnfilteredNotif()
+                .setSuspended(true)
+                .build());
+
+        // THEN filter out the notification
+        assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void filterDozingSuppressAmbient() {
+        // GIVEN should suppress ambient
+        mEntry.setRanking(getRankingForUnfilteredNotif()
+                .setSuppressedVisualEffects(SUPPRESSED_EFFECT_AMBIENT)
+                .build());
+
+        // WHEN it's dozing (on ambient display)
+        when(mStatusBarStateController.isDozing()).thenReturn(true);
+
+        // THEN filter out the notification
+        assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+
+        // WHEN it's not dozing (showing the notification list)
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+
+        // THEN don't filter out the notification
+        assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void filterDozingSuppressNotificationList() {
+        // GIVEN should suppress from the notification list
+        mEntry.setRanking(getRankingForUnfilteredNotif()
+                .setSuppressedVisualEffects(SUPPRESSED_EFFECT_NOTIFICATION_LIST)
+                .build());
+
+        // WHEN it's dozing (on ambient display)
+        when(mStatusBarStateController.isDozing()).thenReturn(true);
+
+        // THEN don't filter out the notification
+        assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+
+        // WHEN it's not dozing (showing the notification list)
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+
+        // THEN filter out the notification
+        assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    private RankingBuilder getRankingForUnfilteredNotif() {
+        return new RankingBuilder()
+                .setKey(mEntry.getKey())
+                .setSuppressedVisualEffects(0)
+                .setSuspended(false);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 4451fa4..5907a0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -76,6 +77,8 @@
     @Mock
     private StatusBar mStatusBar;
     @Mock
+    private ShadeController mShadeController;
+    @Mock
     private KeyguardStateController mKeyguardStateController;
     @Mock
     private Handler mHandler;
@@ -98,13 +101,12 @@
         when(mKeyguardBypassController.canPlaySubtleWindowAnimations()).thenReturn(true);
         mContext.addMockSystemService(PowerManager.class, mPowerManager);
         mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
-        mDependency.injectTestDependency(StatusBarWindowController.class,
-                mStatusBarWindowController);
         res.addOverride(com.android.internal.R.integer.config_wakeUpDelayDoze, 0);
         mBiometricUnlockController = new BiometricUnlockController(mContext, mDozeScrimController,
-                mKeyguardViewMediator, mScrimController, mStatusBar, mKeyguardStateController,
-                mHandler, mUpdateMonitor, res.getResources(), mKeyguardBypassController,
-                mDozeParameters, mMetricsLogger, mDumpController);
+                mKeyguardViewMediator, mScrimController, mStatusBar, mShadeController,
+                mStatusBarWindowController, mKeyguardStateController, mHandler, mUpdateMonitor,
+                res.getResources(), mKeyguardBypassController, mDozeParameters, mMetricsLogger,
+                mDumpController);
         mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
     }
 
@@ -113,7 +115,8 @@
         mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                 BiometricSourceType.FINGERPRINT);
         verify(mStatusBarKeyguardViewManager).showBouncer(eq(false));
-        verify(mStatusBarKeyguardViewManager).animateCollapsePanels(anyFloat());
+        verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(),
+                anyFloat());
     }
 
     @Test
@@ -136,7 +139,8 @@
                 BiometricSourceType.FINGERPRINT);
 
         verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
-        verify(mStatusBarKeyguardViewManager).animateCollapsePanels(anyFloat());
+        verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(),
+                anyFloat());
     }
 
     @Test
@@ -155,7 +159,8 @@
         mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                 BiometricSourceType.FACE);
 
-        verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
+        verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
+                anyBoolean(), anyFloat());
         verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean());
     }
 
@@ -168,7 +173,8 @@
         mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                 BiometricSourceType.FACE);
 
-        verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
+        verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
+                anyBoolean(), anyFloat());
         verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
     }
 
@@ -201,7 +207,8 @@
                 BiometricSourceType.FACE);
 
         verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
-        verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
+        verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
+                anyBoolean(), anyFloat());
         assertThat(mBiometricUnlockController.getMode())
                 .isEqualTo(BiometricUnlockController.MODE_NONE);
     }
@@ -253,7 +260,8 @@
         mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                 BiometricSourceType.FACE);
 
-        verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
+        verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
+                anyBoolean(), anyFloat());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 0df2ebc8..39afbe0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -254,6 +254,7 @@
                 mDivider,
                 Optional.of(mRecents),
                 () -> mock(StatusBar.class),
+                mock(ShadeController.class),
                 mHandler);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index d7c00cf..532192b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -111,6 +111,8 @@
     private Handler mHandler;
     @Mock
     private BubbleController mBubbleController;
+    @Mock
+    private ShadeControllerImpl mShadeController;
 
     @Mock
     private ActivityIntentHelper mActivityIntentHelper;
@@ -177,7 +179,7 @@
                 mKeyguardStateController,
                 mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
                 mock(LockPatternUtils.class), mHandler, mHandler, mActivityIntentHelper,
-                mBubbleController, mSuperStatusBarViewFactory))
+                mBubbleController, mShadeController, mSuperStatusBarViewFactory))
                 .setStatusBar(mStatusBar)
                 .setNotificationPresenter(mock(NotificationPresenter.class))
                 .setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class))
@@ -194,7 +196,7 @@
 
         // set up addPostCollapseAction to synchronously invoke the Runnable arg
         doAnswer(answerVoid(Runnable::run))
-                .when(mStatusBar).addPostCollapseAction(any(Runnable.class));
+                .when(mShadeController).addPostCollapseAction(any(Runnable.class));
 
         // set up Handler to synchronously invoke the Runnable arg
         doAnswer(answerVoid(Runnable::run))
@@ -219,7 +221,7 @@
         mNotificationActivityStarter.onNotificationClicked(sbn, mNotificationRow);
 
         // Then
-        verify(mStatusBar, atLeastOnce()).collapsePanel();
+        verify(mShadeController, atLeastOnce()).collapsePanel();
 
         verify(mContentIntent).sendAndReturnResult(
                 any(Context.class),
@@ -254,7 +256,7 @@
         verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
 
         // This is called regardless, and simply short circuits when there is nothing to do.
-        verify(mStatusBar, atLeastOnce()).collapsePanel();
+        verify(mShadeController, atLeastOnce()).collapsePanel();
 
         verify(mAssistManager).hideAssist();
 
@@ -284,7 +286,7 @@
         // Then
         verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
 
-        verify(mStatusBar, atLeastOnce()).collapsePanel();
+        verify(mShadeController, atLeastOnce()).collapsePanel();
 
         verify(mAssistManager).hideAssist();
 
@@ -314,7 +316,7 @@
         // Then
         verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
 
-        verify(mStatusBar, atLeastOnce()).collapsePanel();
+        verify(mShadeController, atLeastOnce()).collapsePanel();
 
         verify(mAssistManager).hideAssist();
 
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 fb6e168..575f145 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
@@ -115,7 +115,7 @@
                 mock(NotificationAlertingManager.class),
                 mock(NotificationRowBinderImpl.class), mock(KeyguardStateController.class),
                 mock(KeyguardIndicationController.class),
-                mStatusBar, mCommandQueue);
+                mStatusBar, mock(ShadeControllerImpl.class), mCommandQueue);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 6dfd082..cd2c349 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -73,7 +73,7 @@
         mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
                 mock(NotificationGroupManager.class), mNotificationLockscreenUserManager,
                 mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager,
-                mActivityStarter, () -> mShadeController, new CommandQueue(mContext)));
+                mActivityStarter, mShadeController, new CommandQueue(mContext)));
         mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver();
     }
 
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 be68097..d3fce56 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
@@ -63,6 +63,7 @@
 import android.util.SparseArray;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
 import android.widget.LinearLayout;
 
 import androidx.test.filters.SmallTest;
@@ -243,6 +244,7 @@
     @Mock private LockscreenLockIconController mLockscreenLockIconController;
     @Mock private StatusBarNotificationActivityStarter.Builder
             mStatusBarNotificationActivityStarterBuilder;
+    private ShadeController mShadeController;
 
     @Before
     public void setup() throws Exception {
@@ -310,6 +312,11 @@
         when(mStatusBarComponent.getStatusBarWindowViewController()).thenReturn(
                 mStatusBarWindowViewController);
 
+        mShadeController = new ShadeControllerImpl(mCommandQueue,
+                mStatusBarStateController, mStatusBarWindowController,
+                mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
+                () -> mStatusBar, () -> mAssistManager, () -> mBubbleController);
+
         mStatusBar = new StatusBar(
                 mContext,
                 mFeatureFlags,
@@ -382,6 +389,7 @@
                 Optional.of(mDivider),
                 mLightsOutNotifController,
                 mStatusBarNotificationActivityStarterBuilder,
+                mShadeController,
                 mSuperStatusBarViewFactory,
                 mStatusBarKeyguardViewManager,
                 mViewMediatorCallback,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index 00ea187..9f899ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -102,7 +102,7 @@
                 mDozeLog,
                 mDozeParameters,
                 new CommandQueue(mContext),
-                () -> mShadeController,
+                mShadeController,
                 mDockManager,
                 mView);
         mController.setupExpandedStatusBar();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
index b1716ff..bd64124 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
@@ -49,7 +49,6 @@
     @Test
     public void testNoDelay() {
         FakeSystemClock clock = new FakeSystemClock();
-        clock.setAutoIncrement(false);
         FakeExecutor fakeExecutor = new FakeExecutor(clock);
         RunnableImpl runnable = new RunnableImpl();
 
@@ -99,7 +98,6 @@
     @Test
     public void testDelayed() {
         FakeSystemClock clock = new FakeSystemClock();
-        clock.setAutoIncrement(false);
         FakeExecutor fakeExecutor = new FakeExecutor(clock);
         RunnableImpl runnable = new RunnableImpl();
 
@@ -134,7 +132,6 @@
     @Test
     public void testDelayed_AdvanceAndRun() {
         FakeSystemClock clock = new FakeSystemClock();
-        clock.setAutoIncrement(false);
         FakeExecutor fakeExecutor = new FakeExecutor(clock);
         RunnableImpl runnable = new RunnableImpl();
 
@@ -181,7 +178,6 @@
     @Test
     public void testExecutionOrder() {
         FakeSystemClock clock = new FakeSystemClock();
-        clock.setAutoIncrement(false);
         FakeExecutor fakeExecutor = new FakeExecutor(clock);
         RunnableImpl runnableA = new RunnableImpl();
         RunnableImpl runnableB = new RunnableImpl();
@@ -251,7 +247,6 @@
     @Test
     public void testRemoval_single() {
         FakeSystemClock clock = new FakeSystemClock();
-        clock.setAutoIncrement(false);
         FakeExecutor fakeExecutor = new FakeExecutor(clock);
         RunnableImpl runnable = new RunnableImpl();
         Runnable removeFunction;
@@ -291,7 +286,6 @@
     @Test
     public void testRemoval_multi() {
         FakeSystemClock clock = new FakeSystemClock();
-        clock.setAutoIncrement(false);
         FakeExecutor fakeExecutor = new FakeExecutor(clock);
         List<Runnable> removeFunctions = new ArrayList<>();
         RunnableImpl runnable = new RunnableImpl();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
index 65e5902..e94eaaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
@@ -20,8 +20,6 @@
 import java.util.List;
 
 public class FakeSystemClock implements SystemClock {
-    private boolean mAutoIncrement = true;
-
     private long mUptimeMillis;
     private long mElapsedRealtime;
     private long mElapsedRealtimeNanos;
@@ -34,54 +32,36 @@
     @Override
     public long uptimeMillis() {
         long value = mUptimeMillis;
-        if (mAutoIncrement) {
-            setUptimeMillis(mUptimeMillis + 1);
-        }
         return value;
     }
 
     @Override
     public long elapsedRealtime() {
         long value = mElapsedRealtime;
-        if (mAutoIncrement) {
-            setElapsedRealtime(mElapsedRealtime + 1);
-        }
         return value;
     }
 
     @Override
     public long elapsedRealtimeNanos() {
         long value = mElapsedRealtimeNanos;
-        if (mAutoIncrement) {
-            setElapsedRealtimeNanos(mElapsedRealtimeNanos + 1);
-        }
         return value;
     }
 
     @Override
     public long currentThreadTimeMillis() {
         long value = mCurrentThreadTimeMillis;
-        if (mAutoIncrement) {
-            setCurrentThreadTimeMillis(mCurrentThreadTimeMillis + 1);
-        }
         return value;
     }
 
     @Override
     public long currentThreadTimeMicro() {
         long value = mCurrentThreadTimeMicro;
-        if (mAutoIncrement) {
-            setCurrentThreadTimeMicro(mCurrentThreadTimeMicro + 1);
-        }
         return value;
     }
 
     @Override
     public long currentTimeMicro() {
         long value = mCurrentTimeMicro;
-        if (mAutoIncrement) {
-            setCurrentTimeMicro(mCurrentTimeMicro + 1);
-        }
         return value;
     }
 
@@ -127,11 +107,6 @@
         }
     }
 
-    /** If true, each call to get____ will be one higher than the previous call to that method. */
-    public void setAutoIncrement(boolean autoIncrement) {
-        mAutoIncrement = autoIncrement;
-    }
-
     public void addListener(ClockTickListener listener) {
         mListeners.add(listener);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
new file mode 100644
index 0000000..701b2fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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.volume;
+
+import static org.junit.Assert.assertEquals;
+
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.metrics.LogMaker;
+import android.provider.Settings;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Queue;
+
+/**
+ * Parameterized unit test for Events.logEvent.
+ *
+ * This test captures a translation table between the Event class tags, the debugging logs,
+ * the event-buffer logs, and the statsd logs.
+ *
+ * This test works as a straight JUnit4 test, but is declared as a SysuiTestCase because
+ * AAAPlusPlusVerifySysuiRequiredTestPropertiesTest requires all tests in SystemUiTest extend
+ * either SysuiTestCase or SysUiBaseFragmentTest.
+ *
+ */
+@RunWith(Parameterized.class)
+@SmallTest
+public class EventsTest extends SysuiTestCase {
+    private FakeMetricsLogger mLegacyLogger;
+    private UiEventLoggerFake mUiEventLogger;
+
+    @Before
+    public void setFakeLoggers() {
+        mLegacyLogger = new FakeMetricsLogger();
+        Events.sLegacyLogger = mLegacyLogger;
+        mUiEventLogger = new UiEventLoggerFake();
+        Events.sUiEventLogger = mUiEventLogger;
+    }
+
+    // Parameters for calling writeEvent with arbitrary args.
+    @Parameterized.Parameter
+    public int mTag;
+
+    @Parameterized.Parameter(1)
+    public Object[] mArgs;
+
+    // Expect returned string exactly matches.
+    @Parameterized.Parameter(2)
+    public String mExpectedMessage;
+
+    // Expect these MetricsLogger calls.
+
+    @Parameterized.Parameter(3)
+    public int[] mExpectedMetrics;
+
+    // Expect this UiEvent (use null if there isn't one).
+    @Parameterized.Parameter(4)
+    public UiEventLogger.UiEventEnum mUiEvent;
+
+    @Test
+    public void testLogEvent() {
+        String result = Events.logEvent(mTag, mArgs);
+        assertEquals("Show Dialog", mExpectedMessage, result);
+
+        Queue<LogMaker> logs = mLegacyLogger.getLogs();
+        if (mExpectedMetrics == null) {
+            assertEquals(0, logs.size());
+        } else {
+            assertEquals(mExpectedMetrics.length, logs.size());
+            if (mExpectedMetrics.length > 0) {
+                assertEquals(mExpectedMetrics[0], logs.remove().getCategory());
+            }
+            if (mExpectedMetrics.length > 1) {
+                assertEquals(mExpectedMetrics[1], logs.remove().getCategory());
+            }
+        }
+        Queue<UiEventLoggerFake.FakeUiEvent> events = mUiEventLogger.getLogs();
+        if (mUiEvent != null) {
+            assertEquals(mUiEvent.getId(), events.remove().eventId);
+        }
+    }
+
+    @Parameterized.Parameters(name = "{index}: {2}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {Events.EVENT_SETTINGS_CLICK, null,
+                        "writeEvent settings_click",
+                        new int[]{MetricsEvent.ACTION_VOLUME_SETTINGS},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_SETTINGS_CLICK},
+                {Events.EVENT_SHOW_DIALOG, new Object[]{Events.SHOW_REASON_VOLUME_CHANGED, false},
+                        "writeEvent show_dialog volume_changed keyguard=false",
+                        new int[]{MetricsEvent.VOLUME_DIALOG,
+                                MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM},
+                        Events.VolumeDialogOpenEvent.VOLUME_DIALOG_SHOW_VOLUME_CHANGED},
+                {Events.EVENT_EXPAND, new Object[]{true},
+                        "writeEvent expand true",
+                        new int[]{MetricsEvent.VOLUME_DIALOG_DETAILS},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_EXPAND_DETAILS},
+                {Events.EVENT_DISMISS_DIALOG,
+                        new Object[]{Events.DISMISS_REASON_TOUCH_OUTSIDE, true},
+                        "writeEvent dismiss_dialog touch_outside",
+                        new int[]{MetricsEvent.VOLUME_DIALOG},
+                        Events.VolumeDialogCloseEvent.VOLUME_DIALOG_DISMISS_TOUCH_OUTSIDE},
+                {Events.EVENT_ACTIVE_STREAM_CHANGED, new Object[]{AudioSystem.STREAM_ACCESSIBILITY},
+                        "writeEvent active_stream_changed STREAM_ACCESSIBILITY",
+                        new int[]{MetricsEvent.ACTION_VOLUME_STREAM},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_ACTIVE_STREAM_CHANGED},
+                {Events.EVENT_ICON_CLICK,
+                        new Object[]{AudioSystem.STREAM_MUSIC, Events.ICON_STATE_MUTE},
+                        "writeEvent icon_click STREAM_MUSIC mute",
+                        new int[]{MetricsEvent.ACTION_VOLUME_ICON},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_MUTE_STREAM},
+                {Events.EVENT_TOUCH_LEVEL_DONE,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 0},
+                        "writeEvent touch_level_done STREAM_MUSIC 0",
+                        new int[]{MetricsEvent.ACTION_VOLUME_SLIDER},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_SLIDER_TO_ZERO},
+                {Events.EVENT_TOUCH_LEVEL_DONE,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 1},
+                        "writeEvent touch_level_done STREAM_MUSIC 1",
+                        new int[]{MetricsEvent.ACTION_VOLUME_SLIDER},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_SLIDER},
+                {Events.EVENT_TOUCH_LEVEL_CHANGED,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 0},
+                        "writeEvent touch_level_changed STREAM_MUSIC 0",
+                        null, null},
+                {Events.EVENT_LEVEL_CHANGED,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 0},
+                        "writeEvent level_changed STREAM_MUSIC 0",
+                        null, null},
+                {Events.EVENT_MUTE_CHANGED,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 0},
+                        "writeEvent mute_changed STREAM_MUSIC 0",
+                        null, null},
+                {Events.EVENT_KEY,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 0},
+                        "writeEvent key STREAM_MUSIC 0",
+                        new int[]{MetricsEvent.ACTION_VOLUME_KEY},
+                        Events.VolumeDialogEvent.VOLUME_KEY_TO_ZERO},
+                {Events.EVENT_KEY,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 1},
+                        "writeEvent key STREAM_MUSIC 1",
+                        new int[]{MetricsEvent.ACTION_VOLUME_KEY},
+                        Events.VolumeDialogEvent.VOLUME_KEY},
+                {Events.EVENT_RINGER_TOGGLE, new Object[]{AudioManager.RINGER_MODE_NORMAL},
+                        "writeEvent ringer_toggle normal",
+                        new int[]{MetricsEvent.ACTION_VOLUME_RINGER_TOGGLE},
+                        Events.VolumeDialogEvent.RINGER_MODE_NORMAL},
+                {Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED,
+                        new Object[]{AudioManager.RINGER_MODE_NORMAL},
+                        "writeEvent external_ringer_mode_changed normal",
+                        new int[]{MetricsEvent.ACTION_RINGER_MODE},
+                        null},
+                {Events.EVENT_INTERNAL_RINGER_MODE_CHANGED,
+                        new Object[]{AudioManager.RINGER_MODE_NORMAL},
+                        "writeEvent internal_ringer_mode_changed normal",
+                        null, null},
+                {Events.EVENT_ZEN_MODE_CHANGED,
+                        new Object[]{Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS},
+                        "writeEvent zen_mode_changed important_interruptions",
+                        null, Events.ZenModeEvent.ZEN_MODE_IMPORTANT_ONLY},
+                {Events.EVENT_ZEN_MODE_CHANGED,
+                        new Object[]{Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS},
+                        "writeEvent zen_mode_changed important_interruptions",
+                        null, Events.ZenModeEvent.ZEN_MODE_IMPORTANT_ONLY},
+                {Events.EVENT_SUPPRESSOR_CHANGED,
+                        new Object[]{"component", "name"},
+                        "writeEvent suppressor_changed component name",
+                        null, null},
+                {Events.EVENT_SHOW_USB_OVERHEAT_ALARM,
+                        new Object[]{Events.SHOW_REASON_USB_OVERHEAD_ALARM_CHANGED, true},
+                        "writeEvent show_usb_overheat_alarm usb_temperature_above_threshold "
+                                + "keyguard=true",
+                        new int[]{MetricsEvent.POWER_OVERHEAT_ALARM,
+                                MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM},
+                        Events.VolumeDialogEvent.USB_OVERHEAT_ALARM},
+                {Events.EVENT_DISMISS_USB_OVERHEAT_ALARM,
+                        new Object[]{Events.DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED, true},
+                        "writeEvent dismiss_usb_overheat_alarm usb_temperature_below_threshold "
+                                + "keyguard=true",
+                        new int[]{MetricsEvent.POWER_OVERHEAT_ALARM,
+                                MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM},
+                        Events.VolumeDialogEvent.USB_OVERHEAT_ALARM_DISMISSED},
+        });
+    }
+}
+
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 7e8721d..3c953b3 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -31,6 +31,7 @@
         "android.hardware.tetheroffload.control-V1.0-java",
         "tethering-client",
     ],
+    libs: ["unsupportedappusage"],
     manifest: "AndroidManifestBase.xml",
 }
 
diff --git a/services/Android.bp b/services/Android.bp
index 3b56607..fd4094f 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -14,6 +14,7 @@
         ":services.appwidget-sources",
         ":services.autofill-sources",
         ":services.backup-sources",
+        ":backuplib-sources",
         ":services.companion-sources",
         ":services.contentcapture-sources",
         ":services.contentsuggestions-sources",
@@ -101,3 +102,29 @@
     name: "art-profile",
     srcs: ["art-profile"],
 }
+
+// API stub
+// =============================================================
+
+droidstubs {
+    name: "services-stubs.sources",
+    srcs: [":services-sources"],
+    installable: false,
+    // TODO: remove the --hide options below
+    args: " --show-single-annotation android.annotation.SystemApi" +
+        " --hide-annotation android.annotation.Hide" +
+        " --hide-package com.google.android.startop.iorap" +
+        " --hide ReferencesHidden" +
+        " --hide DeprecationMismatch" +
+        " --hide HiddenTypedefConstant",
+    libs: [
+        "framework-all",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+java_library {
+    name: "services-stubs",
+    srcs: [":services-stubs.sources"],
+    installable: false,
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7fdd83b..6a6e2b2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,6 +16,11 @@
 
 package com.android.server.accessibility;
 
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
+import static android.view.accessibility.AccessibilityManager.ShortcutType;
+
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
@@ -31,6 +36,7 @@
 import android.app.AlertDialog;
 import android.app.PendingIntent;
 import android.appwidget.AppWidgetManagerInternal;
+import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -70,6 +76,7 @@
 import android.provider.SettingsStringUtil.SettingStringHelper;
 import android.text.TextUtils;
 import android.text.TextUtils.SimpleStringSplitter;
+import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -113,9 +120,9 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * This class is instantiated by the system as a system level service and can be
@@ -754,6 +761,8 @@
             userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
             userState.setDisplayMagnificationEnabledLocked(false);
             userState.setNavBarMagnificationEnabledLocked(false);
+            userState.disableShortcutMagnificationLocked();
+
             userState.setAutoclickEnabledLocked(false);
             userState.mEnabledServices.clear();
             userState.mEnabledServices.add(service);
@@ -1072,6 +1081,8 @@
         }
     }
 
+    // TODO(a11y shortcut): Remove this function and Use #performAccessibilityShortcutInternal(
+    //  ACCESSIBILITY_BUTTON) instead, after the new Settings shortcut Ui merged.
     private void notifyAccessibilityButtonClickedLocked(int displayId) {
         final AccessibilityUserState state = getCurrentUserStateLocked();
 
@@ -1105,8 +1116,8 @@
             if (state.getServiceAssignedToAccessibilityButtonLocked() == null
                     && !state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
                 mMainHandler.sendMessage(obtainMessage(
-                        AccessibilityManagerService::showAccessibilityButtonTargetSelection, this,
-                        displayId));
+                        AccessibilityManagerService::showAccessibilityTargetsSelection, this,
+                        displayId, ACCESSIBILITY_BUTTON));
             } else if (state.isNavBarMagnificationEnabledLocked()
                     && state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
                 mMainHandler.sendMessage(obtainMessage(
@@ -1125,8 +1136,8 @@
             }
             // The user may have turned off the assigned service or feature
             mMainHandler.sendMessage(obtainMessage(
-                    AccessibilityManagerService::showAccessibilityButtonTargetSelection, this,
-                    displayId));
+                    AccessibilityManagerService::showAccessibilityTargetsSelection, this,
+                    displayId, ACCESSIBILITY_BUTTON));
         }
     }
 
@@ -1138,13 +1149,27 @@
         }
     }
 
-    private void showAccessibilityButtonTargetSelection(int displayId) {
+    private void showAccessibilityTargetsSelection(int displayId,
+            @ShortcutType int shortcutType) {
         Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
+        bundle.putInt(AccessibilityManager.EXTRA_SHORTCUT_TYPE, shortcutType);
         mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId));
     }
 
+    private void launchShortcutTargetActivity(int displayId, ComponentName name) {
+        final Intent intent = new Intent();
+        final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
+        intent.setComponent(name);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        try {
+            mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId));
+        } catch (ActivityNotFoundException ignore) {
+            // ignore the exception
+        }
+    }
+
     private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
         final AccessibilityUserState state = getCurrentUserStateLocked();
         mIsAccessibilityButtonShown = available;
@@ -1353,9 +1378,8 @@
      */
     private void readComponentNamesFromSettingLocked(String settingName, int userId,
             Set<ComponentName> outComponentNames) {
-        String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
-                settingName, userId);
-        readComponentNamesFromStringLocked(settingValue, outComponentNames, false);
+        readColonDelimitedSettingToSet(settingName, userId, outComponentNames,
+                str -> ComponentName.unflattenFromString(str));
     }
 
     /**
@@ -1370,34 +1394,57 @@
     private void readComponentNamesFromStringLocked(String names,
             Set<ComponentName> outComponentNames,
             boolean doMerge) {
-        if (!doMerge) {
-            outComponentNames.clear();
-        }
-        if (names != null) {
-            TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
-            splitter.setString(names);
-            while (splitter.hasNext()) {
-                String str = splitter.next();
-                if (str == null || str.length() <= 0) {
-                    continue;
-                }
-                ComponentName enabledService = ComponentName.unflattenFromString(str);
-                if (enabledService != null) {
-                    outComponentNames.add(enabledService);
-                }
-            }
-        }
+        readColonDelimitedStringToSet(names, outComponentNames, doMerge,
+                str -> ComponentName.unflattenFromString(str));
     }
 
     @Override
     public void persistComponentNamesToSettingLocked(String settingName,
             Set<ComponentName> componentNames, int userId) {
-        StringBuilder builder = new StringBuilder();
-        for (ComponentName componentName : componentNames) {
+        persistColonDelimitedSetToSettingLocked(settingName, userId, componentNames,
+                componentName -> componentName.flattenToShortString());
+    }
+
+    private <T> void readColonDelimitedSettingToSet(String settingName, int userId, Set<T> outSet,
+            Function<String, T> toItem) {
+        final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                settingName, userId);
+        readColonDelimitedStringToSet(settingValue, outSet, false, toItem);
+    }
+
+    private <T> void readColonDelimitedStringToSet(String names, Set<T> outSet, boolean doMerge,
+            Function<String, T> toItem) {
+        if (!doMerge) {
+            outSet.clear();
+        }
+        if (!TextUtils.isEmpty(names)) {
+            final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+            splitter.setString(names);
+            while (splitter.hasNext()) {
+                final String str = splitter.next();
+                if (TextUtils.isEmpty(str)) {
+                    continue;
+                }
+                final T item = toItem.apply(str);
+                if (item != null) {
+                    outSet.add(item);
+                }
+            }
+        }
+    }
+
+    private <T> void persistColonDelimitedSetToSettingLocked(String settingName, int userId,
+            Set<T> set, Function<T, String> toString) {
+        final StringBuilder builder = new StringBuilder();
+        for (T item : set) {
+            final String str = (item != null ? toString.apply(item) : null);
+            if (TextUtils.isEmpty(str)) {
+                continue;
+            }
             if (builder.length() > 0) {
                 builder.append(COMPONENT_NAME_SEPARATOR);
             }
-            builder.append(componentName.flattenToShortString());
+            builder.append(str);
         }
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -1537,7 +1584,8 @@
             if (userState.isDisplayMagnificationEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
             }
-            if (userState.isNavBarMagnificationEnabledLocked()) {
+            if (userState.isNavBarMagnificationEnabledLocked()
+                    || userState.isShortcutKeyMagnificationEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
             }
             if (userHasMagnificationServicesLocked(userState)) {
@@ -1647,7 +1695,6 @@
         mInitialized = true;
         updateLegacyCapabilitiesLocked(userState);
         updateServicesLocked(userState);
-        updateAccessibilityShortcutLocked(userState);
         updateWindowsForAccessibilityCallbackLocked(userState);
         updateFilterKeyEventsLocked(userState);
         updateTouchExplorationLocked(userState);
@@ -1657,6 +1704,7 @@
         scheduleUpdateInputFilter(userState);
         updateRelevantEventsLocked(userState);
         scheduleUpdateClientsIfNeededLocked(userState);
+        updateAccessibilityShortcutKeyTargetsLocked(userState);
         updateAccessibilityButtonTargetsLocked(userState);
     }
 
@@ -1751,7 +1799,7 @@
         somethingChanged |= readHighTextContrastEnabledSettingLocked(userState);
         somethingChanged |= readMagnificationEnabledSettingsLocked(userState);
         somethingChanged |= readAutoclickEnabledSettingLocked(userState);
-        somethingChanged |= readAccessibilityShortcutSettingLocked(userState);
+        somethingChanged |= readAccessibilityShortcutKeySettingLocked(userState);
         somethingChanged |= readAccessibilityButtonSettingsLocked(userState);
         somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
         return somethingChanged;
@@ -1847,57 +1895,43 @@
         }
     }
 
-    private boolean readAccessibilityShortcutSettingLocked(AccessibilityUserState userState) {
-        String componentNameToEnableString = AccessibilityShortcutController
-                .getTargetServiceComponentNameString(mContext, userState.mUserId);
-        if ((componentNameToEnableString == null) || componentNameToEnableString.isEmpty()) {
-            if (userState.getServiceToEnableWithShortcutLocked() == null) {
-                return false;
+    private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
+        final Set<String> targetsFromSetting = new ArraySet<>();
+        readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                userState.mUserId, targetsFromSetting, str -> str);
+        if (targetsFromSetting.isEmpty()) {
+            // Fall back to device's default a11y service.
+            final String defaultService = mContext.getString(
+                    R.string.config_defaultAccessibilityService);
+            if (!TextUtils.isEmpty(defaultService)) {
+                targetsFromSetting.add(defaultService);
             }
-            userState.setServiceToEnableWithShortcutLocked(null);
-            return true;
-        }
-        ComponentName componentNameToEnable =
-            ComponentName.unflattenFromString(componentNameToEnableString);
-        if ((componentNameToEnable != null)
-                && componentNameToEnable.equals(userState.getServiceToEnableWithShortcutLocked())) {
-            return false;
         }
 
-        userState.setServiceToEnableWithShortcutLocked(componentNameToEnable);
+        final Set<String> currentTargets =
+                userState.getShortcutTargetsLocked(ACCESSIBILITY_SHORTCUT_KEY);
+        if (targetsFromSetting.equals(currentTargets)) {
+            return false;
+        }
+        currentTargets.clear();
+        currentTargets.addAll(targetsFromSetting);
         scheduleNotifyClientsOfServicesStateChangeLocked(userState);
         return true;
     }
 
     private boolean readAccessibilityButtonSettingsLocked(AccessibilityUserState userState) {
-        String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId);
-        if (TextUtils.isEmpty(componentId)) {
-            if ((userState.getServiceAssignedToAccessibilityButtonLocked() == null)
-                    && !userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
-                return false;
-            }
-            userState.setServiceAssignedToAccessibilityButtonLocked(null);
-            userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
-            return true;
-        }
+        final Set<String> targetsFromSetting = new ArraySet<>();
+        readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+                userState.mUserId, targetsFromSetting, str -> str);
 
-        if (componentId.equals(MagnificationController.class.getName())) {
-            if (userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
-                return false;
-            }
-            userState.setServiceAssignedToAccessibilityButtonLocked(null);
-            userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
-            return true;
-        }
-
-        ComponentName componentName = ComponentName.unflattenFromString(componentId);
-        if (Objects.equals(componentName,
-                userState.getServiceAssignedToAccessibilityButtonLocked())) {
+        final Set<String> currentTargets =
+                userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON);
+        if (targetsFromSetting.equals(currentTargets)) {
             return false;
         }
-        userState.setServiceAssignedToAccessibilityButtonLocked(componentName);
-        userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
+        currentTargets.clear();
+        currentTargets.addAll(targetsFromSetting);
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
         return true;
     }
 
@@ -1921,34 +1955,33 @@
     }
 
     /**
-     * Check if the service that will be enabled by the shortcut is installed. If it isn't,
-     * clear the value and the associated setting so a sideloaded service can't spoof the
-     * package name of the default service.
-     *
-     * @param userState
+     * Check if the targets that will be enabled by the accessibility shortcut key is installed.
+     * If it isn't, remove it from the list and associated setting so a side loaded service can't
+     * spoof the package name of the default service.
      */
-    private void updateAccessibilityShortcutLocked(AccessibilityUserState userState) {
-        if (userState.getServiceToEnableWithShortcutLocked() == null) {
+    private void updateAccessibilityShortcutKeyTargetsLocked(AccessibilityUserState userState) {
+        final Set<String> currentTargets =
+                userState.getShortcutTargetsLocked(ACCESSIBILITY_SHORTCUT_KEY);
+        final int lastSize = currentTargets.size();
+        if (lastSize == 0) {
             return;
         }
-        boolean shortcutServiceIsInstalled =
-                AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
-                        .containsKey(userState.getServiceToEnableWithShortcutLocked());
-        for (int i = 0; !shortcutServiceIsInstalled && (i < userState.mInstalledServices.size());
-                i++) {
-            if (userState.mInstalledServices.get(i).getComponentName()
-                    .equals(userState.getServiceToEnableWithShortcutLocked())) {
-                shortcutServiceIsInstalled = true;
-            }
+        currentTargets.removeIf(
+                name -> !userState.isShortcutTargetInstalledLocked(name));
+        if (lastSize == currentTargets.size()) {
+            return;
         }
-        if (!shortcutServiceIsInstalled) {
-            userState.setServiceToEnableWithShortcutLocked(null);
+
+        // Update setting key with new value.
+        persistColonDelimitedSetToSettingLocked(
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                userState.mUserId, currentTargets, str -> str);
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+
+        // Disable accessibility shortcut key if there's no shortcut installed.
+        if (currentTargets.isEmpty()) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null,
-                        userState.mUserId);
-
                 Settings.Secure.putIntForUser(mContext.getContentResolver(),
                         Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 0, userState.mUserId);
             } finally {
@@ -2004,7 +2037,8 @@
         // displays in one display. It's not a real display and there's no input events for it.
         final ArrayList<Display> displays = getValidDisplayList();
         if (userState.isDisplayMagnificationEnabledLocked()
-                || userState.isNavBarMagnificationEnabledLocked()) {
+                || userState.isNavBarMagnificationEnabledLocked()
+                || userState.isShortcutKeyMagnificationEnabledLocked()) {
             for (int i = 0; i < displays.size(); i++) {
                 final Display display = displays.get(i);
                 getMagnificationController().register(display.getDisplayId());
@@ -2088,7 +2122,14 @@
         }
     }
 
+    /**
+     * 1) Update accessibility button availability to accessibility services.
+     * 2) Check if the targets that will be enabled by the accessibility button is installed.
+     *    If it isn't, remove it from the list and associated setting so a side loaded service can't
+     *    spoof the package name of the default service.
+     */
     private void updateAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
+        // Update accessibility button availability.
         for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
             final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
             if (service.mRequestAccessibilityButton) {
@@ -2096,6 +2137,24 @@
                         service.isAccessibilityButtonAvailableLocked(userState));
             }
         }
+
+        final Set<String> currentTargets =
+                userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON);
+        final int lastSize = currentTargets.size();
+        if (lastSize == 0) {
+            return;
+        }
+        currentTargets.removeIf(
+                name -> !userState.isShortcutTargetInstalledLocked(name));
+        if (lastSize == currentTargets.size()) {
+            return;
+        }
+
+        // Update setting key with new value.
+        persistColonDelimitedSetToSettingLocked(
+                Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+                userState.mUserId, currentTargets, str -> str);
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
     }
 
     private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) {
@@ -2156,7 +2215,7 @@
     }
 
     /**
-     * AIDL-exposed method to be called when the accessibility shortcut is enabled. Requires
+     * AIDL-exposed method to be called when the accessibility shortcut key is enabled. Requires
      * permission to write secure settings, since someone with that permission can enable
      * accessibility services themselves.
      */
@@ -2168,52 +2227,177 @@
             throw new SecurityException(
                     "performAccessibilityShortcut requires the MANAGE_ACCESSIBILITY permission");
         }
+        mMainHandler.sendMessage(obtainMessage(
+                AccessibilityManagerService::performAccessibilityShortcutInternal, this,
+                Display.DEFAULT_DISPLAY, ACCESSIBILITY_SHORTCUT_KEY));
+    }
+
+    /**
+     * Perform the accessibility shortcut action.
+     *
+     * @param shortcutType The shortcut type.
+     * @param displayId The display id of the accessibility button.
+     */
+    private void performAccessibilityShortcutInternal(int displayId,
+            @ShortcutType int shortcutType) {
+        final List<String> shortcutTargets = getAccessibilityShortcutTargetsInternal(shortcutType);
+        if (shortcutTargets.isEmpty()) {
+            Slog.d(LOG_TAG, "No target to perform shortcut, shortcutType=" + shortcutType);
+            return;
+        }
+        // In case there are many targets assigned to the given shortcut.
+        if (shortcutTargets.size() > 1) {
+            showAccessibilityTargetsSelection(displayId, shortcutType);
+            return;
+        }
+        final String targetName = shortcutTargets.get(0);
+        // In case user assigned magnification to the given shortcut.
+        if (targetName.equals(MAGNIFICATION_CONTROLLER_NAME)) {
+            sendAccessibilityButtonToInputFilter(displayId);
+            return;
+        }
+        final ComponentName targetComponentName = ComponentName.unflattenFromString(targetName);
+        if (targetComponentName == null) {
+            Slog.d(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName);
+            return;
+        }
+        // In case user assigned an accessibility framework feature to the given shortcut.
+        if (performAccessibilityFrameworkFeature(targetComponentName)) {
+            return;
+        }
+        // In case user assigned an accessibility shortcut target to the given shortcut.
+        if (performAccessibilityShortcutTargetActivity(displayId, targetComponentName)) {
+            return;
+        }
+        // in case user assigned an accessibility service to the given shortcut.
+        if (performAccessibilityShortcutTargetService(
+                displayId, shortcutType, targetComponentName)) {
+            return;
+        }
+    }
+
+    private boolean performAccessibilityFrameworkFeature(ComponentName assignedTarget) {
         final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
-        synchronized(mLock) {
-            final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
-            final ComponentName serviceName = userState.getServiceToEnableWithShortcutLocked();
-            if (serviceName == null) {
-                return;
-            }
-            if (frameworkFeatureMap.containsKey(serviceName)) {
-                // Toggle the requested framework feature
-                ToggleableFrameworkFeatureInfo featureInfo = frameworkFeatureMap.get(serviceName);
-                SettingStringHelper setting = new SettingStringHelper(mContext.getContentResolver(),
-                        featureInfo.getSettingKey(), mCurrentUserId);
-                // Assuming that the default state will be to have the feature off
-                if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) {
-                    setting.write(featureInfo.getSettingOnValue());
-                } else {
-                    setting.write(featureInfo.getSettingOffValue());
+        if (!frameworkFeatureMap.containsKey(assignedTarget)) {
+            return false;
+        }
+        // Toggle the requested framework feature
+        final ToggleableFrameworkFeatureInfo featureInfo = frameworkFeatureMap.get(assignedTarget);
+        final SettingStringHelper setting = new SettingStringHelper(mContext.getContentResolver(),
+                featureInfo.getSettingKey(), mCurrentUserId);
+        // Assuming that the default state will be to have the feature off
+        if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) {
+            setting.write(featureInfo.getSettingOnValue());
+        } else {
+            setting.write(featureInfo.getSettingOffValue());
+        }
+        return true;
+    }
+
+    private boolean performAccessibilityShortcutTargetActivity(int displayId,
+            ComponentName assignedTarget) {
+        synchronized (mLock) {
+            final AccessibilityUserState userState = getCurrentUserStateLocked();
+            for (int i = 0; i < userState.mInstalledShortcuts.size(); i++) {
+                final AccessibilityShortcutInfo shortcutInfo = userState.mInstalledShortcuts.get(i);
+                if (!shortcutInfo.getComponentName().equals(assignedTarget)) {
+                    continue;
                 }
-            }
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                if (userState.mComponentNameToServiceMap.get(serviceName) == null) {
-                    enableAccessibilityServiceLocked(serviceName, mCurrentUserId);
-                } else {
-                    disableAccessibilityServiceLocked(serviceName, mCurrentUserId);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+                launchShortcutTargetActivity(displayId, assignedTarget);
+                return true;
             }
         }
-    };
+        return false;
+    }
+
+    /**
+     * Perform accessibility service shortcut action.
+     *
+     * 1) For {@link AccessibilityManager#ACCESSIBILITY_BUTTON} type and services targeting sdk
+     *    version <= Q: callbacks to accessibility service if service is bounded and requests
+     *    accessibility button.
+     * 2) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk
+     *    version <= Q: turns on / off the accessibility service.
+     * 3) For services targeting sdk version > Q:
+     *    a) Turns on / off the accessibility service, if service does not request accessibility
+     *       button.
+     *    b) Callbacks to accessibility service if service is bounded and requests accessibility
+     *       button.
+     */
+    private boolean performAccessibilityShortcutTargetService(int displayId,
+            @ShortcutType int shortcutType, ComponentName assignedTarget) {
+        synchronized (mLock) {
+            final AccessibilityUserState userState = getCurrentUserStateLocked();
+            final AccessibilityServiceInfo installedServiceInfo =
+                    userState.getInstalledServiceInfoLocked(assignedTarget);
+            if (installedServiceInfo == null) {
+                Slog.d(LOG_TAG, "Perform shortcut failed, invalid component name:"
+                        + assignedTarget);
+                return false;
+            }
+
+            final AccessibilityServiceConnection serviceConnection =
+                    userState.getServiceConnectionLocked(assignedTarget);
+            final int targetSdk = installedServiceInfo.getResolveInfo()
+                    .serviceInfo.applicationInfo.targetSdkVersion;
+            final boolean requestA11yButton = (installedServiceInfo.flags
+                    & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+            // Turns on / off the accessibility service
+            if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == ACCESSIBILITY_SHORTCUT_KEY)
+                    || (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) {
+                if (serviceConnection == null) {
+                    enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
+                } else {
+                    disableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
+                }
+                return true;
+            }
+            // Callbacks to a11y service if it's bounded and requests a11y button.
+            if (serviceConnection == null
+                    || !userState.mBoundServices.contains(serviceConnection)
+                    || !serviceConnection.mRequestAccessibilityButton) {
+                Slog.d(LOG_TAG, "Perform shortcut failed, service is not ready:"
+                        + assignedTarget);
+                return false;
+            }
+            serviceConnection.notifyAccessibilityButtonClickedLocked(displayId);
+            return true;
+        }
+    }
 
     @Override
-    public String getAccessibilityShortcutService() {
-        if (mContext.checkCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+    public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException(
                     "getAccessibilityShortcutService requires the MANAGE_ACCESSIBILITY permission");
         }
-        synchronized(mLock) {
-            final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
-            if (userState.getServiceToEnableWithShortcutLocked() == null) {
-                return null;
+        return getAccessibilityShortcutTargetsInternal(shortcutType);
+    }
+
+    private List<String> getAccessibilityShortcutTargetsInternal(@ShortcutType int shortcutType) {
+        synchronized (mLock) {
+            final AccessibilityUserState userState = getCurrentUserStateLocked();
+            final ArrayList<String> shortcutTargets = new ArrayList<>(
+                    userState.getShortcutTargetsLocked(shortcutType));
+            if (shortcutType != ACCESSIBILITY_BUTTON) {
+                return shortcutTargets;
             }
-            return userState.getServiceToEnableWithShortcutLocked().flattenToString();
+            // Adds legacy a11y services requesting a11y button into the list.
+            for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+                final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
+                if (!service.mRequestAccessibilityButton
+                        || service.getServiceInfo().getResolveInfo().serviceInfo.applicationInfo
+                        .targetSdkVersion > Build.VERSION_CODES.Q) {
+                    continue;
+                }
+                final String serviceName = service.getComponentName().flattenToString();
+                if (!TextUtils.isEmpty(serviceName)) {
+                    shortcutTargets.add(serviceName);
+                }
+            }
+            return shortcutTargets;
         }
     }
 
@@ -2609,6 +2793,8 @@
         private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
 
+        // TODO(a11y shortcut): Remove this setting key, and have a migrate function in
+        //  Setting provider after new shortcut UI merged.
         private final Uri mNavBarMagnificationEnabledUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
 
@@ -2713,7 +2899,7 @@
                         || mShowImeWithHardKeyboardUri.equals(uri)) {
                     userState.reconcileSoftKeyboardModeWithSettingsLocked();
                 } else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
-                    if (readAccessibilityShortcutSettingLocked(userState)) {
+                    if (readAccessibilityShortcutKeySettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
                 } else if (mAccessibilityButtonComponentIdUri.equals(uri)) {
@@ -2724,8 +2910,6 @@
                         || mUserInteractiveUiTimeoutUri.equals(uri)) {
                     readUserRecommendedUiTimeoutSettingsLocked(userState);
                 }
-                // TODO(a11y shortcut): Monitor new setting keys, when user adds shortcut, and
-                //  remove from the list of enabled targets anything that's been uninstalled.
             }
         }
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 6cadb6d..cbff6bd 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -294,6 +294,7 @@
         }
     }
 
+    // TODO(a11y shortcut): Refactoring the logic here, after the new Settings shortcut Ui merged.
     public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) {
         // If the service does not request the accessibility button, it isn't available
         if (!mRequestAccessibilityButton) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index a0b9866..a163f74 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -22,6 +22,11 @@
 import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
 import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
 import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
+import static android.view.accessibility.AccessibilityManager.ShortcutType;
+
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
 
 import android.accessibilityservice.AccessibilityService.SoftKeyboardShowMode;
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -33,10 +38,14 @@
 import android.os.Binder;
 import android.os.RemoteCallbackList;
 import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManagerClient;
 
+import com.android.internal.accessibility.AccessibilityShortcutController;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -79,19 +88,18 @@
 
     final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<>();
 
+    final ArraySet<String> mAccessibilityShortcutKeyTargets = new ArraySet<>();
+
+    final ArraySet<String> mAccessibilityButtonTargets = new ArraySet<>();
+
     private final ServiceInfoChangeListener mServiceInfoChangeListener;
 
-    private ComponentName mServiceAssignedToAccessibilityButton;
-
     private ComponentName mServiceChangingSoftKeyboardMode;
 
-    private ComponentName mServiceToEnableWithShortcut;
-
     private boolean mBindInstantServiceAllowed;
     private boolean mIsAutoclickEnabled;
     private boolean mIsDisplayMagnificationEnabled;
     private boolean mIsFilterKeyEventsEnabled;
-    private boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
     private boolean mIsNavBarMagnificationEnabled;
     private boolean mIsPerformGesturesEnabled;
     private boolean mIsTextHighContrastEnabled;
@@ -141,11 +149,11 @@
         // Clear state persisted in settings.
         mEnabledServices.clear();
         mTouchExplorationGrantedServices.clear();
+        mAccessibilityShortcutKeyTargets.clear();
+        mAccessibilityButtonTargets.clear();
         mIsTouchExplorationEnabled = false;
         mIsDisplayMagnificationEnabled = false;
         mIsNavBarMagnificationEnabled = false;
-        mServiceAssignedToAccessibilityButton = null;
-        mIsNavBarMagnificationAssignedToAccessibilityButton = false;
         mIsAutoclickEnabled = false;
         mUserNonInteractiveUiTimeout = 0;
         mUserInteractiveUiTimeout = 0;
@@ -435,6 +443,26 @@
         pw.append(", installedServiceCount=").append(String.valueOf(mInstalledServices.size()));
         pw.append("}");
         pw.println();
+        pw.append("     shortcut key:{");
+        int size = mAccessibilityShortcutKeyTargets.size();
+        for (int i = 0; i < size; i++) {
+            final String componentId = mAccessibilityShortcutKeyTargets.valueAt(i);
+            pw.append(componentId);
+            if (i + 1 < size) {
+                pw.append(", ");
+            }
+        }
+        pw.println("}");
+        pw.append("     button:{");
+        size = mAccessibilityButtonTargets.size();
+        for (int i = 0; i < size; i++) {
+            final String componentId = mAccessibilityButtonTargets.valueAt(i);
+            pw.append(componentId);
+            if (i + 1 < size) {
+                pw.append(", ");
+            }
+        }
+        pw.println("}");
         pw.append("     Bound services:{");
         final int serviceCount = mBoundServices.size();
         for (int j = 0; j < serviceCount; j++) {
@@ -525,20 +553,85 @@
         mLastSentClientState = state;
     }
 
-    public boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() {
-        return mIsNavBarMagnificationAssignedToAccessibilityButton;
+    public boolean isShortcutKeyMagnificationEnabledLocked() {
+        return mAccessibilityShortcutKeyTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
     }
 
-    public void setNavBarMagnificationAssignedToAccessibilityButtonLocked(boolean assigned) {
-        mIsNavBarMagnificationAssignedToAccessibilityButton = assigned;
+    /**
+     * Disable both shortcuts' magnification function.
+     */
+    public void disableShortcutMagnificationLocked() {
+        mAccessibilityShortcutKeyTargets.remove(MAGNIFICATION_CONTROLLER_NAME);
+        mAccessibilityButtonTargets.remove(MAGNIFICATION_CONTROLLER_NAME);
     }
 
-    public boolean isNavBarMagnificationEnabledLocked() {
-        return mIsNavBarMagnificationEnabled;
+    /**
+     * Returns a set which contains the flattened component names and the system class names
+     * assigned to the given shortcut.
+     *
+     * @param shortcutType The shortcut type.
+     * @return The array set of the strings
+     */
+    public ArraySet<String> getShortcutTargetsLocked(@ShortcutType int shortcutType) {
+        if (shortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+            return mAccessibilityShortcutKeyTargets;
+        } else if (shortcutType == ACCESSIBILITY_BUTTON) {
+            return mAccessibilityButtonTargets;
+        }
+        return null;
     }
 
-    public void setNavBarMagnificationEnabledLocked(boolean enabled) {
-        mIsNavBarMagnificationEnabled = enabled;
+    /**
+     * Whether or not the given shortcut target is installed in device.
+     *
+     * @param name The shortcut target name
+     * @return true if the shortcut target is installed.
+     */
+    public boolean isShortcutTargetInstalledLocked(String name) {
+        if (TextUtils.isEmpty(name)) {
+            return false;
+        }
+        if (MAGNIFICATION_CONTROLLER_NAME.equals(name)) {
+            return true;
+        }
+
+        final ComponentName componentName = ComponentName.unflattenFromString(name);
+        if (componentName == null) {
+            return false;
+        }
+        if (AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
+                .containsKey(componentName)) {
+            return true;
+        }
+        if (getInstalledServiceInfoLocked(componentName) != null) {
+            return true;
+        }
+        for (int i = 0; i < mInstalledShortcuts.size(); i++) {
+            if (mInstalledShortcuts.get(i).getComponentName().equals(componentName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns installed accessibility service info by the given service component name.
+     */
+    public AccessibilityServiceInfo getInstalledServiceInfoLocked(ComponentName componentName) {
+        for (int i = 0; i < mInstalledServices.size(); i++) {
+            final AccessibilityServiceInfo serviceInfo = mInstalledServices.get(i);
+            if (serviceInfo.getComponentName().equals(componentName)) {
+                return serviceInfo;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns accessibility service connection by the given service component name.
+     */
+    public AccessibilityServiceConnection getServiceConnectionLocked(ComponentName componentName) {
+        return mComponentNameToServiceMap.get(componentName);
     }
 
     public int getNonInteractiveUiTimeoutLocked() {
@@ -557,14 +650,6 @@
         mIsPerformGesturesEnabled = enabled;
     }
 
-    public ComponentName getServiceAssignedToAccessibilityButtonLocked() {
-        return mServiceAssignedToAccessibilityButton;
-    }
-
-    public void setServiceAssignedToAccessibilityButtonLocked(ComponentName componentName) {
-        mServiceAssignedToAccessibilityButton = componentName;
-    }
-
     public ComponentName getServiceChangingSoftKeyboardModeLocked() {
         return mServiceChangingSoftKeyboardMode;
     }
@@ -574,14 +659,6 @@
         mServiceChangingSoftKeyboardMode = serviceChangingSoftKeyboardMode;
     }
 
-    public ComponentName getServiceToEnableWithShortcutLocked() {
-        return mServiceToEnableWithShortcut;
-    }
-
-    public void setServiceToEnableWithShortcutLocked(ComponentName componentName) {
-        mServiceToEnableWithShortcut = componentName;
-    }
-
     public boolean isTextHighContrastEnabledLocked() {
         return mIsTextHighContrastEnabled;
     }
@@ -613,4 +690,28 @@
     public void setUserNonInteractiveUiTimeoutLocked(int timeout) {
         mUserNonInteractiveUiTimeout = timeout;
     }
+
+    // TODO(a11y shortcut): These functions aren't necessary, after the new Settings shortcut Ui
+    //  is merged.
+    boolean isNavBarMagnificationEnabledLocked() {
+        return mIsNavBarMagnificationEnabled;
+    }
+
+    void setNavBarMagnificationEnabledLocked(boolean enabled) {
+        mIsNavBarMagnificationEnabled = enabled;
+    }
+
+    boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() {
+        return mAccessibilityButtonTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
+    }
+
+    ComponentName getServiceAssignedToAccessibilityButtonLocked() {
+        final String targetName = mAccessibilityButtonTargets.isEmpty() ? null
+                : mAccessibilityButtonTargets.valueAt(0);
+        if (targetName == null) {
+            return null;
+        }
+        return ComponentName.unflattenFromString(targetName);
+    }
+    // TODO(a11y shortcut): End
 }
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index be597d7..de6a080 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -766,8 +766,10 @@
             backupManagerService.prepareOperationTimeout(
                     mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT);
             startedAgentRestore = true;
-            mAgent.doRestore(mBackupData, appVersionCode, mNewState,
-                    mEphemeralOpToken, backupManagerService.getBackupManagerBinder());
+            mAgent.doRestoreWithExcludedKeys(mBackupData, appVersionCode, mNewState,
+                    mEphemeralOpToken, backupManagerService.getBackupManagerBinder(),
+                    mExcludedKeys.containsKey(packageName)
+                            ? new ArrayList<>(mExcludedKeys.get(packageName)) : null);
         } catch (Exception e) {
             Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
             EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 21f5f89..53f306b 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -822,4 +822,19 @@
 
     /** Sets the enforcement of reading external storage */
     public abstract void setReadExternalStorageEnforced(boolean enforced);
+
+    /**
+     * Allows the integrity component to respond to the
+     * {@link Intent#ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION package verification
+     * broadcast} to respond to the package manager. The response must include
+     * the {@code verificationCode} which is one of
+     * {@link PackageManager#VERIFICATION_ALLOW} or
+     * {@link PackageManager#VERIFICATION_REJECT}.
+     *
+     * @param verificationId pending package identifier as passed via the
+     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
+     * @param verificationResult either {@link PackageManager#VERIFICATION_ALLOW}
+     *            or {@link PackageManager#VERIFICATION_REJECT}.
+     */
+    public abstract void setIntegrityVerificationResult(int verificationId, int verificationResult);
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 753c117..b719435 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5573,7 +5573,7 @@
      * @param linkProperties the initial link properties of this network. They can be updated
      *         later : see {@link #updateLinkProperties}.
      * @param networkCapabilities the initial capabilites of this network. They can be updated
-     *         later : see {@link #updateNetworkCapabilities}.
+     *         later : see {@link #updateCapabilities}.
      * @param currentScore the initial score of the network. See
      *         {@link NetworkAgentInfo#getCurrentScore}.
      * @param networkMisc metadata about the network. This is never updated.
@@ -5596,7 +5596,7 @@
                 ns, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd,
                 mDnsResolver, mNMS, factorySerialNumber);
         // Make sure the network capabilities reflect what the agent info says.
-        nai.setNetworkCapabilities(mixInCapabilities(nai, nc));
+        nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
         final String extraInfo = networkInfo.getExtraInfo();
         final String name = TextUtils.isEmpty(extraInfo)
                 ? nai.networkCapabilities.getSSID() : extraInfo;
@@ -5950,11 +5950,7 @@
             }
         }
 
-        final NetworkCapabilities prevNc;
-        synchronized (nai) {
-            prevNc = nai.networkCapabilities;
-            nai.setNetworkCapabilities(newNc);
-        }
+        final NetworkCapabilities prevNc = nai.getAndSetNetworkCapabilities(newNc);
 
         updateUids(nai, prevNc, newNc);
 
@@ -5963,7 +5959,7 @@
             // the change we're processing can't affect any requests, it can only affect the listens
             // on this network. We might have been called by rematchNetworkAndRequests when a
             // network changed foreground state.
-            processListenRequests(nai, true);
+            processListenRequests(nai);
         } else {
             // If the requestable capabilities have changed or the score changed, we can't have been
             // called by rematchNetworkAndRequests, so it's safe to start a rematch.
@@ -6271,8 +6267,14 @@
         updateAllVpnsCapabilities();
     }
 
-    private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
+    private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
         // For consistency with previous behaviour, send onLost callbacks before onAvailable.
+        processNewlyLostListenRequests(nai);
+        notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+        processNewlySatisfiedListenRequests(nai);
+    }
+
+    private void processNewlyLostListenRequests(@NonNull final NetworkAgentInfo nai) {
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
             NetworkRequest nr = nri.request;
             if (!nr.isListen()) continue;
@@ -6281,11 +6283,9 @@
                 callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
             }
         }
+    }
 
-        if (capabilitiesChanged) {
-            notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
-        }
-
+    private void processNewlySatisfiedListenRequests(@NonNull final NetworkAgentInfo nai) {
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
             NetworkRequest nr = nri.request;
             if (!nr.isListen()) continue;
@@ -6468,19 +6468,20 @@
         // before LegacyTypeTracker sends legacy broadcasts
         for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
 
-        // Second pass: process all listens.
-        if (wasBackgroundNetwork != newNetwork.isBackgroundNetwork()) {
-            // TODO : most of the following is useless because the only thing that changed
-            // here is whether the network is a background network. Clean this up.
+        // Finally, process listen requests and update capabilities if the background state has
+        // changed for this network. For consistency with previous behavior, send onLost callbacks
+        // before onAvailable.
+        processNewlyLostListenRequests(newNetwork);
 
-            NetworkCapabilities newNc = mixInCapabilities(newNetwork,
+        // Maybe the network changed background states. Update its capabilities.
+        final boolean backgroundChanged = wasBackgroundNetwork != newNetwork.isBackgroundNetwork();
+        if (backgroundChanged) {
+            final NetworkCapabilities newNc = mixInCapabilities(newNetwork,
                     newNetwork.networkCapabilities);
 
-            if (Objects.equals(newNetwork.networkCapabilities, newNc)) return;
-
             final int oldPermission = getNetworkPermission(newNetwork.networkCapabilities);
             final int newPermission = getNetworkPermission(newNc);
-            if (oldPermission != newPermission && newNetwork.created && !newNetwork.isVPN()) {
+            if (oldPermission != newPermission) {
                 try {
                     mNMS.setNetworkPermission(newNetwork.network.netId, newPermission);
                 } catch (RemoteException e) {
@@ -6488,53 +6489,11 @@
                 }
             }
 
-            final NetworkCapabilities prevNc;
-            synchronized (newNetwork) {
-                prevNc = newNetwork.networkCapabilities;
-                newNetwork.setNetworkCapabilities(newNc);
-            }
-
-            updateUids(newNetwork, prevNc, newNc);
-
-            if (newNetwork.getCurrentScore() == score
-                    && newNc.equalRequestableCapabilities(prevNc)) {
-                // If the requestable capabilities haven't changed, and the score hasn't changed,
-                // then the change we're processing can't affect any requests, it can only affect
-                // the listens on this network.
-                processListenRequests(newNetwork, true);
-            } else {
-                rematchAllNetworksAndRequests();
-                notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_CAP_CHANGED);
-            }
-
-            if (prevNc != null) {
-                final boolean oldMetered = prevNc.isMetered();
-                final boolean newMetered = newNc.isMetered();
-                final boolean meteredChanged = oldMetered != newMetered;
-
-                if (meteredChanged) {
-                    maybeNotifyNetworkBlocked(newNetwork, oldMetered, newMetered,
-                            mRestrictBackground, mRestrictBackground);
-                }
-
-                final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING)
-                        != newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
-
-                // Report changes that are interesting for network statistics tracking.
-                if (meteredChanged || roamingChanged) {
-                    notifyIfacesChangedForNetworkStats();
-                }
-            }
-
-            if (!newNc.hasTransport(TRANSPORT_VPN)) {
-                // Tell VPNs about updated capabilities, since they may need to
-                // bubble those changes through.
-                updateAllVpnsCapabilities();
-            }
-
-        } else {
-            processListenRequests(newNetwork, false);
+            newNetwork.getAndSetNetworkCapabilities(newNc);
+            notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
+
+        processNewlySatisfiedListenRequests(newNetwork);
     }
 
     /**
@@ -6719,9 +6678,8 @@
 
             // NetworkCapabilities need to be set before sending the private DNS config to
             // NetworkMonitor, otherwise NetworkMonitor cannot determine if validation is required.
-            synchronized (networkAgent) {
-                networkAgent.setNetworkCapabilities(networkAgent.networkCapabilities);
-            }
+            networkAgent.getAndSetNetworkCapabilities(networkAgent.networkCapabilities);
+
             handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
             updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
                     null);
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 54dfc98..6300ab8 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -343,11 +343,6 @@
                     onLocationModeChangedLocked(userId);
                 }
             });
-            mSettingsStore.addOnLocationProvidersAllowedChangedListener((userId) -> {
-                synchronized (mLock) {
-                    onProviderAllowedChangedLocked(userId);
-                }
-            });
             mSettingsStore.addOnBackgroundThrottleIntervalChangedListener(() -> {
                 synchronized (mLock) {
                     onBackgroundThrottleIntervalChangedLocked();
@@ -474,18 +469,11 @@
         }
 
         Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION);
-        intent.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabled());
+        intent.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabledForUser(userId));
         mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
 
         for (LocationProvider p : mProviders) {
-            p.onLocationModeChangedLocked(userId);
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void onProviderAllowedChangedLocked(int userId) {
-        for (LocationProvider p : mProviders) {
-            p.onAllowedChangedLocked(userId);
+            p.onUseableChangedLocked(userId);
         }
     }
 
@@ -649,7 +637,7 @@
 
         if (GnssManagerService.isGnssSupported()) {
             // Create a gps location provider manager
-            LocationProvider gnssProviderManager = new LocationProvider(GPS_PROVIDER, true);
+            LocationProvider gnssProviderManager = new LocationProvider(GPS_PROVIDER);
             mRealProviders.add(gnssProviderManager);
             addProviderLocked(gnssProviderManager);
 
@@ -680,7 +668,7 @@
         ensureFallbackFusedProviderPresentLocked(pkgs);
 
         // bind to network provider
-        LocationProvider networkProviderManager = new LocationProvider(NETWORK_PROVIDER, true);
+        LocationProvider networkProviderManager = new LocationProvider(NETWORK_PROVIDER);
         LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
                 mContext,
                 networkProviderManager,
@@ -793,8 +781,8 @@
 
         // let providers know the current user has changed
         for (LocationProvider p : mProviders) {
-            p.onCurrentUserChangedLocked(oldUserId);
-            p.onCurrentUserChangedLocked(mCurrentUserId);
+            p.onUseableChangedLocked(oldUserId);
+            p.onUseableChangedLocked(mCurrentUserId);
         }
     }
 
@@ -805,9 +793,6 @@
 
         private final String mName;
 
-        // whether this provider should respect LOCATION_PROVIDERS_ALLOWED (ie gps and network)
-        private final boolean mIsManagedBySettings;
-
         // remember to clear binder identity before invoking any provider operation
         @GuardedBy("mLock")
         @Nullable
@@ -816,8 +801,6 @@
         @GuardedBy("mLock")
         private SparseArray<Boolean> mUseable;  // combined state for each user id
         @GuardedBy("mLock")
-        private boolean mAllowed;  // state of LOCATION_PROVIDERS_ALLOWED
-        @GuardedBy("mLock")
         private boolean mEnabled;  // state of provider
 
         @GuardedBy("mLock")
@@ -825,27 +808,19 @@
         private ProviderProperties mProperties;
 
         private LocationProvider(String name) {
-            this(name, false);
-        }
-
-        private LocationProvider(String name, boolean isManagedBySettings) {
             mName = name;
-            mIsManagedBySettings = isManagedBySettings;
 
             mProvider = null;
             mUseable = new SparseArray<>(1);
-            mAllowed = !mIsManagedBySettings;
             mEnabled = false;
             mProperties = null;
 
-            if (mIsManagedBySettings) {
-                // since we assume providers are disabled by default
-                Settings.Secure.putStringForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        "-" + mName,
-                        mCurrentUserId);
-            }
+            // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
+            Settings.Secure.putStringForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                    "-" + mName,
+                    mCurrentUserId);
         }
 
         @GuardedBy("mLock")
@@ -861,7 +836,7 @@
 
             // it would be more correct to call this for all users, but we know this can only
             // affect the current user since providers are disabled for non-current users
-            onUseableChangedLocked(false, mCurrentUserId);
+            onUseableChangedLocked(mCurrentUserId);
         }
 
         public String getName() {
@@ -931,9 +906,6 @@
             pw.println("useable=" + isUseableLocked(mCurrentUserId));
             if (!isUseableLocked(mCurrentUserId)) {
                 pw.println("attached=" + (mProvider != null));
-                if (mIsManagedBySettings) {
-                    pw.println("allowed=" + mAllowed);
-                }
                 pw.println("enabled=" + mEnabled);
             }
 
@@ -997,7 +969,7 @@
 
                 // it would be more correct to call this for all users, but we know this can only
                 // affect the current user since providers are disabled for non-current users
-                onUseableChangedLocked(false, mCurrentUserId);
+                onUseableChangedLocked(mCurrentUserId);
             }
         }
 
@@ -1009,43 +981,6 @@
         }
 
         @GuardedBy("mLock")
-        public void onLocationModeChangedLocked(int userId) {
-            if (!isCurrentProfileLocked(userId)) {
-                return;
-            }
-
-            onUseableChangedLocked(false, userId);
-        }
-
-        @GuardedBy("mLock")
-        public void onAllowedChangedLocked(int userId) {
-            if (!isCurrentProfileLocked(userId)) {
-                return;
-            }
-
-            if (mIsManagedBySettings) {
-                boolean allowed = mSettingsStore.getLocationProvidersAllowed(
-                        mCurrentUserId).contains(mName);
-
-                if (allowed == mAllowed) {
-                    return;
-                }
-
-                if (D) {
-                    Log.d(TAG, mName + " provider allowed is now " + mAllowed);
-                }
-
-                mAllowed = allowed;
-                onUseableChangedLocked(true, userId);
-            }
-        }
-
-        @GuardedBy("mLock")
-        public void onCurrentUserChangedLocked(int userId) {
-            onUseableChangedLocked(false, userId);
-        }
-
-        @GuardedBy("mLock")
         public boolean isUseableLocked() {
             return isUseableLocked(mCurrentUserId);
         }
@@ -1056,38 +991,13 @@
         }
 
         @GuardedBy("mLock")
-        public void onUseableChangedLocked(boolean isAllowedChanged, int userId) {
+        public void onUseableChangedLocked(int userId) {
             // if any property that contributes to "useability" here changes state, it MUST result
             // in a direct or indrect call to onUseableChangedLocked. this allows the provider to
             // guarantee that it will always eventually reach the correct state.
-            boolean useableIgnoringAllowed = mProvider != null && mProviders.contains(this)
+            boolean useable = mProvider != null && mProviders.contains(this)
                     && isCurrentProfileLocked(userId) && isLocationEnabledForUser(userId)
                     && mEnabled;
-            boolean useable = useableIgnoringAllowed && mAllowed;
-
-            // update deprecated provider allowed settings for backwards compatibility
-            if (mIsManagedBySettings) {
-                // a "-" change derived from the allowed setting should not be overwritten, but a
-                // "+" change should be corrected if necessary
-                if (useableIgnoringAllowed && !isAllowedChanged) {
-                    Settings.Secure.putStringForUser(
-                            mContext.getContentResolver(),
-                            Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                            "+" + mName,
-                            userId);
-                } else if (!useableIgnoringAllowed) {
-                    Settings.Secure.putStringForUser(
-                            mContext.getContentResolver(),
-                            Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                            "-" + mName,
-                            userId);
-                }
-
-                Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION);
-                intent.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName);
-                intent.putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, useable);
-                mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
-            }
 
             if (useable == isUseableLocked(userId)) {
                 return;
@@ -1098,6 +1008,21 @@
                 Log.d(TAG, "[u" + userId + "] " + mName + " provider useable = " + useable);
             }
 
+            // fused and passive provider never get public updates for legacy reasons
+            if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
+                // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
+                Settings.Secure.putStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        (useable ? "+" : "-") + mName,
+                        userId);
+
+                Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION);
+                intent.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName);
+                intent.putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, useable);
+                mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+            }
+
             if (!useable) {
                 // 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
@@ -1541,12 +1466,9 @@
 
         mProviders.add(provider);
 
-        // allowed state may change while provider was inactive
-        provider.onAllowedChangedLocked(mCurrentUserId);
-
         // it would be more correct to call this for all users, but we know this can only
         // affect the current user since providers are disabled for non-current users
-        provider.onUseableChangedLocked(false, mCurrentUserId);
+        provider.onUseableChangedLocked(mCurrentUserId);
     }
 
     @GuardedBy("mLock")
@@ -1554,7 +1476,7 @@
         if (mProviders.remove(provider)) {
             // it would be more correct to call this for all users, but we know this can only
             // affect the current user since providers are disabled for non-current users
-            provider.onUseableChangedLocked(false, mCurrentUserId);
+            provider.onUseableChangedLocked(mCurrentUserId);
         }
     }
 
@@ -2805,10 +2727,6 @@
         }
     }
 
-    private boolean isLocationEnabled() {
-        return isLocationEnabledForUser(mCurrentUserId);
-    }
-
     @Override
     public boolean isLocationEnabledForUser(int userId) {
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
@@ -3247,7 +3165,7 @@
                     + TimeUtils.formatDuration(SystemClock.elapsedRealtime()));
             ipw.println("Current user: " + mCurrentUserId + " " + Arrays.toString(
                     mCurrentUserProfiles));
-            ipw.println("Location Mode: " + isLocationEnabled());
+            ipw.println("Location Mode: " + isLocationEnabledForUser(mCurrentUserId));
             ipw.println("Battery Saver Location Mode: "
                     + locationPowerSaveModeToString(mBatterySaverMode));
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 840b7af..0d496b6 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -58,6 +58,7 @@
 import android.net.NetworkStats;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.TetherConfigParcel;
 import android.net.TetherStatsParcel;
 import android.net.UidRange;
 import android.net.UidRangeParcel;
@@ -1023,7 +1024,10 @@
         NetworkStack.checkNetworkStackPermission(mContext);
         // an odd number of addrs will fail
         try {
-            mNetdService.tetherStartWithConfiguration(usingLegacyDnsProxy, dhcpRange);
+            final TetherConfigParcel config = new TetherConfigParcel();
+            config.usingLegacyDnsProxy = usingLegacyDnsProxy;
+            config.dhcpRanges = dhcpRange;
+            mNetdService.tetherStartWithConfiguration(config);
         } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 521b393..deff440 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -29,6 +29,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.SystemProperties;
 import android.provider.DeviceConfig;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -82,6 +83,12 @@
     static final String PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED =
             "watchdog_explicit_health_check_enabled";
 
+    // TODO: make the following values configurable via DeviceConfig
+    private static final long NATIVE_CRASH_POLLING_INTERVAL_MILLIS =
+            TimeUnit.SECONDS.toMillis(30);
+    private static final long NUMBER_OF_NATIVE_CRASH_POLLS = 10;
+
+
     public static final int FAILURE_REASON_UNKNOWN = 0;
     public static final int FAILURE_REASON_NATIVE_CRASH = 1;
     public static final int FAILURE_REASON_EXPLICIT_HEALTH_CHECK = 2;
@@ -110,6 +117,8 @@
     // Whether explicit health checks are enabled or not
     private static final boolean DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED = true;
 
+    private long mNumberOfNativeCrashPollsRemaining;
+
     private static final int DB_VERSION = 1;
     private static final String TAG_PACKAGE_WATCHDOG = "package-watchdog";
     private static final String TAG_PACKAGE = "package";
@@ -188,6 +197,7 @@
         mHealthCheckController = controller;
         mConnectivityModuleConnector = connectivityModuleConnector;
         mSystemClock = clock;
+        mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
         loadFromFile();
     }
 
@@ -336,38 +346,70 @@
                 if (mAllObservers.isEmpty()) {
                     return;
                 }
+                boolean requiresImmediateAction = (failureReason == FAILURE_REASON_NATIVE_CRASH
+                        || failureReason == FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
+                if (requiresImmediateAction) {
+                    handleFailureImmediately(packages, failureReason);
+                } else {
+                    for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
+                        VersionedPackage versionedPackage = packages.get(pIndex);
+                        // Observer that will receive failure for versionedPackage
+                        PackageHealthObserver currentObserverToNotify = null;
+                        int currentObserverImpact = Integer.MAX_VALUE;
 
-                for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
-                    VersionedPackage versionedPackage = packages.get(pIndex);
-                    // Observer that will receive failure for versionedPackage
-                    PackageHealthObserver currentObserverToNotify = null;
-                    int currentObserverImpact = Integer.MAX_VALUE;
-
-                    // Find observer with least user impact
-                    for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
-                        ObserverInternal observer = mAllObservers.valueAt(oIndex);
-                        PackageHealthObserver registeredObserver = observer.registeredObserver;
-                        if (registeredObserver != null
-                                && observer.onPackageFailureLocked(
-                                        versionedPackage.getPackageName())) {
-                            int impact = registeredObserver.onHealthCheckFailed(versionedPackage);
-                            if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
-                                    && impact < currentObserverImpact) {
-                                currentObserverToNotify = registeredObserver;
-                                currentObserverImpact = impact;
+                        // Find observer with least user impact
+                        for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
+                            ObserverInternal observer = mAllObservers.valueAt(oIndex);
+                            PackageHealthObserver registeredObserver = observer.registeredObserver;
+                            if (registeredObserver != null
+                                    && observer.onPackageFailureLocked(
+                                    versionedPackage.getPackageName())) {
+                                int impact = registeredObserver.onHealthCheckFailed(
+                                        versionedPackage, failureReason);
+                                if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
+                                        && impact < currentObserverImpact) {
+                                    currentObserverToNotify = registeredObserver;
+                                    currentObserverImpact = impact;
+                                }
                             }
                         }
-                    }
 
-                    // Execute action with least user impact
-                    if (currentObserverToNotify != null) {
-                        currentObserverToNotify.execute(versionedPackage, failureReason);
+                        // Execute action with least user impact
+                        if (currentObserverToNotify != null) {
+                            currentObserverToNotify.execute(versionedPackage, failureReason);
+                        }
                     }
                 }
             }
         });
     }
 
+    /**
+     * For native crashes or explicit health check failures, call directly into each observer to
+     * mitigate the error without going through failure threshold logic.
+     */
+    private void handleFailureImmediately(List<VersionedPackage> packages,
+            @FailureReasons int failureReason) {
+        VersionedPackage failingPackage = packages.size() > 0 ? packages.get(0) : null;
+        PackageHealthObserver currentObserverToNotify = null;
+        int currentObserverImpact = Integer.MAX_VALUE;
+        for (ObserverInternal observer: mAllObservers.values()) {
+            PackageHealthObserver registeredObserver = observer.registeredObserver;
+            if (registeredObserver != null) {
+                int impact = registeredObserver.onHealthCheckFailed(
+                        failingPackage, failureReason);
+                if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
+                        && impact < currentObserverImpact) {
+                    currentObserverToNotify = registeredObserver;
+                    currentObserverImpact = impact;
+                }
+            }
+        }
+        if (currentObserverToNotify != null) {
+            currentObserverToNotify.execute(failingPackage,  failureReason);
+        }
+    }
+
     // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file? Also
     // avoid holding lock?
     // This currently adds about 7ms extra to shutdown thread
@@ -400,6 +442,37 @@
         }
     }
 
+    /**
+     * This method should be only called on mShortTaskHandler, since it modifies
+     * {@link #mNumberOfNativeCrashPollsRemaining}.
+     */
+    private void checkAndMitigateNativeCrashes() {
+        mNumberOfNativeCrashPollsRemaining--;
+        // Check if native watchdog reported a crash
+        if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
+            // We rollback everything available when crash is unattributable
+            onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH);
+            // we stop polling after an attempt to execute rollback, regardless of whether the
+            // attempt succeeds or not
+        } else {
+            if (mNumberOfNativeCrashPollsRemaining > 0) {
+                mShortTaskHandler.postDelayed(() -> checkAndMitigateNativeCrashes(),
+                        NATIVE_CRASH_POLLING_INTERVAL_MILLIS);
+            }
+        }
+    }
+
+    /**
+     * Since this method can eventually trigger a rollback, it should be called
+     * only once boot has completed {@code onBootCompleted} and not earlier, because the install
+     * session must be entirely completed before we try to rollback.
+     */
+    public void scheduleCheckAndMitigateNativeCrashes() {
+        Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check "
+                + "and mitigate native crashes");
+        mShortTaskHandler.post(()->checkAndMitigateNativeCrashes());
+    }
+
     /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */
     @Retention(SOURCE)
     @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_NONE,
@@ -422,17 +495,28 @@
         /**
          * Called when health check fails for the {@code versionedPackage}.
          *
+         * @param versionedPackage the package that is failing. This may be null if a native
+         *                          service is crashing.
+         * @param failureReason   the type of failure that is occurring.
+         *
+         *
          * @return any one of {@link PackageHealthObserverImpact} to express the impact
          * to the user on {@link #execute}
          */
-        @PackageHealthObserverImpact int onHealthCheckFailed(VersionedPackage versionedPackage);
+        @PackageHealthObserverImpact int onHealthCheckFailed(
+                @Nullable VersionedPackage versionedPackage,
+                @FailureReasons int failureReason);
 
         /**
          * Executes mitigation for {@link #onHealthCheckFailed}.
          *
+         * @param versionedPackage the package that is failing. This may be null if a native
+         *                          service is crashing.
+         * @param failureReason   the type of failure that is occurring.
          * @return {@code true} if action was executed successfully, {@code false} otherwise
          */
-        boolean execute(VersionedPackage versionedPackage, @FailureReasons int failureReason);
+        boolean execute(@Nullable VersionedPackage versionedPackage,
+                @FailureReasons int failureReason);
 
         // TODO(b/120598832): Ensure uniqueness?
         /**
@@ -448,6 +532,17 @@
         default boolean isPersistent() {
             return false;
         }
+
+        /**
+         * Returns {@code true} if this observer wishes to observe the given package, {@code false}
+         * otherwise
+         *
+         * <p> A persistent observer may choose to start observing certain failing packages, even if
+         * it has not explicitly asked to watch the package with {@link #startObservingHealth}.
+         */
+        default boolean mayObservePackage(String packageName) {
+            return false;
+        }
     }
 
     long getTriggerFailureCount() {
@@ -784,13 +879,8 @@
                         Slog.wtf(TAG, "NetworkStack failed but could not find its package");
                         return;
                     }
-                    // This is a severe failure and recovery should be attempted immediately.
-                    // TODO: have a better way to handle such failures.
                     final List<VersionedPackage> pkgList = Collections.singletonList(pkg);
-                    final long failureCount = getTriggerFailureCount();
-                    for (int i = 0; i < failureCount; i++) {
-                        onPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
-                    }
+                    onPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
                 });
     }
 
@@ -937,6 +1027,11 @@
          */
         @GuardedBy("mLock")
         public boolean onPackageFailureLocked(String packageName) {
+            if (packages.get(packageName) == null && registeredObserver.isPersistent()
+                    && registeredObserver.mayObservePackage(packageName)) {
+                packages.put(packageName, sPackageWatchdog.newMonitoredPackage(
+                        packageName, DEFAULT_OBSERVING_DURATION_MS, false));
+            }
             MonitoredPackage p = packages.get(packageName);
             if (p != null) {
                 return p.onFailureLocked();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 822fc90..0a6473a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
 import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
@@ -49,6 +50,7 @@
 import android.Manifest;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.KeyguardManager;
@@ -253,6 +255,11 @@
         public void onCleanupUser(int userHandle) {
             mStorageManagerService.onCleanupUser(userHandle);
         }
+
+        @Override
+        public void onStopUser(int userHandle) {
+            mStorageManagerService.onStopUser(userHandle);
+        }
     }
 
     private static final boolean DEBUG_EVENTS = false;
@@ -1075,6 +1082,15 @@
         }
     }
 
+    private void onStopUser(int userId) {
+        Slog.i(TAG, "onStopUser " + userId);
+        try {
+            mStorageSessionController.onUserStopping(userId);
+        } catch (Exception e) {
+            Slog.wtf(TAG, e);
+        }
+    }
+
     private boolean supportsBlockCheckpoint() throws RemoteException {
         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
         return mVold.supportsBlockCheckpoint();
@@ -1309,6 +1325,15 @@
             Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
             return;
         }
+        final ActivityManagerInternal amInternal =
+                LocalServices.getService(ActivityManagerInternal.class);
+
+        if (mIsFuseEnabled && vol.mountUserId >= 0
+                && !amInternal.isUserRunning(vol.mountUserId, 0)) {
+            Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user "
+                    + Integer.toString(vol.mountUserId) + " is no longer running.");
+            return;
+        }
 
         if (vol.type == VolumeInfo.TYPE_EMULATED) {
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
@@ -2229,6 +2254,11 @@
     }
 
     private void remountUidExternalStorage(int uid, int mode) {
+        if (uid == Process.SYSTEM_UID) {
+            // No need to remount uid for system because it has all access anyways
+            return;
+        }
+
         try {
             mVold.remountUid(uid, mode);
         } catch (Exception e) {
@@ -3399,7 +3429,13 @@
         public void opChanged(int op, int uid, String packageName) throws RemoteException {
             if (!ENABLE_ISOLATED_STORAGE) return;
 
-            remountUidExternalStorage(uid, getMountMode(uid, packageName));
+            int mountMode = getMountMode(uid, packageName);
+            boolean isUidActive = LocalServices.getService(ActivityManagerInternal.class)
+                    .getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT;
+
+            if (isUidActive) {
+                remountUidExternalStorage(uid, mountMode);
+            }
         }
     };
 
@@ -4092,6 +4128,13 @@
             }
         }
 
+        @Override
+        public void resetUser(int userId) {
+            // TODO(b/145931219): ideally, we only reset storage for the user in question,
+            // but for now, reset everything.
+            mHandler.obtainMessage(H_RESET).sendToTarget();
+        }
+
         public boolean hasExternalStorage(int uid, String packageName) {
             // No need to check for system uid. This avoids a deadlock between
             // PackageManagerService and AppOpsService.
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 3fe82f0..6bc117b 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -357,7 +357,7 @@
                         SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
                         SubscriptionManager.getDefaultSubscriptionId());
                 int newDefaultPhoneId = intent.getIntExtra(
-                        PhoneConstants.PHONE_KEY,
+                        SubscriptionManager.EXTRA_SLOT_INDEX,
                         SubscriptionManager.getPhoneId(newDefaultSubId));
                 if (DBG) {
                     log("onReceive:current mDefaultSubId=" + mDefaultSubId
@@ -446,9 +446,9 @@
             mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
             mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
             mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
-            mCallQuality[i] = new CallQuality();
+            mCallQuality[i] = createCallQuality();
             mCallAttributes[i] = new CallAttributes(new PreciseCallState(),
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality());
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality());
             mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
             mPreciseCallState[i] = new PreciseCallState();
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
@@ -541,9 +541,9 @@
             mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
             mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
             mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
-            mCallQuality[i] = new CallQuality();
+            mCallQuality[i] = createCallQuality();
             mCallAttributes[i] = new CallAttributes(new PreciseCallState(),
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality());
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality());
             mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
             mPreciseCallState[i] = new PreciseCallState();
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
@@ -1572,8 +1572,6 @@
         }
         broadcastDataConnectionStateChanged(state, isDataAllowed, apn, apnType, linkProperties,
                 networkCapabilities, roaming, subId);
-        broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn,
-                linkProperties, DataFailCause.NONE);
     }
 
     public void notifyDataConnectionFailed(String apnType) {
@@ -1613,9 +1611,6 @@
             handleRemoveListLocked();
         }
         broadcastDataConnectionFailed(apnType, subId);
-        broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, null, null,
-                DataFailCause.NONE);
     }
 
     public void notifyCellLocation(Bundle cellLocation) {
@@ -1705,7 +1700,7 @@
                     if (mPreciseCallState[phoneId].getForegroundCallState()
                             != PreciseCallState.PRECISE_CALL_STATE_ACTIVE) {
                         mCallNetworkType[phoneId] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-                        mCallQuality[phoneId] = new CallQuality();
+                        mCallQuality[phoneId] = createCallQuality();
                     }
                     mCallAttributes[phoneId] = new CallAttributes(mPreciseCallState[phoneId],
                             mCallNetworkType[phoneId], mCallQuality[phoneId]);
@@ -1733,8 +1728,6 @@
             }
             handleRemoveListLocked();
         }
-        broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState,
-                backgroundCallState);
     }
 
     public void notifyDisconnectCause(int phoneId, int subId, int disconnectCause,
@@ -1816,8 +1809,6 @@
 
             handleRemoveListLocked();
         }
-        broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, null, failCause);
     }
 
     @Override
@@ -2147,6 +2138,16 @@
     // the legacy intent broadcasting
     //
 
+    // Legacy intent action.
+    /** Fired when a subscription's phone state changes. */
+    private static final String ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED =
+            "android.intent.action.SUBSCRIPTION_PHONE_STATE";
+
+    // Legacy intent extra keys, copied from PhoneConstants.
+    // Used in legacy intents sent here, for backward compatibility.
+    private static final String PHONE_CONSTANTS_SLOT_KEY = "slot";
+    private static final String PHONE_CONSTANTS_SUBSCRIPTION_KEY = "subscription";
+
     private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) {
         long ident = Binder.clearCallingIdentity();
         try {
@@ -2163,9 +2164,10 @@
         state.fillInNotifierBundle(data);
         intent.putExtras(data);
         // Pass the subscription along with the intent.
-        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+        intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
         intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
-        intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
+        intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
+        intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
@@ -2184,8 +2186,8 @@
         Bundle data = new Bundle();
         signalStrength.fillInNotifierBundle(data);
         intent.putExtras(data);
-        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
-        intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
+        intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
+        intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
@@ -2220,13 +2222,14 @@
         // If a valid subId was specified, we should fire off a subId-specific state
         // change intent and include the subId.
         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            intent.setAction(PhoneConstants.ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED);
-            intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+            intent.setAction(ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED);
+            intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
             intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
         }
         // If the phoneId is invalid, the broadcast is for overall call state.
         if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
-            intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
+            intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
+            intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
         }
 
         // Wakeup apps for the (SUBSCRIPTION_)PHONE_STATE broadcast.
@@ -2286,44 +2289,17 @@
 
         intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
         intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
-        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+        intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     private void broadcastDataConnectionFailed(String apnType, int subId) {
         Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
         intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
-        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+        intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
-    private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState,
-                                                  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);
-        intent.putExtra(TelephonyManager.EXTRA_BACKGROUND_CALL_STATE, backgroundCallState);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
-                android.Manifest.permission.READ_PRECISE_PHONE_STATE);
-    }
-
-    private void broadcastPreciseDataConnectionStateChanged(int state, int networkType,
-            String apnType, String apn, LinkProperties linkProperties,
-            @DataFailureCause int failCause) {
-        Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED);
-        intent.putExtra(TelephonyManager.EXTRA_STATE, state);
-        intent.putExtra(PhoneConstants.DATA_NETWORK_TYPE_KEY, networkType);
-        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_FAILURE_CAUSE_KEY, failCause);
-
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
-                android.Manifest.permission.READ_PRECISE_PHONE_STATE);
-    }
-
     private void enforceNotifyPermissionOrCarrierPrivilege(String method) {
         if (checkNotifyPermission()) {
             return;
@@ -2720,4 +2696,8 @@
         }
     }
 
+    /** Returns a new CallQuality object with default values. */
+    private static CallQuality createCallQuality() {
+        return new CallQuality(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+    }
 }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index b1e2c0f..5bec7a3 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -56,6 +56,7 @@
 import android.service.vr.IVrStateCallbacks;
 import android.util.ArraySet;
 import android.util.Slog;
+
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.DisableCarModeActivity;
@@ -73,8 +74,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import static android.content.Intent.ACTION_SCREEN_OFF;
-
 final class UiModeManagerService extends SystemService {
     private static final String TAG = UiModeManager.class.getSimpleName();
     private static final boolean LOG = false;
@@ -97,10 +96,15 @@
     private boolean mCarModeEnabled = false;
     private boolean mCharging = false;
     private boolean mPowerSave = false;
+    // Do not change configuration now. wait until screen turns off.
+    // This prevents jank and activity restart when the user
+    // is actively using the device
+    private boolean mWaitForScreenOff = false;
     private int mDefaultUiModeType;
     private boolean mCarModeKeepsScreenOn;
     private boolean mDeskModeKeepsScreenOn;
     private boolean mTelevision;
+    private boolean mCar;
     private boolean mWatch;
     private boolean mVrHeadset;
     private boolean mComputedNightMode;
@@ -208,24 +212,27 @@
         public void onTwilightStateChanged(@Nullable TwilightState state) {
             synchronized (mLock) {
                 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                    final IntentFilter intentFilter =
-                            new IntentFilter(ACTION_SCREEN_OFF);
-                    getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
+                    if (mCar) {
+                        updateLocked(0, 0);
+                    } else {
+                        registerScreenOffEvent();
+                    }
                 }
             }
         }
     };
 
+    /**
+     *  DO NOT USE DIRECTLY
+     *  see register registerScreenOffEvent and unregisterScreenOffEvent
+     */
     private final BroadcastReceiver mOnScreenOffHandler = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             synchronized (mLock) {
+                // must unregister first before updating
+                unregisterScreenOffEvent();
                 updateLocked(0, 0);
-                try {
-                    getContext().unregisterReceiver(mOnScreenOffHandler);
-                } catch (IllegalArgumentException e) {
-                    // we ignore this exception if the receiver is unregistered already.
-                }
             }
         }
     };
@@ -327,6 +334,7 @@
         final PackageManager pm = context.getPackageManager();
         mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
                 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+        mCar = pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
         mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
 
         updateNightModeFromSettings(context, res, UserHandle.getCallingUserId());
@@ -335,7 +343,7 @@
         SystemServerInitThreadPool.submit(() -> {
             synchronized (mLock) {
                 updateConfigurationLocked();
-                sendConfigurationLocked();
+                applyConfigurationExternallyLocked();
             }
 
         }, TAG + ".onStart");
@@ -404,6 +412,22 @@
         return oldNightMode != mNightMode;
     }
 
+    private void registerScreenOffEvent() {
+        mWaitForScreenOff = true;
+        final IntentFilter intentFilter =
+                new IntentFilter(Intent.ACTION_SCREEN_OFF);
+        getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
+    }
+
+    private void unregisterScreenOffEvent() {
+        mWaitForScreenOff = false;
+        try {
+            getContext().unregisterReceiver(mOnScreenOffHandler);
+        } catch (IllegalArgumentException e) {
+            // we ignore this exception if the receiver is unregistered already.
+        }
+    }
+
     private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
         @Override
         public void enableCarMode(@UiModeManager.EnableCarMode int flags,
@@ -525,11 +549,7 @@
                 synchronized (mLock) {
                     if (mNightMode != mode) {
                         if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                            try {
-                                getContext().unregisterReceiver(mOnScreenOffHandler);
-                            } catch (IllegalArgumentException e) {
-                                // we ignore this exception if the receiver is unregistered already.
-                            }
+                            unregisterScreenOffEvent();
                         }
                         // Only persist setting if not in car mode
                         if (!mCarModeEnabled) {
@@ -541,12 +561,11 @@
 
                         mNightMode = mode;
                         mNightModeOverride = mode;
-                        //on screen off will update configuration instead
-                        if (mNightMode != UiModeManager.MODE_NIGHT_AUTO) {
+                        // on screen off will update configuration instead
+                        if (mNightMode != UiModeManager.MODE_NIGHT_AUTO || mCar) {
                             updateLocked(0, 0);
                         } else {
-                            getContext().registerReceiver(
-                                    mOnScreenOffHandler, new IntentFilter(ACTION_SCREEN_OFF));
+                            registerScreenOffEvent();
                         }
                     }
                 }
@@ -594,10 +613,7 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                        try {
-                            getContext().unregisterReceiver(mOnScreenOffHandler);
-                        } catch (IllegalArgumentException e) {
-                        }
+                        unregisterScreenOffEvent();
                         mNightModeOverride = active
                                 ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
                     } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
@@ -608,7 +624,7 @@
                         mNightMode = UiModeManager.MODE_NIGHT_NO;
                     }
                     updateConfigurationLocked();
-                    sendConfigurationLocked();
+                    applyConfigurationExternallyLocked();
                     return true;
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -863,12 +879,12 @@
         }
 
         mCurUiMode = uiMode;
-        if (!mHoldingConfiguration) {
+        if (!mHoldingConfiguration || !mWaitForScreenOff) {
             mConfiguration.uiMode = uiMode;
         }
     }
 
-    private void sendConfigurationLocked() {
+    private void applyConfigurationExternallyLocked() {
         if (mSetUiMode != mConfiguration.uiMode) {
             mSetUiMode = mConfiguration.uiMode;
             // load splash screen instead of screenshot
@@ -1052,7 +1068,7 @@
         }
 
         // Send the new configuration.
-        sendConfigurationLocked();
+        applyConfigurationExternallyLocked();
 
         // If we did not start a dock app, then start dreaming if supported.
         if (category != null && !dockAppStarted) {
@@ -1130,7 +1146,6 @@
             final int user = UserHandle.getCallingUserId();
             Secure.putIntForUser(getContext().getContentResolver(),
                     OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
-
         }
     }
 
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index b5cab1f..e101fe0 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1289,6 +1289,33 @@
     }
 
     protected UserAccounts getUserAccounts(int userId) {
+        try {
+            return getUserAccountsNotChecked(userId);
+        } catch (RuntimeException e) {
+            if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+                // Let it go...
+                throw e;
+            }
+            // User accounts database is corrupted, we must wipe out the whole user, otherwise the
+            // system will crash indefinitely
+            Slog.wtf(TAG, "Removing user " + userId + " due to exception (" + e + ") reading its "
+                    + "account database");
+            if (userId == ActivityManager.getCurrentUser() && userId != UserHandle.USER_SYSTEM) {
+                Slog.i(TAG, "Switching to system user first");
+                try {
+                    ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
+                } catch (RemoteException re) {
+                    Slog.e(TAG, "Could not switch to " + UserHandle.USER_SYSTEM + ": " + re);
+                }
+            }
+            if (!getUserManager().removeUserEvenWhenDisallowed(userId)) {
+                Slog.e(TAG, "could not remove user " + userId);
+            }
+            throw e;
+        }
+    }
+
+    private UserAccounts getUserAccountsNotChecked(int userId) {
         synchronized (mUsers) {
             UserAccounts accounts = mUsers.get(userId);
             boolean validateAccounts = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 01f3c26..c2dd046 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2386,7 +2386,8 @@
         mConstants = hasHandlerThread
                 ? new ActivityManagerConstants(mContext, this, mHandler) : null;
         final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
-        mProcessList.init(this, activeUids);
+        mPlatformCompat = null;
+        mProcessList.init(this, activeUids, mPlatformCompat);
         mLowMemDetector = null;
         mOomAdjuster = hasHandlerThread
                 ? new OomAdjuster(this, mProcessList, activeUids, handlerThread) : null;
@@ -2410,7 +2411,6 @@
         mFactoryTest = FACTORY_TEST_OFF;
         mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
         mInternal = new LocalService();
-        mPlatformCompat = null;
     }
 
     // Note: This method is invoked on the main thread but may need to attach various
@@ -2439,7 +2439,9 @@
 
         mConstants = new ActivityManagerConstants(mContext, this, mHandler);
         final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */);
-        mProcessList.init(this, activeUids);
+        mPlatformCompat = (PlatformCompat) ServiceManager.getService(
+                Context.PLATFORM_COMPAT_SERVICE);
+        mProcessList.init(this, activeUids, mPlatformCompat);
         mLowMemDetector = new LowMemDetector(this);
         mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
 
@@ -2547,9 +2549,6 @@
 
         mHiddenApiBlacklist = new HiddenApiSettings(mHandler, mContext);
 
-        mPlatformCompat = (PlatformCompat) ServiceManager.getService(
-                Context.PLATFORM_COMPAT_SERVICE);
-
         Watchdog.getInstance().addMonitor(this);
         Watchdog.getInstance().addThread(mHandler);
 
@@ -5032,9 +5031,7 @@
             bindApplicationTimeMillis = SystemClock.elapsedRealtime();
             mAtmInternal.preBindApplication(app.getWindowProcessController());
             final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
-            long[] disabledCompatChanges = {};
             if (mPlatformCompat != null) {
-                disabledCompatChanges = mPlatformCompat.getDisabledChanges(app.info);
                 mPlatformCompat.resetReporting(app.info);
             }
             if (app.isolatedEntryPoint != null) {
@@ -5053,7 +5050,7 @@
                         app.compat, getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
                         buildSerial, autofillOptions, contentCaptureOptions,
-                        disabledCompatChanges);
+                        app.mDisabledCompatChanges);
             } else {
                 thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                         null, null, null, testMode,
@@ -5063,7 +5060,7 @@
                         app.compat, getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
                         buildSerial, autofillOptions, contentCaptureOptions,
-                        disabledCompatChanges);
+                        app.mDisabledCompatChanges);
             }
             if (profilerInfo != null) {
                 profilerInfo.closeFd();
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 7f1d5a3..79fe610 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2909,6 +2909,12 @@
         final PlatformCompat platformCompat = (PlatformCompat)
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
         String toggleValue = getNextArgRequired();
+        if (toggleValue.equals("reset-all")) {
+            final String packageName = getNextArgRequired();
+            pw.println("Reset all changes for " + packageName + " to default value.");
+            platformCompat.clearOverrides(packageName);
+            return 0;
+        }
         long changeId;
         String changeIdString = getNextArgRequired();
         try {
@@ -3267,9 +3273,14 @@
             pw.println("      without restarting any processes.");
             pw.println("  write");
             pw.println("      Write all pending state to storage.");
-            pw.println("  compat enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
-            pw.println("      Toggles a change either by id or by name for <PACKAGE_NAME>.");
-            pw.println("      It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
+            pw.println("  compat [COMMAND] [...]: sub-commands for toggling app-compat changes.");
+            pw.println("         enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
+            pw.println("            Toggles a change either by id or by name for <PACKAGE_NAME>.");
+            pw.println("            It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
+            pw.println("         reset-all <PACKAGE_NAME>");
+            pw.println("            Removes all existing overrides for all changes for ");
+            pw.println("            <PACKAGE_NAME> (back to default behaviour).");
+            pw.println("            It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
             pw.println();
             Intent.printIntentArgsHelp(pw, "");
         }
diff --git a/services/core/java/com/android/server/am/LmkdConnection.java b/services/core/java/com/android/server/am/LmkdConnection.java
index d1e09db..f41c364 100644
--- a/services/core/java/com/android/server/am/LmkdConnection.java
+++ b/services/core/java/com/android/server/am/LmkdConnection.java
@@ -18,6 +18,7 @@
 
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
@@ -35,9 +36,6 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Lmkd connection to communicate with lowmemorykiller daemon.
@@ -46,7 +44,7 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdConnection" : TAG_AM;
 
     // lmkd reply max size in bytes
-    private static final int LMKD_REPLY_MAX_SIZE = 8;
+    private static final int LMKD_REPLY_MAX_SIZE = 12;
 
     // connection listener interface
     interface LmkdConnectionListener {
@@ -64,6 +62,15 @@
          */
         public boolean isReplyExpected(ByteBuffer replyBuf, ByteBuffer dataReceived,
             int receivedLen);
+
+        /**
+         * Handle the received message if it's unsolicited.
+         *
+         * @param dataReceived The buffer holding received data
+         * @param receivedLen Size of the data received
+         * @return True if the message has been handled correctly, false otherwise.
+         */
+        boolean handleUnsolicitedMessage(ByteBuffer dataReceived, int receivedLen);
     }
 
     private final MessageQueue mMsgQueue;
@@ -187,17 +194,17 @@
                         mReplyBuf.rewind();
                         // wakeup the waiting thread
                         mReplyBufLock.notifyAll();
-                    } else {
-                        // received asynchronous or unexpected packet
+                    } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) {
+                        // received unexpected packet
                         // treat this as an error
                         mReplyBuf = null;
                         mReplyBufLock.notifyAll();
-                        Slog.e(TAG, "Received unexpected packet from lmkd");
+                        Slog.e(TAG, "Received an unexpected packet from lmkd");
                     }
-                } else {
+                } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) {
                     // received asynchronous communication from lmkd
-                    // we don't support this yet
-                    Slog.w(TAG, "Received an asynchronous packet from lmkd");
+                    // but we don't recognize it.
+                    Slog.w(TAG, "Received an unexpected packet from lmkd");
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 32975d7..5378f43 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -101,6 +101,7 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.Watchdog;
+import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.WindowManagerService;
@@ -277,14 +278,16 @@
     // LMK_PROCREMOVE <pid>
     // LMK_PROCPURGE
     // LMK_GETKILLCNT
+    // LMK_PROCKILL
     static final byte LMK_TARGET = 0;
     static final byte LMK_PROCPRIO = 1;
     static final byte LMK_PROCREMOVE = 2;
     static final byte LMK_PROCPURGE = 3;
     static final byte LMK_GETKILLCNT = 4;
+    static final byte LMK_PROCKILL = 5; // Note: this is an unsolicated command
 
     // lmkd reconnect delay in msecs
-    private final static long LMDK_RECONNECT_DELAY_MS = 1000;
+    private static final long LMKD_RECONNECT_DELAY_MS = 1000;
 
     /**
      * How long between a process kill and we actually receive its death recipient
@@ -390,6 +393,12 @@
     ActiveUids mActiveUids;
 
     /**
+     * The listener who is intereted with the lmkd kills.
+     */
+    @GuardedBy("mService")
+    private LmkdKillListener mLmkdKillListener = null;
+
+    /**
      * The currently running isolated processes.
      */
     final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
@@ -405,6 +414,15 @@
     final ArrayMap<AppZygote, ArrayList<ProcessRecord>> mAppZygoteProcesses =
             new ArrayMap<AppZygote, ArrayList<ProcessRecord>>();
 
+    private PlatformCompat mPlatformCompat = null;
+
+    interface LmkdKillListener {
+        /**
+         * Called when there is a process kill by lmkd.
+         */
+        void onLmkdKillOccurred(int pid, int uid);
+    }
+
     final class IsolatedUidRange {
         @VisibleForTesting
         public final int mFirstUid;
@@ -557,7 +575,8 @@
 
     final class KillHandler extends Handler {
         static final int KILL_PROCESS_GROUP_MSG = 4000;
-        static final int LMDK_RECONNECT_MSG = 4001;
+        static final int LMKD_RECONNECT_MSG = 4001;
+        static final int LMKD_PROC_KILLED_MSG = 4002;
 
         public KillHandler(Looper looper) {
             super(looper, null, true);
@@ -571,15 +590,18 @@
                     Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
-                case LMDK_RECONNECT_MSG:
+                case LMKD_RECONNECT_MSG:
                     if (!sLmkdConnection.connect()) {
                         Slog.i(TAG, "Failed to connect to lmkd, retry after " +
-                                LMDK_RECONNECT_DELAY_MS + " ms");
-                        // retry after LMDK_RECONNECT_DELAY_MS
+                                LMKD_RECONNECT_DELAY_MS + " ms");
+                        // retry after LMKD_RECONNECT_DELAY_MS
                         sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage(
-                                KillHandler.LMDK_RECONNECT_MSG), LMDK_RECONNECT_DELAY_MS);
+                                KillHandler.LMKD_RECONNECT_MSG), LMKD_RECONNECT_DELAY_MS);
                     }
                     break;
+                case LMKD_PROC_KILLED_MSG:
+                    handleLmkdProcKilled(msg.arg1 /* pid */, msg.arg2 /* uid */);
+                    break;
 
                 default:
                     super.handleMessage(msg);
@@ -596,9 +618,11 @@
         updateOomLevels(0, 0, false);
     }
 
-    void init(ActivityManagerService service, ActiveUids activeUids) {
+    void init(ActivityManagerService service, ActiveUids activeUids,
+            PlatformCompat platformCompat) {
         mService = service;
         mActiveUids = activeUids;
+        mPlatformCompat = platformCompat;
 
         if (sKillHandler == null) {
             sKillThread = new ServiceThread(TAG + ":kill",
@@ -612,13 +636,15 @@
                             Slog.i(TAG, "Connection with lmkd established");
                             return onLmkdConnect(ostream);
                         }
+
                         @Override
                         public void onDisconnect() {
                             Slog.w(TAG, "Lost connection to lmkd");
                             // start reconnection after delay to let lmkd restart
                             sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage(
-                                    KillHandler.LMDK_RECONNECT_MSG), LMDK_RECONNECT_DELAY_MS);
+                                    KillHandler.LMKD_RECONNECT_MSG), LMKD_RECONNECT_DELAY_MS);
                         }
+
                         @Override
                         public boolean isReplyExpected(ByteBuffer replyBuf,
                                 ByteBuffer dataReceived, int receivedLen) {
@@ -627,6 +653,26 @@
                             return (receivedLen == replyBuf.array().length &&
                                     dataReceived.getInt(0) == replyBuf.getInt(0));
                         }
+
+                        @Override
+                        public boolean handleUnsolicitedMessage(ByteBuffer dataReceived,
+                                int receivedLen) {
+                            if (receivedLen < 4) {
+                                return false;
+                            }
+                            switch (dataReceived.getInt(0)) {
+                                case LMK_PROCKILL:
+                                    if (receivedLen != 12) {
+                                        return false;
+                                    }
+                                    sKillHandler.obtainMessage(KillHandler.LMKD_PROC_KILLED_MSG,
+                                            dataReceived.getInt(4), dataReceived.getInt(8))
+                                            .sendToTarget();
+                                    return true;
+                                default:
+                                    return false;
+                            }
+                        }
                     }
             );
         }
@@ -1303,10 +1349,10 @@
         if (!sLmkdConnection.isConnected()) {
             // try to connect immediately and then keep retrying
             sKillHandler.sendMessage(
-                sKillHandler.obtainMessage(KillHandler.LMDK_RECONNECT_MSG));
+                    sKillHandler.obtainMessage(KillHandler.LMKD_RECONNECT_MSG));
 
             // wait for connection retrying 3 times (up to 3 seconds)
-            if (!sLmkdConnection.waitForConnection(3 * LMDK_RECONNECT_DELAY_MS)) {
+            if (!sLmkdConnection.waitForConnection(3 * LMKD_RECONNECT_DELAY_MS)) {
                 return false;
             }
         }
@@ -1664,6 +1710,10 @@
             Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
                     + " with non-zero pid:" + app.pid);
         }
+        app.mDisabledCompatChanges = null;
+        if (mPlatformCompat != null) {
+            app.mDisabledCompatChanges = mPlatformCompat.getDisabledChanges(app.info);
+        }
         final long startSeq = app.startSeq = ++mProcStartSeqCounter;
         app.setStartParams(uid, hostingRecord, seInfo, startTime);
         app.setUsingWrapper(invokeWith != null
@@ -1826,8 +1876,8 @@
                 startResult = startWebView(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
-                        app.info.dataDir, null, app.info.packageName,
-                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+                        app.info.dataDir, null, app.info.packageName, app.mDisabledCompatChanges,
+                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             } else if (hostingRecord.usesAppZygote()) {
                 final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
 
@@ -1835,14 +1885,15 @@
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, null, app.info.packageName,
-                        /*useUsapPool=*/ false, isTopApp,
-                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+                        /*useUsapPool=*/ false, isTopApp, app.mDisabledCompatChanges,
+                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             } else {
                 startResult = Process.start(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, invokeWith, app.info.packageName, isTopApp,
-                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+                        app.mDisabledCompatChanges,
+                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             }
             checkSlow(startTime, "startProcess: returned from zygote!");
             return startResult;
@@ -3393,4 +3444,28 @@
             }
         }
     }
+
+    void setLmkdKillListener(final LmkdKillListener listener) {
+        synchronized (mService) {
+            mLmkdKillListener = listener;
+        }
+    }
+
+    private void handleLmkdProcKilled(final int pid, final int uid) {
+        // Log only now
+        if (DEBUG_PROCESSES) {
+            Slog.i(TAG, "lmkd kill: pid=" + pid + " uid=" + uid);
+        }
+
+        if (mService == null) {
+            return;
+        }
+        // Notify any interesed party regarding the lmkd kills
+        synchronized (mService) {
+            final LmkdKillListener listener = mLmkdKillListener;
+            if (listener != null) {
+                mService.mHandler.post(()-> listener.onLmkdKillOccurred(pid, uid));
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 6f6f193..24fc743 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -310,6 +310,8 @@
     long startTime;
     // This will be same as {@link #uid} usually except for some apps used during factory testing.
     int startUid;
+    // set of disabled compat changes for the process (all others are enabled)
+    long[] mDisabledCompatChanges;
 
     // Cached task info for OomAdjuster
     private static final int VALUE_INVALID = -1;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 366766e..14f9654 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -20,6 +20,7 @@
 import static android.app.AppOpsManager.MAX_PRIORITY_UID_STATE;
 import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE;
 import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_COARSE_LOCATION;
 import static android.app.AppOpsManager.OP_FLAGS_ALL;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.app.AppOpsManager.OP_PLAY_AUDIO;
@@ -1642,6 +1643,13 @@
             return;
         }
 
+        // STOPSHIP: Remove this check once we are sure no one is doing it.
+        if (code == OP_COARSE_LOCATION && mode != AppOpsManager.opToDefaultMode(code)) {
+            Slog.wtf(TAG, "Trying to setMode() instead of setUidMode(), " + "code=" + code
+                    + ", uid=" + uid + ", packageName=" + packageName + ", mode=" + mode
+                    + ", callingUid=" + Binder.getCallingUid(), new RuntimeException());
+        }
+
         synchronized (this) {
             UidState uidState = getUidStateLocked(uid, false);
             Op op = getOpLocked(code, uid, packageName, isPrivileged, true);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 661451b..9061586 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -68,9 +68,6 @@
 
     private @NonNull AudioDeviceBroker mDeviceBroker;
 
-    // cache of the address of the last dock the device was connected to
-    private String mDockAddress;
-
     // Monitoring of audio routes.  Protected by mAudioRoutes.
     final AudioRoutesInfo mCurAudioRoutes = new AudioRoutesInfo();
     final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers =
@@ -187,7 +184,7 @@
         int a2dpVolume = btInfo.getVolume();
         if (AudioService.DEBUG_DEVICES) {
             Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice + " state="
-                    + state + " is dock=" + btDevice.isBluetoothDock() + " vol=" + a2dpVolume);
+                    + state + " vol=" + a2dpVolume);
         }
         String address = btDevice.getAddress();
         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
@@ -215,42 +212,17 @@
                         mDeviceBroker.postBluetoothA2dpDeviceConfigChange(btDevice);
                     }
                 } else {
-                    if (btDevice.isBluetoothDock()) {
-                        if (state == BluetoothProfile.STATE_DISCONNECTED) {
-                            // introduction of a delay for transient disconnections of docks when
-                            // power is rapidly turned off/on, this message will be canceled if
-                            // we reconnect the dock under a preset delay
-                            makeA2dpDeviceUnavailableLater(address,
-                                    AudioDeviceBroker.BTA2DP_DOCK_TIMEOUT_MS);
-                            // the next time isConnected is evaluated, it will be false for the dock
-                        }
-                    } else {
-                        makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat);
-                    }
+                    makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat);
                 }
-            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
-                if (btDevice.isBluetoothDock()) {
-                    // this could be a reconnection after a transient disconnection
-                    mDeviceBroker.cancelA2dpDockTimeout();
-                    mDockAddress = address;
-                } else {
-                    // this could be a connection of another A2DP device before the timeout of
-                    // a dock: cancel the dock timeout, and make the dock unavailable now
-                    if (mDeviceBroker.hasScheduledA2dpDockTimeout() && mDockAddress != null) {
-                        mDeviceBroker.cancelA2dpDockTimeout();
-                        makeA2dpDeviceUnavailableNow(mDockAddress,
-                                AudioSystem.AUDIO_FORMAT_DEFAULT);
-                    }
-                }
-                if (a2dpVolume != -1) {
-                    mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
-                            // convert index to internal representation in VolumeStreamState
-                            a2dpVolume * 10,
-                            AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState");
-                }
-                makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice),
-                        "onSetA2dpSinkConnectionState", a2dpCodec);
             }
+            if (a2dpVolume != -1) {
+                mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
+                        // convert index to internal representation in VolumeStreamState
+                        a2dpVolume * 10,
+                        AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState");
+            }
+            makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice),
+                    "onSetA2dpSinkConnectionState", a2dpCodec);
         }
     }
 
@@ -723,9 +695,6 @@
                 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
         // Remove A2DP routes as well
         setCurrentAudioRouteNameIfPossible(null);
-        if (mDockAddress == address) {
-            mDockAddress = null;
-        }
     }
 
     @GuardedBy("mConnectedDevices")
diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
index 27f11ff..1b1c546 100644
--- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java
+++ b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
@@ -31,8 +31,6 @@
 import android.util.Log;
 
 import com.android.internal.app.IBatteryStats;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.TelephonyIntents;
 import com.android.server.am.BatteryStatsService;
 
 public class DataConnectionStats extends BroadcastReceiver {
@@ -44,7 +42,7 @@
     private final Handler mListenerHandler;
     private final PhoneStateListener mPhoneStateListener;
 
-    private IccCardConstants.State mSimState = IccCardConstants.State.READY;
+    private int mSimState = TelephonyManager.SIM_STATE_READY;
     private SignalStrength mSignalStrength;
     private ServiceState mServiceState;
     private int mDataState = TelephonyManager.DATA_DISCONNECTED;
@@ -66,7 +64,7 @@
               | PhoneStateListener.LISTEN_DATA_ACTIVITY);
 
         IntentFilter filter = new IntentFilter();
-        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
         mContext.registerReceiver(this, filter, null /* broadcastPermission */, mListenerHandler);
@@ -75,7 +73,7 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         final String action = intent.getAction();
-        if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+        if (action.equals(Intent.ACTION_SIM_STATE_CHANGED)) {
             updateSimState(intent);
             notePhoneDataConnectionState();
         } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
@@ -88,8 +86,8 @@
         if (mServiceState == null) {
             return;
         }
-        boolean simReadyOrUnknown = mSimState == IccCardConstants.State.READY
-                || mSimState == IccCardConstants.State.UNKNOWN;
+        boolean simReadyOrUnknown = mSimState == TelephonyManager.SIM_STATE_READY
+                || mSimState == TelephonyManager.SIM_STATE_UNKNOWN;
         boolean visible = (simReadyOrUnknown || isCdma()) // we only check the sim state for GSM
                 && hasService()
                 && mDataState == TelephonyManager.DATA_CONNECTED;
@@ -105,23 +103,23 @@
     }
 
     private final void updateSimState(Intent intent) {
-        String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
-        if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
-            mSimState = IccCardConstants.State.ABSENT;
-        } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
-            mSimState = IccCardConstants.State.READY;
-        } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+        String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
+        if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) {
+            mSimState = TelephonyManager.SIM_STATE_ABSENT;
+        } else if (Intent.SIM_STATE_READY.equals(stateExtra)) {
+            mSimState = TelephonyManager.SIM_STATE_READY;
+        } else if (Intent.SIM_STATE_LOCKED.equals(stateExtra)) {
             final String lockedReason =
-                    intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
-            if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
-                mSimState = IccCardConstants.State.PIN_REQUIRED;
-            } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
-                mSimState = IccCardConstants.State.PUK_REQUIRED;
+                    intent.getStringExtra(Intent.EXTRA_SIM_LOCKED_REASON);
+            if (Intent.SIM_LOCKED_ON_PIN.equals(lockedReason)) {
+                mSimState = TelephonyManager.SIM_STATE_PIN_REQUIRED;
+            } else if (Intent.SIM_LOCKED_ON_PUK.equals(lockedReason)) {
+                mSimState = TelephonyManager.SIM_STATE_PUK_REQUIRED;
             } else {
-                mSimState = IccCardConstants.State.NETWORK_LOCKED;
+                mSimState = TelephonyManager.SIM_STATE_NETWORK_LOCKED;
             }
         } else {
-            mSimState = IccCardConstants.State.UNKNOWN;
+            mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index bb7f862..5e085ca 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -291,13 +291,18 @@
      *
      * <p>If {@link NetworkMonitor#notifyNetworkCapabilitiesChanged(NetworkCapabilities)} fails,
      * the exception is logged but not reported to callers.
+     *
+     * @return the old capabilities of this network.
      */
-    public void setNetworkCapabilities(NetworkCapabilities nc) {
+    public synchronized NetworkCapabilities getAndSetNetworkCapabilities(
+            @NonNull final NetworkCapabilities nc) {
+        final NetworkCapabilities oldNc = networkCapabilities;
         networkCapabilities = nc;
         final NetworkMonitorManager nm = mNetworkMonitor;
         if (nm != null) {
             nm.notifyNetworkCapabilitiesChanged(nc);
         }
+        return oldNc;
     }
 
     public ConnectivityService connService() {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 31632dc..177e2d8 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -41,6 +41,7 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
 
@@ -215,7 +216,9 @@
     private IActivityTaskManager mActivityTaskManager;
     private PackageManager mPackageManager;
 
-    public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
+    private final Injector mInjector;
+
+    AutomaticBrightnessController(Callbacks callbacks, Looper looper,
             SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
             int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
             int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
@@ -223,6 +226,24 @@
             HysteresisLevels ambientBrightnessThresholds,
             HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout,
             PackageManager packageManager) {
+        this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
+                lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
+                lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
+                darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
+                ambientBrightnessThresholds, screenBrightnessThresholds, shortTermModelTimeout,
+                packageManager);
+    }
+
+    @VisibleForTesting
+    AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper,
+            SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
+            int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
+            int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
+            long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
+            HysteresisLevels ambientBrightnessThresholds,
+            HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout,
+            PackageManager packageManager) {
+        mInjector = injector;
         mCallbacks = callbacks;
         mSensorManager = sensorManager;
         mBrightnessMapper = mapper;
@@ -725,8 +746,8 @@
         float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
                 mForegroundAppCategory);
 
-        int newScreenAutoBrightness =
-                clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
+        int newScreenAutoBrightness = Math.round(clampScreenBrightness(
+                value * PowerManager.BRIGHTNESS_ON));
 
         // If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold,
         // in which case we ignore the new screen brightness if it doesn't differ enough from the
@@ -750,10 +771,10 @@
             }
 
             mScreenAutoBrightness = newScreenAutoBrightness;
-            mScreenBrighteningThreshold =
-                    mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness);
-            mScreenDarkeningThreshold =
-                    mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness);
+            mScreenBrighteningThreshold = clampScreenBrightness(
+                    mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));
+            mScreenDarkeningThreshold = clampScreenBrightness(
+                    mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
 
             if (sendUpdate) {
                 mCallbacks.updateBrightness();
@@ -761,7 +782,7 @@
         }
     }
 
-    private int clampScreenBrightness(int value) {
+    private float clampScreenBrightness(float value) {
         return MathUtils.constrain(value,
                 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
     }
@@ -839,7 +860,7 @@
         }
         // 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() {
+        mInjector.getBackgroundThreadHandler().post(new Runnable() {
             public void run() {
                 try {
                     // The foreground app is the top activity of the focused tasks stack.
@@ -965,6 +986,9 @@
         private int mCount;
 
         public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
+            if (lightSensorRate <= 0) {
+                throw new IllegalArgumentException("lightSensorRate must be above 0");
+            }
             mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
             mRingLux = new float[mCapacity];
             mRingTime = new long[mCapacity];
@@ -1076,4 +1100,10 @@
             return index;
         }
     }
+
+    public static class Injector {
+        public Handler getBackgroundThreadHandler() {
+            return BackgroundThread.getHandler();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java
index 2db1d03..f0a505d 100644
--- a/services/core/java/com/android/server/display/HysteresisLevels.java
+++ b/services/core/java/com/android/server/display/HysteresisLevels.java
@@ -18,13 +18,16 @@
 
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.PrintWriter;
 import java.util.Arrays;
 
 /**
  * A helper class for handling access to illuminance hysteresis level values.
  */
-final class HysteresisLevels {
+@VisibleForTesting
+public class HysteresisLevels {
     private static final String TAG = "HysteresisLevels";
 
     // Default hysteresis constraints for brightening or darkening.
@@ -60,7 +63,7 @@
     /**
      * Return the brightening hysteresis threshold for the given value level.
      */
-    float getBrighteningThreshold(float value) {
+    public float getBrighteningThreshold(float value) {
         float brightConstant = getReferenceLevel(value, mBrighteningThresholds);
         float brightThreshold = value * (1.0f + brightConstant);
         if (DEBUG) {
@@ -73,7 +76,7 @@
     /**
      * Return the darkening hysteresis threshold for the given value level.
      */
-    float getDarkeningThreshold(float value) {
+    public float getDarkeningThreshold(float value) {
         float darkConstant = getReferenceLevel(value, mDarkeningThresholds);
         float darkThreshold = value * (1.0f - darkConstant);
         if (DEBUG) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 1d7c942..308c755 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -410,6 +410,9 @@
                 final DisplayAddress.Physical physicalAddress =
                         DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
                 mInfo.address = physicalAddress;
+                mInfo.densityDpi = (int) (phys.density * 160 + 0.5f);
+                mInfo.xDpi = phys.xDpi;
+                mInfo.yDpi = phys.yDpi;
 
                 // Assume that all built-in displays that have secure output (eg. HDCP) also
                 // support compositing from gralloc protected buffers.
@@ -436,9 +439,6 @@
                     mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
                             mInfo.width, mInfo.height);
                     mInfo.type = Display.TYPE_BUILT_IN;
-                    mInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
-                    mInfo.xDpi = phys.xDpi;
-                    mInfo.yDpi = phys.yDpi;
                     mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
                 } else {
                     mInfo.displayCutout = null;
@@ -447,7 +447,6 @@
                     mInfo.name = getContext().getResources().getString(
                             com.android.internal.R.string.display_manager_hdmi_display_name);
                     mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
-                    mInfo.setAssumedDensityForExternalDisplay(phys.width, phys.height);
 
                     // For demonstration purposes, allow rotation of the external display.
                     // In the future we might allow the user to configure this directly.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index 52cede2..23b5c14 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -250,7 +250,11 @@
             new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_LEFT_UP),
             // No Android keycode defined for CEC_KEYCODE_LEFT_DOWN
             new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_LEFT_DOWN),
+            // Both KEYCODE_HOME and KEYCODE_MENU keys are sent as CEC_KEYCODE_ROOT_MENU
+            // NOTE that the HOME key is not usually forwarded.
+            // When CEC_KEYCODE_ROOT_MENU is received, it is translated to KEYCODE_HOME
             new KeycodeEntry(KeyEvent.KEYCODE_HOME, CEC_KEYCODE_ROOT_MENU),
+            new KeycodeEntry(KeyEvent.KEYCODE_MENU, CEC_KEYCODE_ROOT_MENU),
             new KeycodeEntry(KeyEvent.KEYCODE_SETTINGS, CEC_KEYCODE_SETUP_MENU),
             new KeycodeEntry(KeyEvent.KEYCODE_TV_CONTENTS_MENU, CEC_KEYCODE_CONTENTS_MENU, false),
             // No Android keycode defined for CEC_KEYCODE_FAVORITE_MENU
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java
new file mode 100644
index 0000000..005fb69
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java
@@ -0,0 +1,43 @@
+/*
+ * 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.integrity;
+
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+/**
+ * Service that manages app integrity rules and verifications.
+ *
+ * @hide
+ */
+public class AppIntegrityManagerService extends SystemService {
+
+    private Context mContext;
+    private AppIntegrityManagerServiceImpl mService;
+
+    public AppIntegrityManagerService(Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    @Override
+    public void onStart() {
+        mService = new AppIntegrityManagerServiceImpl(mContext);
+        // TODO: define and publish a binder service.
+    }
+}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
new file mode 100644
index 0000000..39c1b85
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -0,0 +1,80 @@
+/*
+ * 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.integrity;
+
+import static android.content.Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION;
+import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+
+/** Implementation of {@link AppIntegrityManagerService}. */
+class AppIntegrityManagerServiceImpl {
+    private static final String TAG = "AppIntegrityManagerServiceImpl";
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final PackageManagerInternal mPackageManagerInternal;
+
+    AppIntegrityManagerServiceImpl(Context context) {
+        mContext = context;
+
+        HandlerThread handlerThread = new HandlerThread("AppIntegrityManagerServiceHandler");
+        handlerThread.start();
+        mHandler = handlerThread.getThreadHandler();
+
+        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+
+        IntentFilter integrityVerificationFilter = new IntentFilter();
+        integrityVerificationFilter.addAction(ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
+
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        if (!ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION.equals(
+                                intent.getAction())) {
+                            return;
+                        }
+                        mHandler.post(() -> handleIntegrityVerification(intent));
+                    }
+                },
+                integrityVerificationFilter,
+                /* broadcastPermission= */ null,
+                mHandler);
+    }
+
+    // protected broadcasts cannot be sent in the test.
+    @VisibleForTesting
+    void handleIntegrityVerification(Intent intent) {
+        int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
+        // TODO: implement this method.
+        Slog.i(TAG, "Received integrity verification intent " + intent.toString());
+        mPackageManagerInternal.setIntegrityVerificationResult(
+                verificationId, PackageManager.VERIFICATION_ALLOW);
+    }
+}
diff --git a/services/core/java/com/android/server/location/LocationSettingsStore.java b/services/core/java/com/android/server/location/LocationSettingsStore.java
index eb2a37b..3d18d4a 100644
--- a/services/core/java/com/android/server/location/LocationSettingsStore.java
+++ b/services/core/java/com/android/server/location/LocationSettingsStore.java
@@ -23,7 +23,6 @@
 import static android.provider.Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS;
 import static android.provider.Settings.Secure.LOCATION_MODE;
 import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
-import static android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
 
 import android.app.ActivityManager;
 import android.content.Context;
@@ -89,7 +88,6 @@
     private final Context mContext;
 
     private final IntegerSecureSetting mLocationMode;
-    private final StringListCachedSecureSetting mLocationProvidersAllowed;
     private final LongGlobalSetting mBackgroundThrottleIntervalMs;
     private final StringListCachedSecureSetting mLocationPackageBlacklist;
     private final StringListCachedSecureSetting mLocationPackageWhitelist;
@@ -101,8 +99,6 @@
         mContext = context;
 
         mLocationMode = new IntegerSecureSetting(context, LOCATION_MODE, handler);
-        mLocationProvidersAllowed = new StringListCachedSecureSetting(context,
-                LOCATION_PROVIDERS_ALLOWED, handler);
         mBackgroundThrottleIntervalMs = new LongGlobalSetting(context,
                 LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, handler);
         mLocationPackageBlacklist = new StringListCachedSecureSetting(context,
@@ -139,28 +135,6 @@
     }
 
     /**
-     * Retrieve the currently allowed location providers.
-     */
-    public List<String> getLocationProvidersAllowed(int userId) {
-        return mLocationProvidersAllowed.getValueForUser(userId);
-    }
-
-    /**
-     * Add a listener for changes to the currently allowed location providers.
-     */
-    public void addOnLocationProvidersAllowedChangedListener(UserSettingChangedListener listener) {
-        mLocationProvidersAllowed.addListener(listener);
-    }
-
-    /**
-     * Remove a listener for changes to the currently allowed location providers.
-     */
-    public void removeOnLocationProvidersAllowedChangedListener(
-            UserSettingChangedListener listener) {
-        mLocationProvidersAllowed.removeListener(listener);
-    }
-
-    /**
      * Retrieve the background throttle interval.
      */
     public long getBackgroundThrottleIntervalMs() {
@@ -280,9 +254,6 @@
         ipw.print("Location Enabled: ");
         ipw.println(isLocationEnabled(userId));
 
-        ipw.print("Location Providers Allowed: ");
-        ipw.println(getLocationProvidersAllowed(userId));
-
         List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
         if (!locationPackageBlacklist.isEmpty()) {
             ipw.println("Location Blacklisted Packages:");
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index bc05154..7098435 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.provider.Settings;
 import android.telecom.TelecomManager;
 
 import com.android.internal.util.NotificationMessagingUtil;
@@ -55,14 +54,9 @@
         final boolean isLeftHighImportance = leftImportance >= IMPORTANCE_DEFAULT;
         final boolean isRightHighImportance = rightImportance >= IMPORTANCE_DEFAULT;
 
-        // With new interruption model, prefer importance bucket above all other criteria
-        // (to ensure buckets are contiguous)
-        if (Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
-            if (isLeftHighImportance != isRightHighImportance) {
-                // by importance bucket, high importance higher than low importance
-                return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance);
-            }
+        if (isLeftHighImportance != isRightHighImportance) {
+            // by importance bucket, high importance higher than low importance
+            return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance);
         }
 
         // first all colorized notifications
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index ac3bf9a..e055116 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -26,7 +26,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.text.TextUtils;
-import android.util.Pair;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
@@ -45,38 +44,6 @@
 
     private final VerifyCallback mVerifyCallback;
 
-    /**
-     * @return nullable actor result with {@link ActorState} failure status
-     */
-    static Pair<String, ActorState> getPackageNameForActor(String actorUriString,
-            Map<String, Map<String, String>> namedActors) {
-        if (namedActors.isEmpty()) {
-            return Pair.create(null, ActorState.NO_NAMED_ACTORS);
-        }
-
-        Uri actorUri = Uri.parse(actorUriString);
-
-        String actorScheme = actorUri.getScheme();
-        List<String> actorPathSegments = actorUri.getPathSegments();
-        if (!"overlay".equals(actorScheme) || CollectionUtils.size(actorPathSegments) != 1) {
-            return Pair.create(null, ActorState.INVALID_OVERLAYABLE_ACTOR_NAME);
-        }
-
-        String actorNamespace = actorUri.getAuthority();
-        Map<String, String> namespace = namedActors.get(actorNamespace);
-        if (namespace == null) {
-            return Pair.create(null, ActorState.MISSING_NAMESPACE);
-        }
-
-        String actorName = actorPathSegments.get(0);
-        String packageName = namespace.get(actorName);
-        if (TextUtils.isEmpty(packageName)) {
-            return Pair.create(null, ActorState.MISSING_ACTOR_NAME);
-        }
-
-        return Pair.create(packageName, ActorState.ALLOWED);
-    }
-
     public OverlayActorEnforcer(@NonNull VerifyCallback verifyCallback) {
         mVerifyCallback = verifyCallback;
     }
@@ -174,14 +141,31 @@
             }
         }
 
-        Map<String, Map<String, String>> namedActors = mVerifyCallback.getNamedActors();
-        Pair<String, ActorState> actorUriPair = getPackageNameForActor(actor, namedActors);
-        ActorState actorUriState = actorUriPair.second;
-        if (actorUriState != ActorState.ALLOWED) {
-            return actorUriState;
+        Map<String, ? extends Map<String, String>> namedActors = mVerifyCallback.getNamedActors();
+        if (namedActors.isEmpty()) {
+            return ActorState.NO_NAMED_ACTORS;
         }
 
-        String packageName = actorUriPair.first;
+        Uri actorUri = Uri.parse(actor);
+
+        String actorScheme = actorUri.getScheme();
+        List<String> actorPathSegments = actorUri.getPathSegments();
+        if (!"overlay".equals(actorScheme) || CollectionUtils.size(actorPathSegments) != 1) {
+            return ActorState.INVALID_OVERLAYABLE_ACTOR_NAME;
+        }
+
+        String actorNamespace = actorUri.getAuthority();
+        Map<String, String> namespace = namedActors.get(actorNamespace);
+        if (namespace == null) {
+            return ActorState.MISSING_NAMESPACE;
+        }
+
+        String actorName = actorPathSegments.get(0);
+        String packageName = namespace.get(actorName);
+        if (TextUtils.isEmpty(packageName)) {
+            return ActorState.MISSING_ACTOR_NAME;
+        }
+
         PackageInfo packageInfo = mVerifyCallback.getPackageInfo(packageName, userId);
         if (packageInfo == null) {
             return ActorState.MISSING_APP_INFO;
@@ -208,7 +192,7 @@
      * For easier logging/debugging, a set of all possible failure/success states when running
      * enforcement.
      */
-    enum ActorState {
+    private enum ActorState {
         ALLOWED,
         INVALID_ACTOR,
         MISSING_NAMESPACE,
@@ -260,7 +244,7 @@
          * value maps actor name to package name
          */
         @NonNull
-        Map<String, Map<String, String>> getNamedActors();
+        Map<String, ? extends Map<String, String>> getNamedActors();
 
         /**
          * @return true if the target package has declared an overlayable
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f1947ac..8b69946 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -1073,6 +1073,8 @@
             mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         }
 
+        // TODO(b/143096091): Remove PackageInfo cache so that PackageManager is always queried
+        //  to enforce visibility/other permission checks
         public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId,
                 final boolean useCache) {
             if (useCache) {
@@ -1095,12 +1097,18 @@
 
         @Override
         public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) {
-            return getPackageInfo(packageName, userId, true);
+            // TODO(b/143096091): Remove clearing calling ID
+            long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                return getPackageInfo(packageName, userId, true);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
         }
 
         @NonNull
         @Override
-        public Map<String, Map<String, String>> getNamedActors() {
+        public Map<String, ? extends Map<String, String>> getNamedActors() {
             return SystemConfig.getInstance().getNamedActors();
         }
 
@@ -1128,45 +1136,57 @@
         public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
                 @Nullable String targetOverlayableName, int userId)
                 throws IOException {
-            PackageInfo packageInfo = getPackageInfo(packageName, userId);
-            if (packageInfo == null) {
-                throw new IOException("Unable to get target package");
-            }
-
-            String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
-
-            ApkAssets apkAssets = null;
+            // TODO(b/143096091): Remove clearing calling ID
+            long callingIdentity = Binder.clearCallingIdentity();
             try {
-                apkAssets = ApkAssets.loadFromPath(baseCodePath);
-                return apkAssets.getOverlayableInfo(targetOverlayableName);
-            } finally {
-                if (apkAssets != null) {
-                    try {
-                        apkAssets.close();
-                    } catch (Throwable ignored) {
+                PackageInfo packageInfo = getPackageInfo(packageName, userId);
+                if (packageInfo == null) {
+                    throw new IOException("Unable to get target package");
+                }
+
+                String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
+
+                ApkAssets apkAssets = null;
+                try {
+                    apkAssets = ApkAssets.loadFromPath(baseCodePath);
+                    return apkAssets.getOverlayableInfo(targetOverlayableName);
+                } finally {
+                    if (apkAssets != null) {
+                        try {
+                            apkAssets.close();
+                        } catch (Throwable ignored) {
+                        }
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
             }
         }
 
         @Override
         public boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
                 throws RemoteException, IOException {
-            PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
-                    userId);
-            String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
-
-            ApkAssets apkAssets = null;
+            // TODO(b/143096091): Remove clearing calling ID
+            long callingIdentity = Binder.clearCallingIdentity();
             try {
-                apkAssets = ApkAssets.loadFromPath(baseCodePath);
-                return apkAssets.definesOverlayable();
-            } finally {
-                if (apkAssets != null) {
-                    try {
-                        apkAssets.close();
-                    } catch (Throwable ignored) {
+                PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
+                        userId);
+                String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
+
+                ApkAssets apkAssets = null;
+                try {
+                    apkAssets = ApkAssets.loadFromPath(baseCodePath);
+                    return apkAssets.definesOverlayable();
+                } finally {
+                    if (apkAssets != null) {
+                        try {
+                            apkAssets.close();
+                        } catch (Throwable ignored) {
+                        }
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
             }
         }
 
@@ -1209,10 +1229,16 @@
         @Nullable
         @Override
         public String[] getPackagesForUid(int uid) {
+            // TODO(b/143096091): Remove clearing calling ID
+            long callingIdentity = Binder.clearCallingIdentity();
             try {
-                return mPackageManager.getPackagesForUid(uid);
-            } catch (RemoteException ignored) {
-                return null;
+                try {
+                    return mPackageManager.getPackagesForUid(uid);
+                } catch (RemoteException ignored) {
+                    return null;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
             }
         }
 
diff --git a/services/core/java/com/android/server/om/OverlayReferenceMapper.java b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
deleted file mode 100644
index 8bea119..0000000
--- a/services/core/java/com/android/server/om/OverlayReferenceMapper.java
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * 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.om;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.parsing.AndroidPackage;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.Pair;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.CollectionUtils;
-import com.android.server.SystemConfig;
-import com.android.server.pm.PackageSetting;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Track visibility of a targets and overlays to actors.
- *
- * 4 cases to handle:
- * <ol>
- *     <li>Target adds/changes an overlayable to add a reference to an actor
- *         <ul>
- *             <li>Must expose target to actor</li>
- *             <li>Must expose any overlays that pointed to that overlayable name to the actor</li>
- *         </ul>
- *     </li>
- *     <li>Target removes/changes an overlayable to remove a reference to an actor
- *         <ul>
- *             <li>If this target has no other overlayables referencing the actor, hide the
- *             target</li>
- *             <li>For all overlays targeting this overlayable, if the overlay is only visible to
- *             the actor through this overlayable, hide the overlay</li>
- *         </ul>
- *     </li>
- *     <li>Overlay adds/changes an overlay tag to add a reference to an overlayable name
- *         <ul>
- *             <li>Expose this overlay to the actor defined by the target overlayable</li>
- *         </ul>
- *     </li>
- *     <li>Overlay removes/changes an overlay tag to remove a reference to an overlayable name
- *         <ul>
- *             <li>If this overlay is only visible to an actor through this overlayable name's
- *             target's actor</li>
- *         </ul>
- *     </li>
- * </ol>
- *
- * In this class, the names "actor", "target", and "overlay" all refer to the ID representations.
- * All other use cases are named appropriate. "actor" is actor name, "target" is target package
- * name, and "overlay" is overlay package name.
- */
-public class OverlayReferenceMapper {
-
-    private final Object mLock = new Object();
-
-    /**
-     * Keys are actors, values are maps which map target to a set of overlays targeting it.
-     * The presence of a target in the value map means the actor and targets are connected, even
-     * if the corresponding target's set is empty.
-     * See class comment for specific types.
-     */
-    @GuardedBy("mLock")
-    private final Map<String, Map<String, Set<String>>> mActorToTargetToOverlays = new HashMap<>();
-
-    /**
-     * Keys are actor package names, values are generic package names the actor should be able
-     * to see.
-     */
-    @GuardedBy("mLock")
-    private final Map<String, Set<String>> mActorPkgToPkgs = new HashMap<>();
-
-    @GuardedBy("mLock")
-    private boolean mDeferRebuild;
-
-    @NonNull
-    private final Provider mProvider;
-
-    /**
-     * @param deferRebuild whether or not to defer rebuild calls on add/remove until first get call;
-     *                     useful during boot when multiple packages are added in rapid succession
-     *                     and queries in-between are not expected
-     */
-    public OverlayReferenceMapper(boolean deferRebuild, @Nullable Provider provider) {
-        this.mDeferRebuild = deferRebuild;
-        this.mProvider = provider != null ? provider : new Provider() {
-            @Nullable
-            @Override
-            public String getActorPkg(String actor) {
-                Map<String, Map<String, String>> namedActors = SystemConfig.getInstance()
-                        .getNamedActors();
-
-                Pair<String, OverlayActorEnforcer.ActorState> actorPair =
-                        OverlayActorEnforcer.getPackageNameForActor(actor, namedActors);
-                return actorPair.first;
-            }
-
-            @NonNull
-            @Override
-            public Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg) {
-                String target = pkg.getOverlayTarget();
-                if (TextUtils.isEmpty(target)) {
-                    return Collections.emptyMap();
-                }
-
-                String overlayable = pkg.getOverlayTargetName();
-                Map<String, Set<String>> targetToOverlayables = new HashMap<>();
-                Set<String> overlayables = new HashSet<>();
-                overlayables.add(overlayable);
-                targetToOverlayables.put(target, overlayables);
-                return targetToOverlayables;
-            }
-        };
-    }
-
-    /**
-     * @return mapping of actor package to a set of packages it can view
-     */
-    @NonNull
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    public Map<String, Set<String>> getActorPkgToPkgs() {
-        return mActorPkgToPkgs;
-    }
-
-    public boolean isValidActor(@NonNull String targetName, @NonNull String actorPackageName) {
-        synchronized (mLock) {
-            assertMapBuilt();
-            Set<String> validSet = mActorPkgToPkgs.get(actorPackageName);
-            return validSet != null && validSet.contains(targetName);
-        }
-    }
-
-    /**
-     * Add a package to be considered for visibility. Currently supports adding as a target and/or
-     * an overlay. Adding an actor is not supported. Those are configured as part of
-     * {@link SystemConfig#getNamedActors()}.
-     *
-     * @param pkg the package to add
-     * @param otherPkgs map of other packages to consider, excluding {@param pkg}
-     */
-    public void addPkg(AndroidPackage pkg, Map<String, AndroidPackage> otherPkgs) {
-        synchronized (mLock) {
-            if (!pkg.getOverlayables().isEmpty()) {
-                addTarget(pkg, otherPkgs);
-            }
-
-            // TODO(b/135203078): Replace with isOverlay boolean flag check; fix test mocks
-            if (!mProvider.getTargetToOverlayables(pkg).isEmpty()) {
-                addOverlay(pkg, otherPkgs);
-            }
-
-            if (!mDeferRebuild) {
-                rebuild();
-            }
-        }
-    }
-
-    /**
-     * Removes a package to be considered for visibility. Currently supports removing as a target
-     * and/or an overlay. Removing an actor is not supported. Those are staticly configured as part
-     * of {@link SystemConfig#getNamedActors()}.
-     *
-     * @param pkgName name to remove, as was added through {@link #addPkg(AndroidPackage, Map)}
-     */
-    public void removePkg(String pkgName) {
-        synchronized (mLock) {
-            removeTarget(pkgName);
-            removeOverlay(pkgName);
-
-            if (!mDeferRebuild) {
-                rebuild();
-            }
-        }
-    }
-
-    private void removeTarget(String target) {
-        synchronized (mLock) {
-            Iterator<Map<String, Set<String>>> iterator =
-                    mActorToTargetToOverlays.values().iterator();
-            while (iterator.hasNext()) {
-                Map<String, Set<String>> next = iterator.next();
-                next.remove(target);
-                if (next.isEmpty()) {
-                    iterator.remove();
-                }
-            }
-        }
-    }
-
-    /**
-     * Associate an actor with an association of a new target to overlays for that target.
-     *
-     * If a target overlays itself, it will not be associated with itself, as only one half of the
-     * relationship needs to exist for visibility purposes.
-     */
-    private void addTarget(AndroidPackage targetPkg, Map<String, AndroidPackage> otherPkgs) {
-        synchronized (mLock) {
-            String target = targetPkg.getPackageName();
-            removeTarget(target);
-
-            Map<String, String> overlayablesToActors = targetPkg.getOverlayables();
-            for (String overlayable : overlayablesToActors.keySet()) {
-                String actor = overlayablesToActors.get(overlayable);
-                addTargetToMap(actor, target);
-
-                for (AndroidPackage overlayPkg : otherPkgs.values()) {
-                    Map<String, Set<String>> targetToOverlayables =
-                            mProvider.getTargetToOverlayables(overlayPkg);
-                    Set<String> overlayables = targetToOverlayables.get(target);
-                    if (CollectionUtils.isEmpty(overlayables)) {
-                        continue;
-                    }
-
-                    if (overlayables.contains(overlayable)) {
-                        addOverlayToMap(actor, target, overlayPkg.getPackageName());
-                    }
-                }
-            }
-        }
-    }
-
-    private void removeOverlay(String overlay) {
-        synchronized (mLock) {
-            for (Map<String, Set<String>> targetToOverlays : mActorToTargetToOverlays.values()) {
-                for (Set<String> overlays : targetToOverlays.values()) {
-                    overlays.remove(overlay);
-                }
-            }
-        }
-    }
-
-    /**
-     * Associate an actor with an association of targets to overlays for a new overlay.
-     *
-     * If an overlay targets itself, it will not be associated with itself, as only one half of the
-     * relationship needs to exist for visibility purposes.
-     */
-    private void addOverlay(AndroidPackage overlayPkg, Map<String, AndroidPackage> otherPkgs) {
-        synchronized (mLock) {
-            String overlay = overlayPkg.getPackageName();
-            removeOverlay(overlay);
-
-            Map<String, Set<String>> targetToOverlayables =
-                    mProvider.getTargetToOverlayables(overlayPkg);
-            for (Map.Entry<String, Set<String>> entry : targetToOverlayables.entrySet()) {
-                String target = entry.getKey();
-                Set<String> overlayables = entry.getValue();
-                AndroidPackage targetPkg = otherPkgs.get(target);
-                if (targetPkg == null) {
-                    continue;
-                }
-
-                String targetPkgName = targetPkg.getPackageName();
-                Map<String, String> overlayableToActor = targetPkg.getOverlayables();
-                for (String overlayable : overlayables) {
-                    String actor = overlayableToActor.get(overlayable);
-                    if (TextUtils.isEmpty(actor)) {
-                        continue;
-                    }
-                    addOverlayToMap(actor, targetPkgName, overlay);
-                }
-            }
-        }
-    }
-
-    public void rebuildIfDeferred() {
-        synchronized (mLock) {
-            if (mDeferRebuild) {
-                rebuild();
-                mDeferRebuild = false;
-            }
-        }
-    }
-
-    private void assertMapBuilt() {
-        if (mDeferRebuild) {
-            throw new IllegalStateException("The actor map must be built by calling "
-                    + "rebuildIfDeferred before it is queried");
-        }
-    }
-
-    private void rebuild() {
-        synchronized (mLock) {
-            mActorPkgToPkgs.clear();
-            for (String actor : mActorToTargetToOverlays.keySet()) {
-                String actorPkg = mProvider.getActorPkg(actor);
-                if (TextUtils.isEmpty(actorPkg)) {
-                    continue;
-                }
-
-                Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
-                Set<String> pkgs = new HashSet<>();
-
-                for (String target : targetToOverlays.keySet()) {
-                    Set<String> overlays = targetToOverlays.get(target);
-                    pkgs.add(target);
-                    pkgs.addAll(overlays);
-                }
-
-                mActorPkgToPkgs.put(actorPkg, pkgs);
-            }
-        }
-    }
-
-    private void addTargetToMap(String actor, String target) {
-        Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
-        if (targetToOverlays == null) {
-            targetToOverlays = new HashMap<>();
-            mActorToTargetToOverlays.put(actor, targetToOverlays);
-        }
-
-        Set<String> overlays = targetToOverlays.get(target);
-        if (overlays == null) {
-            overlays = new HashSet<>();
-            targetToOverlays.put(target, overlays);
-        }
-    }
-
-    private void addOverlayToMap(String actor, String target, String overlay) {
-        synchronized (mLock) {
-            Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
-            if (targetToOverlays == null) {
-                targetToOverlays = new HashMap<>();
-                mActorToTargetToOverlays.put(actor, targetToOverlays);
-            }
-
-            Set<String> overlays = targetToOverlays.get(target);
-            if (overlays == null) {
-                overlays = new HashSet<>();
-                targetToOverlays.put(target, overlays);
-            }
-
-            overlays.add(overlay);
-        }
-    }
-
-    public interface Provider {
-
-        /**
-         * Given the actor string from an overlayable definition, return the actor's package name.
-         */
-        @Nullable
-        String getActorPkg(String actor);
-
-        /**
-         * Mock response of multiple overlay tags.
-         *
-         * TODO(b/119899133): Replace with actual implementation; fix OverlayReferenceMapperTests
-         */
-        @NonNull
-        Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg);
-    }
-}
diff --git a/tests/utils/testutils/java/test/package-info.java b/services/core/java/com/android/server/package-info.java
similarity index 76%
rename from tests/utils/testutils/java/test/package-info.java
rename to services/core/java/com/android/server/package-info.java
index c34d7b2..a783e8d 100644
--- a/tests/utils/testutils/java/test/package-info.java
+++ b/services/core/java/com/android/server/package-info.java
@@ -13,9 +13,5 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-/**
- * This package separated from android. because placing classes under android.'s .test/.util
- * may be confused with tests for that actual android subpackage.
- **/
-package test;
+@android.annotation.Hide
+package com.android.server;
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index c4bcf80..8374ee6 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -16,6 +16,8 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.PackageParser.Component;
+import static android.content.pm.PackageParser.IntentInfo;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
 
@@ -24,6 +26,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
 import android.content.pm.parsing.AndroidPackage;
 import android.content.pm.parsing.ComponentParseUtils;
 import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
@@ -47,9 +50,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.FgThread;
-import com.android.server.om.OverlayReferenceMapper;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -106,16 +109,11 @@
 
     private final FeatureConfig mFeatureConfig;
 
-    private final OverlayReferenceMapper mOverlayReferenceMapper;
-
     AppsFilter(FeatureConfig featureConfig, String[] forceQueryableWhitelist,
-            boolean systemAppsQueryable,
-            @Nullable OverlayReferenceMapper.Provider overlayProvider) {
+            boolean systemAppsQueryable) {
         mFeatureConfig = featureConfig;
         mForceQueryableByDevicePackageNames = forceQueryableWhitelist;
         mSystemAppsQueryable = systemAppsQueryable;
-        mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
-                overlayProvider);
     }
 
     public interface FeatureConfig {
@@ -195,7 +193,7 @@
             }
         }
         return new AppsFilter(featureConfig, forcedQueryablePackageNames,
-                forceSystemAppsQueryable, null);
+                forceSystemAppsQueryable);
     }
 
     /** Returns true if the querying package may query for the potential target package */
@@ -284,7 +282,6 @@
 
     public void onSystemReady() {
         mFeatureConfig.onSystemReady();
-        mOverlayReferenceMapper.rebuildIfDeferred();
     }
 
     /**
@@ -341,16 +338,6 @@
                     }
                 }
             }
-
-            int existingSize = existingSettings.size();
-            ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
-            for (int index = 0; index < existingSize; index++) {
-                PackageSetting pkgSetting = existingSettings.valueAt(index);
-                if (pkgSetting.pkg != null) {
-                    existingPkgs.put(pkgSetting.name, pkgSetting.pkg);
-                }
-            }
-            mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -394,8 +381,6 @@
                 addPackage(setting.sharedUser.packages.valueAt(i), existingSettings);
             }
         }
-
-        mOverlayReferenceMapper.removePkg(setting.name);
     }
 
     /**
@@ -412,7 +397,8 @@
             PackageSetting targetPkgSetting, int userId) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication");
         try {
-            if (!shouldFilterApplicationInternal(callingUid, callingSetting, targetPkgSetting,
+            if (!shouldFilterApplicationInternal(callingUid, callingSetting,
+                    targetPkgSetting,
                     userId)) {
                 return false;
             }
@@ -426,8 +412,8 @@
         }
     }
 
-    private boolean shouldFilterApplicationInternal(int callingUid, SettingBase callingSetting,
-            PackageSetting targetPkgSetting, int userId) {
+    private boolean shouldFilterApplicationInternal(int callingUid,
+            SettingBase callingSetting, PackageSetting targetPkgSetting, int userId) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal");
         try {
             final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
@@ -544,29 +530,6 @@
                     }
                 }
             }
-
-            if (callingSharedPkgSettings != null) {
-                int size = callingSharedPkgSettings.size();
-                for (int index = 0; index < size; index++) {
-                    PackageSetting pkgSetting = callingSharedPkgSettings.valueAt(index);
-                    if (mOverlayReferenceMapper.isValidActor(targetName, pkgSetting.name)) {
-                        if (DEBUG_LOGGING) {
-                            log(callingPkgSetting, targetPkgSetting,
-                                    "matches shared user of package that acts on target of "
-                                            + "overlay");
-                        }
-                        return false;
-                    }
-                }
-            } else {
-                if (mOverlayReferenceMapper.isValidActor(targetName, callingPkgSetting.name)) {
-                    if (DEBUG_LOGGING) {
-                        log(callingPkgSetting, targetPkgSetting, "acts on target of overlay");
-                    }
-                    return false;
-                }
-            }
-
             return true;
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 73ab82d..642b500 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1439,6 +1439,7 @@
     static final int ENABLE_ROLLBACK_TIMEOUT = 22;
     static final int DEFERRED_NO_KILL_POST_DELETE = 23;
     static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
+    static final int INTEGRITY_VERIFICATION_COMPLETE = 25;
 
     static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
     static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
@@ -1603,6 +1604,10 @@
                     final boolean didRestore = (msg.arg2 != 0);
                     mRunningInstalls.delete(msg.arg1);
 
+                    if (data != null && data.res.freezer != null) {
+                        data.res.freezer.close();
+                    }
+
                     if (data != null && data.mPostInstallRunnable != null) {
                         data.mPostInstallRunnable.run();
                     } else if (data != null) {
@@ -1763,6 +1768,10 @@
 
                     break;
                 }
+                case INTEGRITY_VERIFICATION_COMPLETE: {
+                    // TODO: implement this case.
+                    break;
+                }
                 case START_INTENT_FILTER_VERIFICATIONS: {
                     IFVerificationParams params = (IFVerificationParams) msg.obj;
                     verifyIntentFiltersIfNeeded(params.userId, params.verifierUid, params.replacing,
@@ -3019,8 +3028,7 @@
 
             mWellbeingPackage = getWellbeingPackageName();
             mDocumenterPackage = getDocumenterPackageName();
-            mConfiguratorPackage =
-                    mContext.getString(R.string.config_deviceConfiguratorPackageName);
+            mConfiguratorPackage = getDeviceConfiguratorPackageName();
             mAppPredictionServicePackage = getAppPredictionServicePackageName();
             mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
             mTelephonyPackages = getTelephonyPackageNames();
@@ -13434,35 +13442,10 @@
             // restore if appropriate, then pass responsibility back to the
             // Package Manager to run the post-install observer callbacks
             // and broadcasts.
-            IBackupManager bm = IBackupManager.Stub.asInterface(
-                    ServiceManager.getService(Context.BACKUP_SERVICE));
-            if (bm != null) {
-                // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
-                // in the BackupManager. USER_ALL is used in compatibility tests.
-                if (userId == UserHandle.USER_ALL) {
-                    userId = UserHandle.USER_SYSTEM;
-                }
-                if (DEBUG_INSTALL) {
-                    Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
-                }
-                Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
-                try {
-                    if (bm.isBackupServiceActive(userId)) {
-                        bm.restoreAtInstallForUser(
-                                userId, res.pkg.getAppInfoPackageName(), token);
-                    } else {
-                        doRestore = false;
-                    }
-                } catch (RemoteException e) {
-                    // can't happen; the backup manager is local
-                } catch (Exception e) {
-                    Slog.e(TAG, "Exception trying to enqueue restore", e);
-                    doRestore = false;
-                }
-            } else {
-                Slog.e(TAG, "Backup Manager not found!");
-                doRestore = false;
+            if (res.freezer != null) {
+                res.freezer.close();
             }
+            doRestore = performBackupManagerRestore(userId, token, res);
         }
 
         // If this is an update to a package that might be potentially downgraded, then we
@@ -13471,42 +13454,7 @@
         //
         // TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL.
         if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
-            IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                    ServiceManager.getService(Context.ROLLBACK_SERVICE));
-
-            final String packageName = res.pkg.getAppInfoPackageName();
-            final String seInfo = res.pkg.getSeInfo();
-            final int[] allUsers = mUserManager.getUserIds();
-            final int[] installedUsers;
-
-            final PackageSetting ps;
-            int appId = -1;
-            long ceDataInode = -1;
-            synchronized (mSettings) {
-                ps = mSettings.getPackageLPr(packageName);
-                if (ps != null) {
-                    appId = ps.appId;
-                    ceDataInode = ps.getCeDataInode(userId);
-                }
-
-                // NOTE: We ignore the user specified in the InstallParam because we know this is
-                // an update, and hence need to restore data for all installed users.
-                installedUsers = ps.queryInstalledUsers(allUsers, true);
-            }
-
-            boolean doSnapshotOrRestore = data != null && data.args != null
-                    && ((data.args.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
-                    || (data.args.installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
-
-            if (ps != null && doSnapshotOrRestore) {
-                try {
-                    rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
-                            seInfo, token);
-                } catch (RemoteException re) {
-                    Log.e(TAG, "Error snapshotting/restoring user data: " + re);
-                }
-                doRestore = true;
-            }
+            doRestore = performRollbackManagerRestore(userId, token, res, data);
         }
 
         if (!doRestore) {
@@ -13522,6 +13470,89 @@
     }
 
     /**
+     * Perform Backup Manager restore for a given {@link PackageInstalledInfo}.
+     * Returns whether the restore successfully completed.
+     */
+    private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
+        IBackupManager bm = IBackupManager.Stub.asInterface(
+                ServiceManager.getService(Context.BACKUP_SERVICE));
+        if (bm != null) {
+            // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
+            // in the BackupManager. USER_ALL is used in compatibility tests.
+            if (userId == UserHandle.USER_ALL) {
+                userId = UserHandle.USER_SYSTEM;
+            }
+            if (DEBUG_INSTALL) {
+                Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
+            }
+            Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
+            try {
+                if (bm.isBackupServiceActive(userId)) {
+                    bm.restoreAtInstallForUser(
+                            userId, res.pkg.getAppInfoPackageName(), token);
+                } else {
+                    return false;
+                }
+            } catch (RemoteException e) {
+                // can't happen; the backup manager is local
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception trying to enqueue restore", e);
+                return false;
+            }
+        } else {
+            Slog.e(TAG, "Backup Manager not found!");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Perform Rollback Manager restore for a given {@link PackageInstalledInfo}.
+     * Returns whether the restore successfully completed.
+     */
+    private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
+            PostInstallData data) {
+        IRollbackManager rm = IRollbackManager.Stub.asInterface(
+                ServiceManager.getService(Context.ROLLBACK_SERVICE));
+
+        final String packageName = res.pkg.getAppInfoPackageName();
+        final String seInfo = res.pkg.getSeInfo();
+        final int[] allUsers = mUserManager.getUserIds();
+        final int[] installedUsers;
+
+        final PackageSetting ps;
+        int appId = -1;
+        long ceDataInode = -1;
+        synchronized (mSettings) {
+            ps = mSettings.getPackageLPr(packageName);
+            if (ps != null) {
+                appId = ps.appId;
+                ceDataInode = ps.getCeDataInode(userId);
+            }
+
+            // NOTE: We ignore the user specified in the InstallParam because we know this is
+            // an update, and hence need to restore data for all installed users.
+            installedUsers = ps.queryInstalledUsers(allUsers, true);
+        }
+
+        boolean doSnapshotOrRestore = data != null && data.args != null
+                && ((data.args.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
+                || (data.args.installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
+
+        if (ps != null && doSnapshotOrRestore) {
+            try {
+                rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
+                        seInfo, token);
+            } catch (RemoteException re) {
+                Log.e(TAG, "Error snapshotting/restoring user data: " + re);
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Callback from PackageSettings whenever an app is first transitioned out of the
      * 'stopped' state.  Normally we just issue the broadcast, but we can't do that if
      * the app was "launched" for a restoreAtInstall operation.  Therefore we check
@@ -14802,6 +14833,7 @@
         ArrayMap<String, PackageInstalledInfo> addedChildPackages;
         // The set of packages consuming this shared library or null if no consumers exist.
         ArrayList<AndroidPackage> libraryConsumers;
+        PackageFreezer freezer;
 
         public void setError(int code, String msg) {
             setReturnCode(code);
@@ -15675,16 +15707,12 @@
                 // TODO(patb): create a more descriptive reason than unknown in future release
                 // mark all non-failure installs as UNKNOWN so we do not treat them as success
                 for (InstallRequest request : requests) {
+                    request.installResult.freezer.close();
                     if (request.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                         request.installResult.returnCode = PackageManager.INSTALL_UNKNOWN;
                     }
                 }
             }
-            for (PrepareResult result : prepareResults.values()) {
-                if (result.freezer != null) {
-                    result.freezer.close();
-                }
-            }
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
@@ -15792,15 +15820,13 @@
         public final ParsedPackage packageToScan;
         public final boolean clearCodeCache;
         public final boolean system;
-        public final PackageFreezer freezer;
         public final PackageSetting originalPs;
         public final PackageSetting disabledPs;
 
         private PrepareResult(boolean replace, int scanFlags,
                 int parseFlags, AndroidPackage existingPackage,
                 ParsedPackage packageToScan, boolean clearCodeCache, boolean system,
-                PackageFreezer freezer, PackageSetting originalPs,
-                PackageSetting disabledPs) {
+                PackageSetting originalPs, PackageSetting disabledPs) {
             this.replace = replace;
             this.scanFlags = scanFlags;
             this.parseFlags = parseFlags;
@@ -15808,7 +15834,6 @@
             this.packageToScan = packageToScan;
             this.clearCodeCache = clearCodeCache;
             this.system = system;
-            this.freezer = freezer;
             this.originalPs = originalPs;
             this.disabledPs = disabledPs;
         }
@@ -16437,9 +16462,10 @@
             shouldCloseFreezerBeforeReturn = false;
 
             return new PrepareResult(replace, targetScanFlags, targetParseFlags,
-                    existingPackage, parsedPackage, replace /* clearCodeCache */, sysPkg, freezer,
+                    existingPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,
                     ps, disabledPs);
         } finally {
+            res.freezer = freezer;
             if (shouldCloseFreezerBeforeReturn) {
                 freezer.close();
             }
@@ -19160,13 +19186,14 @@
 
     @Override
     public String getSystemTextClassifierPackageName() {
-        return mContext.getString(R.string.config_defaultTextClassifierPackage);
+        return ensureSystemPackageName(mContext.getString(
+                R.string.config_defaultTextClassifierPackage));
     }
 
     @Override
     public String[] getSystemTextClassifierPackages() {
-        return mContext.getResources().getStringArray(
-                R.array.config_defaultTextClassifierPackages);
+        return ensureSystemPackageNames(mContext.getResources().getStringArray(
+                R.array.config_defaultTextClassifierPackages));
     }
 
     @Override
@@ -19176,7 +19203,7 @@
         if (flattenedComponentName != null) {
             ComponentName componentName = ComponentName.unflattenFromString(flattenedComponentName);
             if (componentName != null && componentName.getPackageName() != null) {
-                return componentName.getPackageName();
+                return ensureSystemPackageName(componentName.getPackageName());
             }
         }
         return null;
@@ -19201,9 +19228,15 @@
         }
     }
 
+    @Nullable
+    private String getDeviceConfiguratorPackageName() {
+        return ensureSystemPackageName(mContext.getString(
+                R.string.config_deviceConfiguratorPackageName));
+    }
+
     @Override
     public String getWellbeingPackageName() {
-        return mContext.getString(R.string.config_defaultWellbeingPackage);
+        return ensureSystemPackageName(mContext.getString(R.string.config_defaultWellbeingPackage));
     }
 
     @Override
@@ -19218,7 +19251,7 @@
         if (appPredictionServiceComponentName == null) {
             return null;
         }
-        return appPredictionServiceComponentName.getPackageName();
+        return ensureSystemPackageName(appPredictionServiceComponentName.getPackageName());
     }
 
     @Override
@@ -19235,7 +19268,7 @@
         if (systemCaptionsServiceComponentName == null) {
             return null;
         }
-        return systemCaptionsServiceComponentName.getPackageName();
+        return ensureSystemPackageName(systemCaptionsServiceComponentName.getPackageName());
     }
 
     @Override
@@ -19247,7 +19280,8 @@
     }
 
     public String getIncidentReportApproverPackageName() {
-        return mContext.getString(R.string.config_incidentReportApproverPackage);
+        return ensureSystemPackageName(mContext.getString(
+                R.string.config_incidentReportApproverPackage));
     }
 
     @Override
@@ -19257,7 +19291,7 @@
         if (!TextUtils.isEmpty(names)) {
             telephonyPackageNames = names.trim().split(",");
         }
-        return telephonyPackageNames;
+        return ensureSystemPackageNames(telephonyPackageNames);
     }
 
     @Override
@@ -19274,7 +19308,32 @@
         if (contentCaptureServiceComponentName == null) {
             return null;
         }
-        return contentCaptureServiceComponentName.getPackageName();
+        return ensureSystemPackageName(contentCaptureServiceComponentName.getPackageName());
+    }
+
+    @Nullable
+    private String ensureSystemPackageName(@Nullable String packageName) {
+        if (packageName == null) {
+            return null;
+        }
+        if (getPackageInfo(packageName, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE
+                        | MATCH_DIRECT_BOOT_UNAWARE | MATCH_DISABLED_COMPONENTS,
+                UserHandle.getCallingUserId()) == null) {
+            return null;
+        }
+        return packageName;
+    }
+
+    @Nullable
+    private String[] ensureSystemPackageNames(@Nullable String[] packageNames) {
+        if (packageNames == null) {
+            return null;
+        }
+        final int packageNamesLength = packageNames.length;
+        for (int i = 0; i < packageNamesLength; i++) {
+            packageNames[i] = ensureSystemPackageName(packageNames[i]);
+        }
+        return ArrayUtils.filterNotNull(packageNames, String[]::new);
     }
 
     @Override
@@ -23199,6 +23258,14 @@
                 mSettings.writeLPr();
             }
         }
+
+        @Override
+        public void setIntegrityVerificationResult(int verificationId, int verificationResult) {
+            final Message msg = mHandler.obtainMessage(INTEGRITY_VERIFICATION_COMPLETE);
+            msg.arg1 = verificationId;
+            msg.obj = verificationResult;
+            mHandler.sendMessage(msg);
+        }
     }
 
     @GuardedBy("mLock")
@@ -23233,11 +23300,12 @@
                 PackageSetting ps = it.next();
                 if (ps.getInstalled(userId)) {
                     res[i++] = ps.name;
-                } else {
-                    res = ArrayUtils.removeElement(String.class, res, res[i]);
                 }
             }
-            return res;
+            res = ArrayUtils.trimToSize(res, i);
+            if (res != null) {
+                return res;
+            }
         } catch (PackageManagerException e) {
             // Should not happen
         }
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 9668c21..05545cd 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -115,6 +115,12 @@
         protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
     }
 
+    @Override
+    public String toString() {
+        return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
+                + "}";
+    }
+
     public String getName() {
         return name;
     }
@@ -170,7 +176,8 @@
         if (this.perm == null) {
             return false;
         }
-        return Objects.equals(this.perm.className, perm.className);
+        return Objects.equals(this.perm.getPackageName(), perm.getPackageName())
+                && Objects.equals(this.perm.className, perm.className);
     }
 
     public boolean isDynamic() {
@@ -406,7 +413,8 @@
             r.append("DUP:");
             r.append(p.getName());
         }
-        if (bp.perm != null && Objects.equals(bp.perm.className, p.className)) {
+        if (bp.perm != null && Objects.equals(bp.perm.getPackageName(), p.getPackageName())
+                && Objects.equals(bp.perm.className, p.className)) {
             bp.protectionLevel = p.protectionLevel;
         }
         if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
@@ -643,20 +651,4 @@
         }
         return true;
     }
-
-    @Override
-    public String toString() {
-        return "BasePermission{" +
-                "name='" + name + '\'' +
-                ", type=" + type +
-                ", sourcePackageName='" + sourcePackageName + '\'' +
-                ", sourcePackageSetting=" + sourcePackageSetting +
-                ", protectionLevel=" + protectionLevel +
-                ", perm=" + perm +
-                ", pendingPermissionInfo=" + pendingPermissionInfo +
-                ", uid=" + uid +
-                ", gids=" + Arrays.toString(gids) +
-                ", perUser=" + perUser +
-                '}';
-    }
 }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 5adb648..8ce1a52 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -321,7 +321,10 @@
         public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync,
                 int uid) {
             onPermissionUpdated(updatedUserIds, sync);
-            mOnPermissionChangeListeners.onPermissionsChanged(uid);
+            for (int i = 0; i < updatedUserIds.length; i++) {
+                int userUid = UserHandle.getUid(updatedUserIds[i], UserHandle.getAppId(uid));
+                mOnPermissionChangeListeners.onPermissionsChanged(userUid);
+            }
         }
         public void onInstallPermissionUpdatedNotifyListener(int uid) {
             onInstallPermissionUpdated();
@@ -733,7 +736,8 @@
             // Install and runtime permissions are stored in different places,
             // so figure out what permission changed and persist the change.
             if (permissionsState.getInstallPermissionState(permName) != null) {
-                callback.onInstallPermissionUpdatedNotifyListener(pkg.getUid());
+                int userUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
+                callback.onInstallPermissionUpdatedNotifyListener(userUid);
             } else if (permissionsState.getRuntimePermissionState(permName, userId) != null
                     || hadState) {
                 callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false,
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 5f39f51..b1feb14 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -677,10 +677,19 @@
 
         private void setUidMode(int opCode, int uid, int mode,
                 @NonNull String packageName) {
-            final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
-                    .opToPublicName(opCode), uid, packageName);
-            if (currentMode != mode) {
+            final int oldMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
+                    opCode), uid, packageName);
+            if (oldMode != mode) {
                 mAppOpsManager.setUidMode(opCode, uid, mode);
+                final int newMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
+                        opCode), uid, packageName);
+                if (newMode != mode) {
+                    // Work around incorrectly-set package mode. It never makes sense for app ops
+                    // related to runtime permissions, but can get in the way and we have to reset
+                    // it.
+                    mAppOpsManager.setMode(opCode, uid, packageName, AppOpsManager.opToDefaultMode(
+                            opCode));
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 95a5f52..f608642 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -18,6 +18,7 @@
 
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -272,14 +273,6 @@
         public WindowManager.LayoutParams getAttrs();
 
         /**
-         * Return whether this window needs the menu key shown.  Must be called
-         * with window lock held, because it may need to traverse down through
-         * window list to determine the result.
-         * @param bottom The bottom-most window to consider when determining this.
-         */
-        public boolean getNeedsMenuLw(WindowState bottom);
-
-        /**
          * Retrieve the current system UI visibility flags associated with
          * this window.
          */
@@ -876,13 +869,15 @@
             case TYPE_ACCESSIBILITY_OVERLAY:
                 // overlay put by accessibility services to intercept user interaction
                 return  30;
+            case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
+                return 31;
             case TYPE_SECURE_SYSTEM_OVERLAY:
-                return  31;
-            case TYPE_BOOT_PROGRESS:
                 return  32;
+            case TYPE_BOOT_PROGRESS:
+                return  33;
             case TYPE_POINTER:
                 // the (mouse) pointer layer
-                return  33;
+                return  34;
             default:
                 Slog.e("WindowManager", "Unknown window type: " + type);
                 return APPLICATION_LAYER;
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 6da8fb4..cc1cddd 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -41,12 +41,12 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.Vibrator;
+import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.TimingsTraceLog;
 import android.view.WindowManager;
 
-import com.android.internal.telephony.ITelephony;
 import com.android.server.LocalServices;
 import com.android.server.RescueParty;
 import com.android.server.pm.PackageManagerService;
@@ -586,19 +586,15 @@
                 TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog();
                 boolean radioOff;
 
-                final ITelephony phone =
-                        ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+                TelephonyManager telephonyManager = mContext.getSystemService(
+                        TelephonyManager.class);
 
-                try {
-                    radioOff = phone == null || !phone.needMobileRadioShutdown();
-                    if (!radioOff) {
-                        Log.w(TAG, "Turning off cellular radios...");
-                        metricStarted(METRIC_RADIO);
-                        phone.shutdownMobileRadios();
-                    }
-                } catch (RemoteException ex) {
-                    Log.e(TAG, "RemoteException during radio shutdown", ex);
-                    radioOff = true;
+                radioOff = telephonyManager == null
+                        || !telephonyManager.isAnyRadioPoweredOn();
+                if (!radioOff) {
+                    Log.w(TAG, "Turning off cellular radios...");
+                    metricStarted(METRIC_RADIO);
+                    telephonyManager.shutdownAllRadios();
                 }
 
                 Log.i(TAG, "Waiting for Radio...");
@@ -613,12 +609,7 @@
                     }
 
                     if (!radioOff) {
-                        try {
-                            radioOff = !phone.needMobileRadioShutdown();
-                        } catch (RemoteException ex) {
-                            Log.e(TAG, "RemoteException during radio shutdown", ex);
-                            radioOff = true;
-                        }
+                        radioOff = !telephonyManager.isAnyRadioPoweredOn();
                         if (radioOff) {
                             Log.i(TAG, "Radio turned off.");
                             metricEnded(METRIC_RADIO);
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 ff91299..a62bb74 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -773,9 +773,9 @@
 
         // Handle triggering the notification to show/hide when appropriate
         if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) {
-            runOnBgThread(this::triggerDynamicModeNotification);
+            triggerDynamicModeNotification();
         } else if (!enable) {
-            runOnBgThread(this::hideDynamicModeNotification);
+            hideDynamicModeNotification();
         }
 
         if (DEBUG) {
@@ -787,33 +787,42 @@
 
     @VisibleForTesting
     void triggerDynamicModeNotification() {
-        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
-        ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID,
-                R.string.dynamic_mode_notification_channel_name);
+        // The current lock is the PowerManager lock, which sits very low in the service lock
+        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
+        runOnBgThread(() -> {
+            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+            ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID,
+                    R.string.dynamic_mode_notification_channel_name);
 
-        manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID,
-                buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID,
-                        mContext.getResources().getString(R.string.dynamic_mode_notification_title),
-                        R.string.dynamic_mode_notification_summary,
-                        Intent.ACTION_POWER_USAGE_SUMMARY),
-                UserHandle.ALL);
+            manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID,
+                    buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID,
+                            mContext.getResources().getString(
+                                    R.string.dynamic_mode_notification_title),
+                            R.string.dynamic_mode_notification_summary,
+                            Intent.ACTION_POWER_USAGE_SUMMARY),
+                    UserHandle.ALL);
+        });
     }
 
     @VisibleForTesting
     void triggerStickyDisabledNotification() {
-        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
-        ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID,
-                R.string.battery_saver_notification_channel_name);
+        // The current lock is the PowerManager lock, which sits very low in the service lock
+        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
+        runOnBgThread(() -> {
+            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+            ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID,
+                    R.string.battery_saver_notification_channel_name);
 
-        final String percentage = NumberFormat.getPercentInstance()
-                .format((double) mBatteryLevel / 100.0);
-        manager.notifyAsUser(TAG, STICKY_AUTO_DISABLED_NOTIFICATION_ID,
-                buildNotification(BATTERY_SAVER_NOTIF_CHANNEL_ID,
-                        mContext.getResources().getString(
-                                R.string.battery_saver_charged_notification_title, percentage),
-                        R.string.battery_saver_off_notification_summary,
-                        Settings.ACTION_BATTERY_SAVER_SETTINGS),
-                UserHandle.ALL);
+            final String percentage = NumberFormat.getPercentInstance()
+                    .format((double) mBatteryLevel / 100.0);
+            manager.notifyAsUser(TAG, STICKY_AUTO_DISABLED_NOTIFICATION_ID,
+                    buildNotification(BATTERY_SAVER_NOTIF_CHANNEL_ID,
+                            mContext.getResources().getString(
+                                    R.string.battery_saver_charged_notification_title, percentage),
+                            R.string.battery_saver_off_notification_summary,
+                            Settings.ACTION_BATTERY_SAVER_SETTINGS),
+                    UserHandle.ALL);
+        });
     }
 
     private void ensureNotificationChannelExists(NotificationManager manager,
@@ -854,8 +863,12 @@
     }
 
     private void hideNotification(int notificationId) {
-        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
-        manager.cancel(notificationId);
+        // The current lock is the PowerManager lock, which sits very low in the service lock
+        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
+        runOnBgThread(() -> {
+            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+            manager.cancelAsUser(TAG, notificationId, UserHandle.ALL);
+        });
     }
 
     private void setStickyActive(boolean active) {
diff --git a/services/core/java/com/android/server/role/FinancialSmsManager.java b/services/core/java/com/android/server/role/FinancialSmsManager.java
deleted file mode 100644
index 2ec3993..0000000
--- a/services/core/java/com/android/server/role/FinancialSmsManager.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * 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.role;
-
-import android.Manifest;
-import android.annotation.MainThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteCallback;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.service.sms.FinancialSmsService;
-import android.service.sms.IFinancialSmsService;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-/**
- * This class binds to {@code FinancialSmsService}.
- */
-final class FinancialSmsManager {
-
-    private static final String TAG = "FinancialSmsManager";
-
-    private final Context mContext;
-    private final Object mLock = new Object();
-
-    @GuardedBy("mLock")
-    private ServiceConnection mServiceConnection;
-
-    @GuardedBy("mLock")
-    private IFinancialSmsService mRemoteService;
-
-    @GuardedBy("mLock")
-    private ArrayList<Command> mQueuedCommands;
-
-    FinancialSmsManager(Context context) {
-        mContext = context;
-    }
-
-    @Nullable
-    ServiceInfo getServiceInfo() {
-        final String packageName =
-                mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
-        if (packageName == null) {
-            Slog.w(TAG, "no external services package!");
-            return null;
-        }
-
-        final Intent intent = new Intent(FinancialSmsService.ACTION_FINANCIAL_SERVICE_INTENT);
-        intent.setPackage(packageName);
-        final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
-                PackageManager.GET_SERVICES);
-        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
-            Slog.w(TAG, "No valid components found.");
-            return null;
-        }
-        return resolveInfo.serviceInfo;
-    }
-
-    @Nullable
-    private ComponentName getServiceComponentName() {
-        final ServiceInfo serviceInfo = getServiceInfo();
-        if (serviceInfo == null) return null;
-
-        final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
-        if (!Manifest.permission.BIND_FINANCIAL_SMS_SERVICE.equals(serviceInfo.permission)) {
-            Slog.w(TAG, name.flattenToShortString() + " does not require permission "
-                    + Manifest.permission.BIND_FINANCIAL_SMS_SERVICE);
-            return null;
-        }
-
-        return name;
-    }
-
-    void reset() {
-        synchronized (mLock) {
-            if (mServiceConnection != null) {
-                mContext.unbindService(mServiceConnection);
-                mServiceConnection = null;
-            } else {
-                Slog.d(TAG, "reset(): service is not bound. Do nothing.");
-            }
-        }
-    }
-
-    /**
-     * Run a command, starting the service connection if necessary.
-     */
-    private void connectAndRun(@NonNull Command command) {
-        synchronized (mLock) {
-            if (mRemoteService != null) {
-                try {
-                    command.run(mRemoteService);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "exception calling service: " + e);
-                }
-                return;
-            } else {
-                if (mQueuedCommands == null) {
-                    mQueuedCommands = new ArrayList<>(1);
-                }
-                mQueuedCommands.add(command);
-                // If we're already connected, don't create a new connection, just leave - the
-                // command will be run when the service connects
-                if (mServiceConnection != null) return;
-            }
-
-            // Create the connection
-            mServiceConnection = new ServiceConnection() {
-                @Override
-                public void onServiceConnected(ComponentName name, IBinder service) {
-                    synchronized (mLock) {
-                        mRemoteService = IFinancialSmsService.Stub.asInterface(service);
-                        if (mQueuedCommands != null) {
-                            final int size = mQueuedCommands.size();
-                            for (int i = 0; i < size; i++) {
-                                final Command queuedCommand = mQueuedCommands.get(i);
-                                try {
-                                    queuedCommand.run(mRemoteService);
-                                } catch (RemoteException e) {
-                                    Slog.w(TAG, "exception calling " + name + ": " + e);
-                                }
-                            }
-                            mQueuedCommands = null;
-                        }
-                    }
-                }
-
-                @Override
-                @MainThread
-                public void onServiceDisconnected(ComponentName name) {
-                    synchronized (mLock) {
-                        mRemoteService = null;
-                    }
-                }
-
-                @Override
-                public void onBindingDied(ComponentName name) {
-                    synchronized (mLock) {
-                        mRemoteService = null;
-                    }
-                }
-
-                @Override
-                public void onNullBinding(ComponentName name) {
-                    synchronized (mLock) {
-                        mRemoteService = null;
-                    }
-                }
-            };
-
-            final ComponentName component = getServiceComponentName();
-            if (component != null) {
-                final Intent intent = new Intent();
-                intent.setComponent(component);
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    mContext.bindServiceAsUser(intent, mServiceConnection, Context.BIND_AUTO_CREATE,
-                            UserHandle.getUserHandleForUid(UserHandle.getCallingUserId()));
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
-            }
-        }
-    }
-
-    void getSmsMessages(RemoteCallback callback, @Nullable Bundle params) {
-        connectAndRun((service) -> service.getSmsMessages(callback, params));
-    }
-
-    void dump(String prefix, PrintWriter pw) {
-        final ComponentName impl = getServiceComponentName();
-        pw.print(prefix); pw.print("User ID: "); pw.println(UserHandle.getCallingUserId());
-        pw.print(prefix); pw.print("Queued commands: ");
-        if (mQueuedCommands == null) {
-            pw.println("N/A");
-        } else {
-            pw.println(mQueuedCommands.size());
-        }
-        pw.print(prefix); pw.print("Implementation: ");
-        if (impl == null) {
-            pw.println("N/A");
-            return;
-        }
-        pw.println(impl.flattenToShortString());
-    }
-
-    private interface Command {
-        void run(IFinancialSmsService service) throws RemoteException;
-    }
-}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index a4eef9b..c4522e0 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -50,7 +50,6 @@
 import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
-import android.service.sms.FinancialSmsService;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index a626166..bb09584 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -61,7 +61,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.TimeUnit;
 
 /**
  * {@link PackageHealthObserver} for {@link RollbackManagerService}.
@@ -74,10 +73,6 @@
     private static final String TAG = "RollbackPackageHealthObserver";
     private static final String NAME = "rollback-observer";
     private static final int INVALID_ROLLBACK_ID = -1;
-    // TODO: make the following values configurable via DeviceConfig
-    private static final long NATIVE_CRASH_POLLING_INTERVAL_MILLIS =
-            TimeUnit.SECONDS.toMillis(30);
-    private static final long NUMBER_OF_NATIVE_CRASH_POLLS = 10;
 
     private final Context mContext;
     private final Handler mHandler;
@@ -85,13 +80,9 @@
     // Staged rollback ids that have been committed but their session is not yet ready
     @GuardedBy("mPendingStagedRollbackIds")
     private final Set<Integer> mPendingStagedRollbackIds = new ArraySet<>();
-    // this field is initialized in the c'tor and then only accessed from mHandler thread, so
-    // no need to guard with a lock
-    private long mNumberOfNativeCrashPollsRemaining;
 
     RollbackPackageHealthObserver(Context context) {
         mContext = context;
-        mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
         HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver");
         handlerThread.start();
         mHandler = handlerThread.getThreadHandler();
@@ -102,9 +93,15 @@
     }
 
     @Override
-    public int onHealthCheckFailed(VersionedPackage failedPackage) {
-        if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), failedPackage)
-                == null) {
+    public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
+            @FailureReasons int failureReason) {
+        // For native crashes, we will roll back any available rollbacks
+        if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH
+                && !mContext.getSystemService(RollbackManager.class)
+                .getAvailableRollbacks().isEmpty()) {
+            return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
+        }
+        if (getAvailableRollback(failedPackage) == null) {
             // Don't handle the notification, no rollbacks available for the package
             return PackageHealthObserverImpact.USER_IMPACT_NONE;
         } else {
@@ -114,52 +111,21 @@
     }
 
     @Override
-    public boolean execute(VersionedPackage failedPackage, @FailureReasons int rollbackReason) {
-        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
-        VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
-        RollbackInfo rollback = getAvailableRollback(rollbackManager, failedPackage);
-        int reasonToLog = mapFailureReasonToMetric(rollbackReason);
+    public boolean execute(@Nullable VersionedPackage failedPackage,
+            @FailureReasons int rollbackReason) {
+        if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+            rollbackAll();
+            return true;
+        }
 
+        RollbackInfo rollback = getAvailableRollback(failedPackage);
         if (rollback == null) {
             Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ "
                     + failedPackage.getPackageName() + "] with versionCode: ["
                     + failedPackage.getVersionCode() + "]");
             return false;
         }
-
-        logEvent(moduleMetadataPackage,
-                StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
-                reasonToLog, failedPackage.getPackageName());
-        LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
-            int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
-                    RollbackManager.STATUS_FAILURE);
-            if (status == RollbackManager.STATUS_SUCCESS) {
-                if (rollback.isStaged()) {
-                    int rollbackId = rollback.getRollbackId();
-                    synchronized (mPendingStagedRollbackIds) {
-                        mPendingStagedRollbackIds.add(rollbackId);
-                    }
-                    BroadcastReceiver listener =
-                            listenForStagedSessionReady(rollbackManager, rollbackId,
-                                    moduleMetadataPackage);
-                    handleStagedSessionChange(rollbackManager, rollbackId, listener,
-                            moduleMetadataPackage);
-                } else {
-                    logEvent(moduleMetadataPackage,
-                            StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
-                            reasonToLog, failedPackage.getPackageName());
-                }
-            } else {
-                logEvent(moduleMetadataPackage,
-                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
-                        reasonToLog, failedPackage.getPackageName());
-            }
-        });
-
-        mHandler.post(() ->
-                rollbackManager.commitRollback(rollback.getRollbackId(),
-                    Collections.singletonList(failedPackage),
-                    rollbackReceiver.getIntentSender()));
+        rollbackPackage(rollback, failedPackage, rollbackReason);
         // Assume rollback executed successfully
         return true;
     }
@@ -188,10 +154,10 @@
         RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
         PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
         String moduleMetadataPackageName = getModuleMetadataPackageName();
-        VersionedPackage newModuleMetadataPackage = getModuleMetadataPackage();
 
-        if (getAvailableRollback(rollbackManager, newModuleMetadataPackage) != null) {
-            scheduleCheckAndMitigateNativeCrashes();
+        if (!rollbackManager.getAvailableRollbacks().isEmpty()) {
+            // TODO(gavincorkery): Call into Package Watchdog from outside the observer
+            PackageWatchdog.getInstance(mContext).scheduleCheckAndMitigateNativeCrashes();
         }
 
         int rollbackId = popLastStagedRollbackId();
@@ -242,8 +208,8 @@
         }
     }
 
-    private RollbackInfo getAvailableRollback(RollbackManager rollbackManager,
-            VersionedPackage failedPackage) {
+    private RollbackInfo getAvailableRollback(VersionedPackage failedPackage) {
+        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
         for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
             for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
                 boolean hasFailedPackage = packageRollback.getPackageName().equals(
@@ -285,7 +251,7 @@
     }
 
     private BroadcastReceiver listenForStagedSessionReady(RollbackManager rollbackManager,
-            int rollbackId, VersionedPackage moduleMetadataPackage) {
+            int rollbackId, @Nullable VersionedPackage moduleMetadataPackage) {
         BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -300,7 +266,7 @@
     }
 
     private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
-            BroadcastReceiver listener, VersionedPackage moduleMetadataPackage) {
+            BroadcastReceiver listener, @Nullable VersionedPackage moduleMetadataPackage) {
         PackageInstaller packageInstaller =
                 mContext.getPackageManager().getPackageInstaller();
         List<RollbackInfo> recentRollbacks =
@@ -382,36 +348,102 @@
         }
     }
 
+
     /**
-     * This method should be only called on mHandler thread, since it modifies
-     * {@link #mNumberOfNativeCrashPollsRemaining} and we want to keep this class lock free.
+     * Returns true if the package name is the name of a module.
      */
-    private void checkAndMitigateNativeCrashes() {
-        mNumberOfNativeCrashPollsRemaining--;
-        // Check if native watchdog reported a crash
-        if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
-            execute(getModuleMetadataPackage(), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
-            // we stop polling after an attempt to execute rollback, regardless of whether the
-            // attempt succeeds or not
-        } else {
-            if (mNumberOfNativeCrashPollsRemaining > 0) {
-                mHandler.postDelayed(() -> checkAndMitigateNativeCrashes(),
-                        NATIVE_CRASH_POLLING_INTERVAL_MILLIS);
-            }
+    private boolean isModule(String packageName) {
+        PackageManager pm = mContext.getPackageManager();
+        try {
+            return pm.getModuleInfo(packageName, 0) != null;
+        } catch (PackageManager.NameNotFoundException ignore) {
+            return false;
+        }
+    }
+
+    private VersionedPackage getVersionedPackage(String packageName) {
+        try {
+            return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo(
+                    packageName, 0 /* flags */).getLongVersionCode());
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
         }
     }
 
     /**
-     * Since this method can eventually trigger a RollbackManager rollback, it should be called
-     * only once boot has completed {@code onBootCompleted} and not earlier, because the install
-     * session must be entirely completed before we try to rollback.
+     * Rolls back the session that owns {@code failedPackage}
+     *
+     * @param rollback {@code rollbackInfo} of the {@code failedPackage}
+     * @param failedPackage the package that needs to be rolled back
      */
-    private void scheduleCheckAndMitigateNativeCrashes() {
-        Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check "
-                + "and mitigate native crashes");
-        mHandler.post(()->checkAndMitigateNativeCrashes());
+    private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage,
+            @FailureReasons int rollbackReason) {
+        final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+        int reasonToLog = mapFailureReasonToMetric(rollbackReason);
+        final String failedPackageToLog;
+        if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+            failedPackageToLog = SystemProperties.get(
+                    "sys.init.updatable_crashing_process_name", "");
+        } else {
+            failedPackageToLog = failedPackage.getPackageName();
+        }
+        final VersionedPackage logPackage = isModule(failedPackage.getPackageName())
+                ? getModuleMetadataPackage()
+                : null;
+
+        logEvent(logPackage,
+                StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
+                reasonToLog, failedPackageToLog);
+        final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
+            int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+                    RollbackManager.STATUS_FAILURE);
+            if (status == RollbackManager.STATUS_SUCCESS) {
+                if (rollback.isStaged()) {
+                    int rollbackId = rollback.getRollbackId();
+                    synchronized (mPendingStagedRollbackIds) {
+                        mPendingStagedRollbackIds.add(rollbackId);
+                    }
+                    BroadcastReceiver listener =
+                            listenForStagedSessionReady(rollbackManager, rollbackId,
+                                    logPackage);
+                    handleStagedSessionChange(rollbackManager, rollbackId, listener,
+                            logPackage);
+                } else {
+                    logEvent(logPackage,
+                            StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+                            reasonToLog, failedPackageToLog);
+                }
+            } else {
+                logEvent(logPackage,
+                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+                        reasonToLog, failedPackageToLog);
+            }
+        });
+
+        mHandler.post(() ->
+                rollbackManager.commitRollback(rollback.getRollbackId(),
+                        Collections.singletonList(failedPackage),
+                        rollbackReceiver.getIntentSender()));
     }
 
+    private void rollbackAll() {
+        Slog.i(TAG, "Rolling back all available rollbacks");
+        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+        List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();
+
+        for (RollbackInfo rollback : rollbacks) {
+            String samplePackageName = rollback.getPackages().get(0).getPackageName();
+            VersionedPackage sampleVersionedPackage = getVersionedPackage(samplePackageName);
+            if (sampleVersionedPackage == null) {
+                Slog.e(TAG, "Failed to rollback " + samplePackageName);
+                continue;
+            }
+            rollbackPackage(rollback, sampleVersionedPackage,
+                    PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
+        }
+    }
+
+
     private int mapFailureReasonToMetric(@FailureReasons int failureReason) {
         switch (failureReason) {
             case PackageWatchdog.FAILURE_REASON_NATIVE_CRASH:
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index d76836f..46dd366 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -43,7 +43,6 @@
 import com.android.internal.util.Preconditions;
 
 import java.io.FileDescriptor;
-import java.io.IOException;
 
 /**
  * Controls storage sessions for users initiated by the {@link StorageManagerService}.
@@ -101,18 +100,10 @@
                 connection = new StorageUserConnection(mContext, userId, this);
                 mConnections.put(userId, connection);
             }
-            Slog.i(TAG, "Creating session with id: " + sessionId);
-            connection.createSession(sessionId, new ParcelFileDescriptor(deviceFd),
+            Slog.i(TAG, "Creating and starting session with id: " + sessionId);
+            connection.startSession(sessionId, new ParcelFileDescriptor(deviceFd),
                     vol.getPath().getPath(), vol.getInternalPath().getPath());
         }
-
-        // At boot, a volume can be mounted before user is unlocked, in that case, we create it
-        // above and save it so that we can restart all sessions when the user is unlocked
-        if (mExternalStorageServiceComponent != null) {
-            connection.startSession(sessionId);
-        } else {
-            Slog.i(TAG, "Controller not initialised, session not started " + sessionId);
-        }
     }
 
     /**
@@ -179,23 +170,32 @@
      * a session will be ignored.
      */
     public void onUnlockUser(int userId) throws ExternalStorageServiceException {
+        Slog.i(TAG, "On user unlock " + userId);
+        if (shouldHandle(null) && userId == 0) {
+            initExternalStorageServiceComponent();
+        }
+    }
+
+    /**
+     * Called when a user is in the process is being stopped.
+     *
+     * Does nothing if {@link #shouldHandle} is {@code false}
+     *
+     * This call removes all sessions for the user that is being stopped;
+     * this will make sure that we don't rebind to the service needlessly.
+     */
+    public void onUserStopping(int userId) throws ExternalStorageServiceException {
         if (!shouldHandle(null)) {
             return;
         }
-
-        Slog.i(TAG, "On user unlock " + userId);
-        if (userId == 0) {
-            initExternalStorageServiceComponent();
-        }
-
         StorageUserConnection connection = null;
         synchronized (mLock) {
             connection = mConnections.get(userId);
         }
 
         if (connection != null) {
-            Slog.i(TAG, "Restarting all sessions for user: " + userId);
-            connection.startAllSessions();
+            Slog.i(TAG, "Removing all sessions for user: " + userId);
+            connection.removeAllSessions();
         } else {
             Slog.w(TAG, "No connection found for user: " + userId);
         }
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index 7c47730..5c44eee 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -34,6 +34,7 @@
 import android.os.ParcelableException;
 import android.os.RemoteCallback;
 import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
 import android.service.storage.ExternalStorageService;
 import android.service.storage.IExternalStorageService;
 import android.text.TextUtils;
@@ -41,6 +42,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
 
 import java.io.IOException;
 import java.util.HashMap;
@@ -73,42 +75,25 @@
     }
 
     /**
-     * Creates and stores a storage {@link Session}.
+     * Creates and starts a storage {@link Session}.
      *
      * They must also be cleaned up with {@link #removeSession}.
      *
      * @throws IllegalArgumentException if a {@code Session} with {@code sessionId} already exists
      */
-    public void createSession(String sessionId, ParcelFileDescriptor pfd, String upperPath,
-            String lowerPath) {
+    public void startSession(String sessionId, ParcelFileDescriptor pfd, String upperPath,
+            String lowerPath) throws ExternalStorageServiceException {
         Preconditions.checkNotNull(sessionId);
         Preconditions.checkNotNull(pfd);
         Preconditions.checkNotNull(upperPath);
         Preconditions.checkNotNull(lowerPath);
 
-        synchronized (mLock) {
-            Preconditions.checkArgument(!mSessions.containsKey(sessionId));
-            mSessions.put(sessionId, new Session(sessionId, pfd, upperPath, lowerPath));
-        }
-    }
-
-    /**
-     * Starts an already created storage {@link Session} for {@code sessionId}.
-     *
-     * It is safe to call this multiple times, however if the session is already started,
-     * subsequent calls will be ignored.
-     *
-     * @throws ExternalStorageServiceException if the session failed to start
-     **/
-    public void startSession(String sessionId) throws ExternalStorageServiceException {
-        Session session;
-        synchronized (mLock) {
-            session = mSessions.get(sessionId);
-        }
-
         prepareRemote();
         synchronized (mLock) {
-            mActiveConnection.startSessionLocked(session);
+            Preconditions.checkArgument(!mSessions.containsKey(sessionId));
+            Session session = new Session(sessionId, upperPath, lowerPath);
+            mSessions.put(sessionId, session);
+            mActiveConnection.startSessionLocked(session, pfd);
         }
     }
 
@@ -121,16 +106,10 @@
      **/
     public Session removeSession(String sessionId) {
         synchronized (mLock) {
-            Session session = mSessions.remove(sessionId);
-            if (session != null) {
-                session.close();
-                return session;
-            }
-            return null;
+            return mSessions.remove(sessionId);
         }
     }
 
-
     /**
      * Removes a session and waits for exit
      *
@@ -150,26 +129,30 @@
         }
     }
 
-    /** Starts all available sessions for a user without blocking. Any failures will be ignored. */
-    public void startAllSessions() {
-        try {
-            prepareRemote();
-        } catch (ExternalStorageServiceException e) {
-            Slog.e(TAG, "Failed to start all sessions for user: " + mUserId, e);
-            return;
-        }
-
+    /** Restarts all available sessions for a user without blocking.
+     *
+     * Any failures will be ignored.
+     **/
+    public void resetUserSessions() {
         synchronized (mLock) {
-            Slog.i(TAG, "Starting " + mSessions.size() + " sessions for user: " + mUserId + "...");
-            for (Session session : mSessions.values()) {
-                try {
-                    mActiveConnection.startSessionLocked(session);
-                } catch (IllegalStateException | ExternalStorageServiceException e) {
-                    // TODO: Don't crash process? We could get into process crash loop
-                    Slog.e(TAG, "Failed to start " + session, e);
-                }
+            if (mSessions.isEmpty()) {
+                // Nothing to reset if we have no sessions to restart; we typically
+                // hit this path if the user was consciously shut down.
+                return;
             }
         }
+        StorageManagerInternal sm = LocalServices.getService(StorageManagerInternal.class);
+        sm.resetUser(mUserId);
+    }
+
+    /**
+     * Removes all sessions, without waiting.
+     */
+    public void removeAllSessions() {
+        synchronized (mLock) {
+            Slog.i(TAG, "Removing  " + mSessions.size() + " sessions for user: " + mUserId + "...");
+            mSessions.clear();
+        }
     }
 
     /**
@@ -269,26 +252,37 @@
             return true;
         }
 
-        public void startSessionLocked(Session session) throws ExternalStorageServiceException {
+        public void startSessionLocked(Session session, ParcelFileDescriptor fd)
+                throws ExternalStorageServiceException {
             if (!isActiveLocked(session)) {
+                try {
+                    fd.close();
+                } catch (IOException e) {
+                    // ignore
+                }
                 return;
             }
 
             CountDownLatch latch = new CountDownLatch(1);
-            try (ParcelFileDescriptor dupedPfd = session.pfd.dup()) {
+            try {
                 mRemote.startSession(session.sessionId,
                         FLAG_SESSION_TYPE_FUSE | FLAG_SESSION_ATTRIBUTE_INDEXABLE,
-                        dupedPfd, session.upperPath, session.lowerPath, new RemoteCallback(result ->
+                        fd, session.upperPath, session.lowerPath, new RemoteCallback(result ->
                                 setResultLocked(latch, result)));
                 waitForLatch(latch, "start_session " + session);
                 maybeThrowExceptionLocked();
             } catch (Exception e) {
                 throw new ExternalStorageServiceException("Failed to start session: " + session, e);
+            } finally {
+                try {
+                    fd.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
             }
         }
 
         public void endSessionLocked(Session session) throws ExternalStorageServiceException {
-            session.close();
             if (!isActiveLocked(session)) {
                 // Nothing to end, not started yet
                 return;
@@ -390,7 +384,7 @@
                         // will be called for any required mounts.
                         // Notify StorageManagerService so it can restart all necessary sessions
                         close();
-                        new Thread(StorageUserConnection.this::startAllSessions).start();
+                        resetUserSessions();
                     }
                 };
             }
@@ -411,29 +405,18 @@
         }
     }
 
-    private static final class Session implements AutoCloseable {
+    private static final class Session {
         public final String sessionId;
-        public final ParcelFileDescriptor pfd;
         public final String lowerPath;
         public final String upperPath;
 
-        Session(String sessionId, ParcelFileDescriptor pfd, String upperPath, String lowerPath) {
+        Session(String sessionId, String upperPath, String lowerPath) {
             this.sessionId = sessionId;
-            this.pfd = pfd;
             this.upperPath = upperPath;
             this.lowerPath = lowerPath;
         }
 
         @Override
-        public void close() {
-            try {
-                pfd.close();
-            } catch (IOException e) {
-                Slog.i(TAG, "Failed to close session: " + this);
-            }
-        }
-
-        @Override
         public String toString() {
             return "[SessionId: " + sessionId + ". UpperPath: " + upperPath + ". LowerPath: "
                     + lowerPath + "]";
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 7d905ba..6a986b9 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -24,15 +24,12 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
-import android.provider.Settings;
 import android.service.textclassifier.ITextClassifierCallback;
 import android.service.textclassifier.ITextClassifierService;
 import android.service.textclassifier.TextClassifierService;
@@ -41,7 +38,6 @@
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.view.textclassifier.ConfigParser;
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.TextClassification;
@@ -143,7 +139,7 @@
     private TextClassificationManagerService(Context context) {
         mContext = Preconditions.checkNotNull(context);
         mLock = new Object();
-        mSettingsListener = new TextClassifierSettingsListener(mContext, this);
+        mSettingsListener = new TextClassifierSettingsListener(mContext);
     }
 
     private void startListenSettings() {
@@ -171,10 +167,8 @@
                 Slog.d(LOG_TAG, "Unable to bind TextClassifierService at suggestSelection.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
-                if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
-                    Slog.d(LOG_TAG,
-                            "Only allow to see own content for non-default service at "
-                                    + "suggestSelection.");
+                if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+                        "suggestSelection")) {
                     return;
                 }
                 userState.mService.onSuggestSelection(sessionId, request, callback);
@@ -203,10 +197,7 @@
                 Slog.d(LOG_TAG, "Unable to bind TextClassifierService at classifyText.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
-                if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
-                    Slog.d(LOG_TAG,
-                            "Only allow to see own content for non-default service at "
-                                    + "classifyText.");
+                if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(), "classifyText")) {
                     return;
                 }
                 userState.mService.onClassifyText(sessionId, request, callback);
@@ -235,10 +226,8 @@
                 Slog.d(LOG_TAG, "Unable to bind TextClassifierService at generateLinks.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
-                if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
-                    Slog.d(LOG_TAG,
-                            "Only allow to see own content for non-default service at "
-                                    + "generateLinks.");
+                if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+                        "generateLinks")) {
                     return;
                 }
                 userState.mService.onGenerateLinks(sessionId, request, callback);
@@ -262,10 +251,8 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
-                if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
-                    Slog.d(LOG_TAG,
-                            "Only allow to see own content for non-default service at "
-                                    + "selectionEvent.");
+                if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+                        "selectionEvent")) {
                     return;
                 }
                 userState.mService.onSelectionEvent(sessionId, event);
@@ -293,10 +280,8 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
-                if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
-                    Slog.d(LOG_TAG,
-                            "Only allow to see own content for non-default service at "
-                                    + "textClassifierEvent.");
+                if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+                        "textClassifierEvent")) {
                     return;
                 }
                 userState.mService.onTextClassifierEvent(sessionId, event);
@@ -325,10 +310,8 @@
                 Slog.d(LOG_TAG, "Unable to bind TextClassifierService at detectLanguage.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
-                if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
-                    Slog.d(LOG_TAG,
-                            "Only allow to see own content for non-default service at "
-                                    + "detectLanguage.");
+                if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+                        "detectLanguage")) {
                     return;
                 }
                 userState.mService.onDetectLanguage(sessionId, request, callback);
@@ -358,10 +341,8 @@
                         "Unable to bind TextClassifierService at suggestConversationActions.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
-                if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
-                    Slog.d(LOG_TAG,
-                            "Only allow to see own content for non-default service at "
-                                    + "suggestConversationActions.");
+                if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+                        "suggestConversationActions")) {
                     return;
                 }
                 userState.mService.onSuggestConversationActions(sessionId, request, callback);
@@ -387,10 +368,8 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
-                if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
-                    Slog.d(LOG_TAG,
-                            "Only allow to see own content for non-default service at "
-                                    + "createTextClassificationSession.");
+                if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+                        "createTextClassificationSession")) {
                     return;
                 }
                 userState.mService.onCreateTextClassificationSession(
@@ -422,10 +401,8 @@
 
             UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
-                if (!userState.isRequestAcceptedLocked(Binder.getCallingUid())) {
-                    Slog.d(LOG_TAG,
-                            "Only allow to see own content for non-default service at "
-                                    + "destroyTextClassificationSession.");
+                if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(),
+                        "destroyTextClassificationSession")) {
                     return;
                 }
                 userState.mService.onDestroyTextClassificationSession(sessionId);
@@ -488,6 +465,29 @@
         }
     }
 
+    private void unbindServiceIfNecessary() {
+        final ComponentName serviceComponentName =
+                TextClassifierService.getServiceComponentName(mContext);
+        if (serviceComponentName == null) {
+            // It should not occur if we had defined default service names in config.xml
+            Slog.w(LOG_TAG, "No default configured system TextClassifierService.");
+            return;
+        }
+        synchronized (mLock) {
+            final int size = mUserStates.size();
+            for (int i = 0; i < size; i++) {
+                UserState userState = mUserStates.valueAt(i);
+                // Only unbind for a new service
+                if (userState.isCurrentlyBoundToComponentLocked(serviceComponentName)) {
+                    return;
+                }
+                if (userState.isBoundLocked()) {
+                    userState.unbindLocked();
+                }
+            }
+        }
+    }
+
     private static final class PendingRequest implements IBinder.DeathRecipient {
 
         private final int mUid;
@@ -582,48 +582,6 @@
         }
     }
 
-    private TextClassificationConstants getTextClassifierSettings(Context context) {
-        synchronized (mLock) {
-            if (mSettings == null) {
-                mSettings = new TextClassificationConstants(
-                        () ->  Settings.Global.getString(
-                                context.getContentResolver(),
-                                Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
-            }
-            return mSettings;
-        }
-    }
-
-    private void invalidateSettings() {
-        synchronized (mLock) {
-            mSettings = null;
-        }
-    }
-
-    private void unbindServiceIfNeeded() {
-        final ComponentName serviceComponentName =
-                TextClassifierService.getServiceComponentName(mContext,
-                        getTextClassifierSettings(mContext));
-        if (serviceComponentName == null) {
-            // It should not occur if we had defined default service name in config
-            Slog.w(LOG_TAG, "No default configured system TextClassifierService.");
-            return;
-        }
-        synchronized (mLock) {
-            final int size = mUserStates.size();
-            for (int i = 0; i < size; i++) {
-                UserState userState = mUserStates.valueAt(i);
-                // Only unbind for a new service
-                if (userState.isServiceCurrentBoundLocked(serviceComponentName)) {
-                    return;
-                }
-                if (userState.isBoundLocked()) {
-                    userState.unbindLocked();
-                }
-            }
-        }
-    }
-
     private final class UserState {
         @UserIdInt final int mUserId;
         @GuardedBy("mLock")
@@ -635,9 +593,9 @@
         @GuardedBy("mLock")
         boolean mBinding;
         @GuardedBy("mLock")
-        ComponentName mBoundServiceComponent = null;
+        ComponentName mBoundComponentName = null;
         @GuardedBy("mLock")
-        boolean mIsBoundToDefaultService;
+        boolean mBoundToDefaultTrustService;
         @GuardedBy("mLock")
         int mBoundServiceUid;
 
@@ -660,10 +618,7 @@
             PendingRequest request;
             while ((request = mPendingRequests.poll()) != null) {
                 if (isBoundLocked()) {
-                    if (!isRequestAcceptedLocked(request.mUid)) {
-                        Slog.d(LOG_TAG,
-                                "Only allow to see own content for non-default service at "
-                                        + request.mName);
+                    if (!checkRequestAcceptedLocked(request.mUid, request.mName)) {
                         return;
                     }
                     request.mRequest.run();
@@ -687,15 +642,15 @@
         }
 
         @GuardedBy("mLock")
-        private boolean isServiceCurrentBoundLocked(@NonNull ComponentName componentName) {
-            return (mBoundServiceComponent != null
-                    && mBoundServiceComponent.getPackageName().equals(
+        private boolean isCurrentlyBoundToComponentLocked(@NonNull ComponentName componentName) {
+            return (mBoundComponentName != null
+                    && mBoundComponentName.getPackageName().equals(
                     componentName.getPackageName()));
         }
 
         @GuardedBy("mLock")
         private void unbindLocked() {
-            Slog.d(LOG_TAG, "unbinding to " + mBoundServiceComponent + " for " + mUserId);
+            Slog.v(LOG_TAG, "unbinding from " + mBoundComponentName + " for " + mUserId);
             mContext.unbindService(mConnection);
             mConnection.cleanupService();
             mConnection = null;
@@ -716,8 +671,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 final ComponentName componentName =
-                        TextClassifierService.getServiceComponentName(mContext,
-                                getTextClassifierSettings(mContext));
+                        TextClassifierService.getServiceComponentName(mContext);
                 if (componentName == null) {
                     // Might happen if the storage is encrypted and the user is not unlocked
                     return false;
@@ -742,8 +696,8 @@
             pw.printPair("context", mContext);
             pw.printPair("userId", mUserId);
             synchronized (mLock) {
-                pw.printPair("BoundServiceComponent", mBoundServiceComponent);
-                pw.printPair("isBoundToDefaultService", mIsBoundToDefaultService);
+                pw.printPair("boundComponentName", mBoundComponentName);
+                pw.printPair("boundToDefaultTrustService", mBoundToDefaultTrustService);
                 pw.printPair("boundServiceUid", mBoundServiceUid);
                 pw.printPair("binding", mBinding);
                 pw.printPair("numberRequests", mPendingRequests.size());
@@ -751,14 +705,17 @@
         }
 
         @GuardedBy("mLock")
-        private boolean isRequestAcceptedLocked(int requestUid) {
-            if (mIsBoundToDefaultService) {
+        private boolean checkRequestAcceptedLocked(int requestUid, @NonNull String methodName) {
+            if (mBoundToDefaultTrustService || (requestUid == mBoundServiceUid)) {
                 return true;
             }
-            return (requestUid == mBoundServiceUid);
+            Slog.w(LOG_TAG, String.format(
+                    "[%s] Non-default TextClassifierServices may only see text from the same uid.",
+                    methodName));
+            return false;
         }
 
-        private boolean isDefaultService(@NonNull ComponentName currentService) {
+        private boolean isDefaultTrustService(@NonNull ComponentName currentService) {
             final String[] defaultServiceNames =
                     mContext.getPackageManager().getSystemTextClassifierPackages();
             final String servicePackageName = currentService.getPackageName();
@@ -789,16 +746,16 @@
         }
 
         @GuardedBy("mLock")
-        private void updateServiceInfoLocked(@Nullable ComponentName componentName, int userId) {
-            mBoundServiceComponent = componentName;
-            mIsBoundToDefaultService = (mBoundServiceComponent != null && isDefaultService(
-                    mBoundServiceComponent));
-            mBoundServiceUid = getServiceUid(mBoundServiceComponent, userId);
+        private void updateServiceInfoLocked(int userId, @Nullable ComponentName componentName) {
+            mBoundComponentName = componentName;
+            mBoundToDefaultTrustService = (mBoundComponentName != null && isDefaultTrustService(
+                    mBoundComponentName));
+            mBoundServiceUid = getServiceUid(mBoundComponentName, userId);
         }
 
         private final class TextClassifierServiceConnection implements ServiceConnection {
 
-            @UserIdInt final int mUserId;
+            @UserIdInt private final int mUserId;
 
             TextClassifierServiceConnection(int userId) {
                 mUserId = userId;
@@ -840,60 +797,42 @@
                 synchronized (mLock) {
                     mService = service;
                     mBinding = false;
-                    updateServiceInfoLocked(name, mUserId);
+                    updateServiceInfoLocked(mUserId, name);
                     handlePendingRequestsLocked();
                 }
             }
         }
     }
 
-    private final class TextClassifierSettingsListener extends ContentObserver
-            implements DeviceConfig.OnPropertiesChangedListener {
+    private final class TextClassifierSettingsListener implements
+            DeviceConfig.OnPropertiesChangedListener {
 
         @NonNull private final Context mContext;
+        @NonNull private final TextClassificationConstants mSettings;
         @Nullable private String mServicePackageName;
 
-        TextClassifierSettingsListener(Context context, TextClassificationManagerService service) {
-            super(null);
+        TextClassifierSettingsListener(Context context) {
             mContext = context;
-            mServicePackageName =
-                    getTextClassifierSettings(mContext).getTextClassifierServiceName();
+            mSettings = TextClassificationManager.getSettings(mContext);
+            mServicePackageName = mSettings.getTextClassifierServicePackageOverride();
         }
 
         public void registerObserver() {
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.TEXT_CLASSIFIER_CONSTANTS),
-                    false /* notifyForDescendants */,
+            DeviceConfig.addOnPropertiesChangedListener(
+                    DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                    mContext.getMainExecutor(),
                     this);
-            if (ConfigParser.ENABLE_DEVICE_CONFIG) {
-                DeviceConfig.addOnPropertiesChangedListener(
-                        DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                        mContext.getMainExecutor(),
-                        this);
-            }
-        }
-
-        private void updateChange() {
-            final String overrideServiceName = getTextClassifierSettings(
-                    mContext).getTextClassifierServiceName();
-            if (TextUtils.equals(overrideServiceName, mServicePackageName)) {
-                return;
-            }
-            mServicePackageName = overrideServiceName;
-            unbindServiceIfNeeded();
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            super.onChange(selfChange, uri);
-            invalidateSettings();
-            updateChange();
         }
 
         @Override
         public void onPropertiesChanged(DeviceConfig.Properties properties) {
-            invalidateSettings();
-            updateChange();
+            final String overrideServiceName = mSettings.getTextClassifierServicePackageOverride();
+
+            if (TextUtils.equals(overrideServiceName, mServicePackageName)) {
+                return;
+            }
+            mServicePackageName = overrideServiceName;
+            unbindServiceIfNecessary();
         }
     }
 }
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
deleted file mode 100644
index 4e8ba07..0000000
--- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
+++ /dev/null
@@ -1,305 +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.timedetector;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.timedetector.ManualTimeSuggestion;
-import android.app.timedetector.PhoneTimeSuggestion;
-import android.content.Intent;
-import android.util.LocalLog;
-import android.util.Slog;
-import android.util.TimestampedValue;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to
- * {@link AlarmManager}.
- *
- * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
- */
-public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
-
-    private static final boolean DBG = false;
-    private static final String LOG_TAG = "SimpleTimeDetectorStrategy";
-
-    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Origin {}
-
-    /** Used when a time value originated from a telephony signal. */
-    @Origin
-    private static final int ORIGIN_PHONE = 1;
-
-    /** Used when a time value originated from a user / manual settings. */
-    @Origin
-    private static final int ORIGIN_MANUAL = 2;
-
-    /**
-     * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the
-     * actual system clock time before a warning is logged. Used to help identify situations where
-     * there is something other than this class setting the system clock automatically.
-     */
-    private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
-
-    // A log for changes made to the system clock and why.
-    @NonNull
-    private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
-
-    // @NonNull after initialize()
-    private Callback mCallback;
-
-    // Last phone suggestion.
-    @Nullable private PhoneTimeSuggestion mLastPhoneSuggestion;
-
-    // Information about the last time signal received: Used when toggling auto-time.
-    @Nullable private TimestampedValue<Long> mLastAutoSystemClockTime;
-    private boolean mLastAutoSystemClockTimeSendNetworkBroadcast;
-
-    // System clock state.
-    @Nullable private TimestampedValue<Long> mLastAutoSystemClockTimeSet;
-
-    @Override
-    public void initialize(@NonNull Callback callback) {
-        mCallback = callback;
-    }
-
-    @Override
-    public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
-        // NITZ logic
-
-        // Empty suggestions are just ignored as we don't currently keep track of suggestion origin.
-        if (timeSuggestion.getUtcTime() == null) {
-            return;
-        }
-
-        boolean timeSuggestionIsValid =
-                validateNewPhoneSuggestion(timeSuggestion, mLastPhoneSuggestion);
-        if (!timeSuggestionIsValid) {
-            return;
-        }
-        // Always store the last NITZ value received, regardless of whether we go on to use it to
-        // update the system clock. This is so that we can validate future phone suggestions.
-        mLastPhoneSuggestion = timeSuggestion;
-
-        // System clock update logic.
-        final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
-        setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, timeSuggestion);
-    }
-
-    @Override
-    public synchronized void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
-        final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
-        setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, timeSuggestion);
-    }
-
-    private static boolean validateNewPhoneSuggestion(@NonNull PhoneTimeSuggestion newSuggestion,
-            @Nullable PhoneTimeSuggestion lastSuggestion) {
-
-        if (lastSuggestion != null) {
-            long referenceTimeDifference = TimestampedValue.referenceTimeDifference(
-                    newSuggestion.getUtcTime(), lastSuggestion.getUtcTime());
-            if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) {
-                // Out of order or bogus.
-                Slog.w(LOG_TAG, "Bad NITZ signal received."
-                        + " referenceTimeDifference=" + referenceTimeDifference
-                        + " lastSuggestion=" + lastSuggestion
-                        + " newSuggestion=" + newSuggestion);
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @GuardedBy("this")
-    private void setSystemClockIfRequired(
-            @Origin int origin, TimestampedValue<Long> time, Object cause) {
-        // Historically, Android has sent a TelephonyIntents.ACTION_NETWORK_SET_TIME broadcast only
-        // when setting the time using NITZ.
-        boolean sendNetworkBroadcast = origin == ORIGIN_PHONE;
-
-        boolean isOriginAutomatic = isOriginAutomatic(origin);
-        if (isOriginAutomatic) {
-            // Store the last auto time candidate we've seen in all cases so we can set the system
-            // clock when/if time detection is off but later enabled.
-            mLastAutoSystemClockTime = time;
-            mLastAutoSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast;
-
-            if (!mCallback.isAutoTimeDetectionEnabled()) {
-                if (DBG) {
-                    Slog.d(LOG_TAG, "Auto time detection is not enabled."
-                            + " origin=" + origin
-                            + ", time=" + time
-                            + ", cause=" + cause);
-                }
-                return;
-            }
-        } else {
-            if (mCallback.isAutoTimeDetectionEnabled()) {
-                if (DBG) {
-                    Slog.d(LOG_TAG, "Auto time detection is enabled."
-                            + " origin=" + origin
-                            + ", time=" + time
-                            + ", cause=" + cause);
-                }
-                return;
-            }
-        }
-
-        mCallback.acquireWakeLock();
-        try {
-            long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
-            long actualTimeMillis = mCallback.systemClockMillis();
-
-            if (isOriginAutomatic) {
-                // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
-                // may be setting the clock.
-                if (mLastAutoSystemClockTimeSet != null) {
-                    long expectedTimeMillis = TimeDetectorStrategy.getTimeAt(
-                            mLastAutoSystemClockTimeSet, elapsedRealtimeMillis);
-                    long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis);
-                    if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) {
-                        Slog.w(LOG_TAG,
-                                "System clock has not tracked elapsed real time clock. A clock may"
-                                        + " be inaccurate or something unexpectedly set the system"
-                                        + " clock."
-                                        + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
-                                        + " expectedTimeMillis=" + expectedTimeMillis
-                                        + " actualTimeMillis=" + actualTimeMillis);
-                    }
-                }
-            }
-
-            adjustAndSetDeviceSystemClock(
-                    time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, cause);
-        } finally {
-            mCallback.releaseWakeLock();
-        }
-    }
-
-    private static boolean isOriginAutomatic(@Origin int origin) {
-        return origin == ORIGIN_PHONE;
-    }
-
-    @Override
-    public synchronized void handleAutoTimeDetectionChanged() {
-        // If automatic time detection is enabled we update the system clock instantly if we can.
-        // Conversely, if automatic time detection is disabled we leave the clock as it is.
-        boolean enabled = mCallback.isAutoTimeDetectionEnabled();
-        if (enabled) {
-            if (mLastAutoSystemClockTime != null) {
-                // Only send the network broadcast if the last candidate would have caused one.
-                final boolean sendNetworkBroadcast = mLastAutoSystemClockTimeSendNetworkBroadcast;
-
-                mCallback.acquireWakeLock();
-                try {
-                    long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
-                    long actualTimeMillis = mCallback.systemClockMillis();
-
-                    final String reason = "Automatic time detection enabled.";
-                    adjustAndSetDeviceSystemClock(mLastAutoSystemClockTime, sendNetworkBroadcast,
-                            elapsedRealtimeMillis, actualTimeMillis, reason);
-                } finally {
-                    mCallback.releaseWakeLock();
-                }
-            }
-        } else {
-            // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what
-            // it should be in future.
-            mLastAutoSystemClockTimeSet = null;
-        }
-    }
-
-    @Override
-    public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
-        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
-        ipw.println("TimeDetectorStrategy:");
-        ipw.increaseIndent(); // level 1
-
-        ipw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
-        ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
-        ipw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime);
-        ipw.println("mLastAutoSystemClockTimeSendNetworkBroadcast="
-                + mLastAutoSystemClockTimeSendNetworkBroadcast);
-
-
-        ipw.println("Time change log:");
-        ipw.increaseIndent(); // level 2
-        mTimeChangesLog.dump(ipw);
-        ipw.decreaseIndent(); // level 2
-
-        ipw.decreaseIndent(); // level 1
-        ipw.flush();
-    }
-
-    @GuardedBy("this")
-    private void adjustAndSetDeviceSystemClock(
-            TimestampedValue<Long> newTime, boolean sendNetworkBroadcast,
-            long elapsedRealtimeMillis, long actualSystemClockMillis, Object cause) {
-
-        // Adjust for the time that has elapsed since the signal was received.
-        long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis);
-
-        // Check if the new signal would make sufficient difference to the system clock. If it's
-        // below the threshold then ignore it.
-        long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
-        long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
-        if (absTimeDifference < systemClockUpdateThreshold) {
-            if (DBG) {
-                Slog.d(LOG_TAG, "Not setting system clock. New time and"
-                        + " system clock are close enough."
-                        + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
-                        + " newTime=" + newTime
-                        + " cause=" + cause
-                        + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
-                        + " absTimeDifference=" + absTimeDifference);
-            }
-            return;
-        }
-
-        mCallback.setSystemClock(newSystemClockMillis);
-        String logMsg = "Set system clock using time=" + newTime
-                + " cause=" + cause
-                + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
-                + " newSystemClockMillis=" + newSystemClockMillis;
-        if (DBG) {
-            Slog.d(LOG_TAG, logMsg);
-        }
-        mTimeChangesLog.log(logMsg);
-
-        // CLOCK_PARANOIA : Record the last time this class set the system clock.
-        mLastAutoSystemClockTimeSet = newTime;
-
-        if (sendNetworkBroadcast) {
-            // Send a broadcast that telephony code used to send after setting the clock.
-            // TODO Remove this broadcast as soon as there are no remaining listeners.
-            Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
-            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-            intent.putExtra("time", newSystemClockMillis);
-            mCallback.sendStickyBroadcast(intent);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 34400ff..172367a 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -31,7 +31,6 @@
 import com.android.internal.util.DumpUtils;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
-import com.android.server.timedetector.TimeDetectorStrategy.Callback;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -58,17 +57,16 @@
 
     @NonNull private final Handler mHandler;
     @NonNull private final Context mContext;
-    @NonNull private final Callback mCallback;
     @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
 
     private static TimeDetectorService create(@NonNull Context context) {
-        TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
+        TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl();
         TimeDetectorStrategyCallbackImpl callback = new TimeDetectorStrategyCallbackImpl(context);
-        timeDetector.initialize(callback);
+        timeDetectorStrategy.initialize(callback);
 
         Handler handler = FgThread.getHandler();
         TimeDetectorService timeDetectorService =
-                new TimeDetectorService(context, handler, callback, timeDetector);
+                new TimeDetectorService(context, handler, timeDetectorStrategy);
 
         // Wire up event listening.
         ContentResolver contentResolver = context.getContentResolver();
@@ -85,10 +83,9 @@
 
     @VisibleForTesting
     public TimeDetectorService(@NonNull Context context, @NonNull Handler handler,
-            @NonNull Callback callback, @NonNull TimeDetectorStrategy timeDetectorStrategy) {
+            @NonNull TimeDetectorStrategy timeDetectorStrategy) {
         mContext = Objects.requireNonNull(context);
         mHandler = Objects.requireNonNull(handler);
-        mCallback = Objects.requireNonNull(callback);
         mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
     }
 
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
new file mode 100644
index 0000000..1b1ac6d
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -0,0 +1,549 @@
+/*
+ * 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.timedetector;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.PhoneTimeSuggestion;
+import android.content.Intent;
+import android.util.ArrayMap;
+import android.util.LocalLog;
+import android.util.Slog;
+import android.util.TimestampedValue;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * An implementation of TimeDetectorStrategy that passes phone and manual suggestions to
+ * {@link AlarmManager}. When there are multiple phone sources, the one with the lowest ID is used
+ * unless the data becomes too stale.
+ *
+ * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
+ */
+public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
+
+    private static final boolean DBG = false;
+    private static final String LOG_TAG = "SimpleTimeDetectorStrategy";
+
+    /** A score value used to indicate "no score", either due to validation failure or age. */
+    private static final int PHONE_INVALID_SCORE = -1;
+    /** The number of buckets phone suggestions can be put in by age. */
+    private static final int PHONE_BUCKET_COUNT = 24;
+    /** Each bucket is this size. All buckets are equally sized. */
+    @VisibleForTesting
+    static final int PHONE_BUCKET_SIZE_MILLIS = 60 * 60 * 1000;
+    /** Phone suggestions older than this value are considered too old. */
+    @VisibleForTesting
+    static final long PHONE_MAX_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS;
+
+    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Origin {}
+
+    /** Used when a time value originated from a telephony signal. */
+    @Origin
+    private static final int ORIGIN_PHONE = 1;
+
+    /** Used when a time value originated from a user / manual settings. */
+    @Origin
+    private static final int ORIGIN_MANUAL = 2;
+
+    /**
+     * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the
+     * actual system clock time before a warning is logged. Used to help identify situations where
+     * there is something other than this class setting the system clock.
+     */
+    private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
+
+    /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
+    private static final int KEEP_SUGGESTION_HISTORY_SIZE = 30;
+
+    // A log for changes made to the system clock and why.
+    @NonNull
+    private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
+
+    // @NonNull after initialize()
+    private Callback mCallback;
+
+    // Used to store the last time the system clock state was set automatically. It is used to
+    // detect (and log) issues with the realtime clock or whether the clock is being set without
+    // going through this strategy code.
+    @GuardedBy("this")
+    @Nullable
+    private TimestampedValue<Long> mLastAutoSystemClockTimeSet;
+
+    /**
+     * A mapping from phoneId to a linked list of time suggestions (the "first" being the latest).
+     * We typically expect one or two entries in this Map: devices will have a small number
+     * of telephony devices and phoneIds are assumed to be stable. The LinkedList associated with
+     * the ID will not exceed {@link #KEEP_SUGGESTION_HISTORY_SIZE} in size.
+     */
+    @GuardedBy("this")
+    private ArrayMap<Integer, LinkedList<PhoneTimeSuggestion>> mSuggestionByPhoneId =
+            new ArrayMap<>();
+
+    @Override
+    public void initialize(@NonNull Callback callback) {
+        mCallback = callback;
+    }
+
+    @Override
+    public synchronized void suggestManualTime(@NonNull ManualTimeSuggestion suggestion) {
+        final TimestampedValue<Long> newUtcTime = suggestion.getUtcTime();
+
+        if (!validateSuggestionTime(newUtcTime, suggestion)) {
+            return;
+        }
+
+        String cause = "Manual time suggestion received: suggestion=" + suggestion;
+        setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, cause);
+    }
+
+    @Override
+    public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
+        // Empty time suggestion means that telephony network connectivity has been lost.
+        // The passage of time is relentless, and we don't expect our users to use a time machine,
+        // so we can continue relying on previous suggestions when we lose connectivity. This is
+        // unlike time zone, where a user may lose connectivity when boarding a flight and where we
+        // do want to "forget" old signals. Suggestions that are too old are discarded later in the
+        // detection algorithm.
+        if (timeSuggestion.getUtcTime() == null) {
+            return;
+        }
+
+        // Perform validation / input filtering and record the validated suggestion against the
+        // phoneId.
+        if (!validateAndStorePhoneSuggestion(timeSuggestion)) {
+            return;
+        }
+
+        // Now perform auto time detection. The new suggestion may be used to modify the system
+        // clock.
+        String reason = "New phone time suggested. timeSuggestion=" + timeSuggestion;
+        doAutoTimeDetection(reason);
+    }
+
+    @Override
+    public synchronized void handleAutoTimeDetectionChanged() {
+        boolean enabled = mCallback.isAutoTimeDetectionEnabled();
+        // When automatic time detection is enabled we update the system clock instantly if we can.
+        // Conversely, when automatic time detection is disabled we leave the clock as it is.
+        if (enabled) {
+            String reason = "Auto time zone detection setting enabled.";
+            doAutoTimeDetection(reason);
+        } else {
+            // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what
+            // it should be in future.
+            mLastAutoSystemClockTimeSet = null;
+        }
+    }
+
+    @Override
+    public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+        ipw.println("TimeDetectorStrategy:");
+        ipw.increaseIndent(); // level 1
+
+        ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
+
+        ipw.println("Time change log:");
+        ipw.increaseIndent(); // level 2
+        mTimeChangesLog.dump(ipw);
+        ipw.decreaseIndent(); // level 2
+
+        ipw.println("Phone suggestion history:");
+        ipw.increaseIndent(); // level 2
+        for (Map.Entry<Integer, LinkedList<PhoneTimeSuggestion>> entry
+                : mSuggestionByPhoneId.entrySet()) {
+            ipw.println("Phone " + entry.getKey());
+
+            ipw.increaseIndent(); // level 3
+            for (PhoneTimeSuggestion suggestion : entry.getValue()) {
+                ipw.println(suggestion);
+            }
+            ipw.decreaseIndent(); // level 3
+        }
+        ipw.decreaseIndent(); // level 2
+
+        ipw.decreaseIndent(); // level 1
+        ipw.flush();
+    }
+
+    @GuardedBy("this")
+    private boolean validateAndStorePhoneSuggestion(@NonNull PhoneTimeSuggestion suggestion) {
+        TimestampedValue<Long> newUtcTime = suggestion.getUtcTime();
+        if (!validateSuggestionTime(newUtcTime, suggestion)) {
+            // There's probably nothing useful we can do: elsewhere we assume that reference
+            // times are in the past so just stop here.
+            return false;
+        }
+
+        int phoneId = suggestion.getPhoneId();
+        LinkedList<PhoneTimeSuggestion> phoneSuggestions = mSuggestionByPhoneId.get(phoneId);
+        if (phoneSuggestions == null) {
+            // The first time we've seen this phoneId.
+            phoneSuggestions = new LinkedList<>();
+            mSuggestionByPhoneId.put(phoneId, phoneSuggestions);
+        } else if (phoneSuggestions.isEmpty()) {
+            Slog.w(LOG_TAG, "Suggestions unexpectedly empty when adding suggestion=" + suggestion);
+        }
+
+        if (!phoneSuggestions.isEmpty()) {
+            // We can log / discard suggestions with obvious issues with the reference time clock.
+            PhoneTimeSuggestion previousSuggestion = phoneSuggestions.getFirst();
+            if (previousSuggestion == null
+                    || previousSuggestion.getUtcTime() == null
+                    || previousSuggestion.getUtcTime().getValue() == null) {
+                // This should be impossible given we only store validated suggestions.
+                Slog.w(LOG_TAG, "Previous suggestion is null or has a null time."
+                        + " previousSuggestion=" + previousSuggestion
+                        + ", suggestion=" + suggestion);
+                return false;
+            }
+
+            long referenceTimeDifference = TimestampedValue.referenceTimeDifference(
+                    newUtcTime, previousSuggestion.getUtcTime());
+            if (referenceTimeDifference < 0) {
+                // The reference time is before the previously received suggestion. Ignore it.
+                Slog.w(LOG_TAG, "Out of order phone suggestion received."
+                        + " referenceTimeDifference=" + referenceTimeDifference
+                        + " previousSuggestion=" + previousSuggestion
+                        + " suggestion=" + suggestion);
+                return false;
+            }
+        }
+
+        // Store the latest suggestion.
+        phoneSuggestions.addFirst(suggestion);
+        if (phoneSuggestions.size() > KEEP_SUGGESTION_HISTORY_SIZE) {
+            phoneSuggestions.removeLast();
+        }
+        return true;
+    }
+
+    private boolean validateSuggestionTime(
+            @NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) {
+        if (newUtcTime.getValue() == null) {
+            Slog.w(LOG_TAG, "Suggested time value is null. suggestion=" + suggestion);
+            return false;
+        }
+
+        // We can validate the suggestion against the reference time clock.
+        long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+        if (elapsedRealtimeMillis < newUtcTime.getReferenceTimeMillis()) {
+            // elapsedRealtime clock went backwards?
+            Slog.w(LOG_TAG, "New reference time is in the future? Ignoring."
+                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                    + ", suggestion=" + suggestion);
+            return false;
+        }
+        return true;
+    }
+
+    @GuardedBy("this")
+    private void doAutoTimeDetection(@NonNull String detectionReason) {
+        if (!mCallback.isAutoTimeDetectionEnabled()) {
+            // Avoid doing unnecessary work with this (race-prone) check.
+            return;
+        }
+
+        PhoneTimeSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
+
+        // Work out what to do with the best suggestion.
+        if (bestPhoneSuggestion == null) {
+            // There is no good phone suggestion.
+            if (DBG) {
+                Slog.d(LOG_TAG, "Could not determine time: No best phone suggestion."
+                        + " detectionReason=" + detectionReason);
+            }
+            return;
+        }
+
+        final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime();
+        String cause = "Found good suggestion."
+                + ", bestPhoneSuggestion=" + bestPhoneSuggestion
+                + ", detectionReason=" + detectionReason;
+        setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause);
+    }
+
+    @GuardedBy("this")
+    @Nullable
+    private PhoneTimeSuggestion findBestPhoneSuggestion() {
+        long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+
+        // Phone time suggestions are assumed to be derived from NITZ or NITZ-like signals. These
+        // have a number of limitations:
+        // 1) No guarantee of accuracy ("accuracy of the time information is in the order of
+        // minutes") [1]
+        // 2) No guarantee of regular signals ("dependent on the handset crossing radio network
+        // boundaries") [1]
+        //
+        // [1] https://en.wikipedia.org/wiki/NITZ
+        //
+        // Generally, when there are suggestions from multiple phoneIds they should usually
+        // approximately agree. In cases where signals *are* inaccurate we don't want to vacillate
+        // between signals from two phoneIds. However, it is known for NITZ signals to be incorrect
+        // occasionally, which means we also don't want to stick forever with one phoneId. Without
+        // cross-referencing across sources (e.g. the current device time, NTP), or doing some kind
+        // of statistical analysis of consistency within and across phoneIds, we can't know which
+        // suggestions are more correct.
+        //
+        // For simplicity, we try to value recency, then consistency of phoneId.
+        //
+        // The heuristic works as follows:
+        // Recency: The most recent suggestion from each phone is scored. The score is based on a
+        // discrete age bucket, i.e. so signals received around the same time will be in the same
+        // bucket, thus applying a loose reference time ordering. The suggestion with the highest
+        // score is used.
+        // Consistency: If there a multiple suggestions with the same score, the suggestion with the
+        // lowest phoneId is always taken.
+        //
+        // In the trivial case with a single ID this will just mean that the latest received
+        // suggestion is used.
+
+        PhoneTimeSuggestion bestSuggestion = null;
+        int bestScore = PHONE_INVALID_SCORE;
+        for (int i = 0; i < mSuggestionByPhoneId.size(); i++) {
+            Integer phoneId = mSuggestionByPhoneId.keyAt(i);
+            LinkedList<PhoneTimeSuggestion> phoneSuggestions = mSuggestionByPhoneId.valueAt(i);
+            if (phoneSuggestions == null) {
+                // Unexpected - map is missing a value.
+                Slog.w(LOG_TAG, "Suggestions unexpectedly missing for phoneId."
+                        + " phoneId=" + phoneId);
+                continue;
+            }
+
+            PhoneTimeSuggestion candidateSuggestion = phoneSuggestions.getFirst();
+            if (candidateSuggestion == null) {
+                // Unexpected - null suggestions should never be stored.
+                Slog.w(LOG_TAG, "Latest suggestion unexpectedly null for phoneId."
+                        + " phoneId=" + phoneId);
+                continue;
+            } else if (candidateSuggestion.getUtcTime() == null) {
+                // Unexpected - we do not store empty suggestions.
+                Slog.w(LOG_TAG, "Latest suggestion unexpectedly empty. "
+                        + " candidateSuggestion=" + candidateSuggestion);
+                continue;
+            }
+
+            int candidateScore = scorePhoneSuggestion(elapsedRealtimeMillis, candidateSuggestion);
+            if (candidateScore == PHONE_INVALID_SCORE) {
+                // Expected: This means the suggestion is obviously invalid or just too old.
+                continue;
+            }
+
+            // Higher scores are better.
+            if (bestSuggestion == null || bestScore < candidateScore) {
+                bestSuggestion = candidateSuggestion;
+                bestScore = candidateScore;
+            } else if (bestScore == candidateScore) {
+                // Tie! Use the suggestion with the lowest phoneId.
+                int candidatePhoneId = candidateSuggestion.getPhoneId();
+                int bestPhoneId = bestSuggestion.getPhoneId();
+                if (candidatePhoneId < bestPhoneId) {
+                    bestSuggestion = candidateSuggestion;
+                }
+            }
+        }
+        return bestSuggestion;
+    }
+
+    private static int scorePhoneSuggestion(
+            long elapsedRealtimeMillis, @NonNull PhoneTimeSuggestion timeSuggestion) {
+        // The score is based on the age since receipt. Suggestions are bucketed so two
+        // suggestions in the same bucket from different phoneIds are scored the same.
+        TimestampedValue<Long> utcTime = timeSuggestion.getUtcTime();
+        long referenceTimeMillis = utcTime.getReferenceTimeMillis();
+        if (referenceTimeMillis > elapsedRealtimeMillis) {
+            // Future times are ignored. They imply the reference time was wrong, or the elapsed
+            // realtime clock has gone backwards, neither of which are supportable situations.
+            Slog.w(LOG_TAG, "Existing suggestion found to be in the future. "
+                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                    + ", timeSuggestion=" + timeSuggestion);
+            return PHONE_INVALID_SCORE;
+        }
+
+        long ageMillis = elapsedRealtimeMillis - referenceTimeMillis;
+
+        // Any suggestion > MAX_AGE_MILLIS is treated as too old. Although time is relentless and
+        // predictable, the accuracy of the reference time clock may be poor over long periods which
+        // would lead to errors creeping in. Also, in edge cases where a bad suggestion has been
+        // made and never replaced, it could also mean that the time detection code remains
+        // opinionated using a bad invalid suggestion. This caps that edge case at MAX_AGE_MILLIS.
+        if (ageMillis > PHONE_MAX_AGE_MILLIS) {
+            return PHONE_INVALID_SCORE;
+        }
+
+        // Turn the age into a discrete value: 0 <= bucketIndex < MAX_AGE_HOURS.
+        int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS);
+
+        // We want the lowest bucket index to have the highest score. 0 > score >= BUCKET_COUNT.
+        return PHONE_BUCKET_COUNT - bucketIndex;
+    }
+
+    @GuardedBy("this")
+    private void setSystemClockIfRequired(
+            @Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) {
+
+        boolean isOriginAutomatic = isOriginAutomatic(origin);
+        if (isOriginAutomatic) {
+            if (!mCallback.isAutoTimeDetectionEnabled()) {
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time detection is not enabled."
+                            + " origin=" + origin
+                            + ", time=" + time
+                            + ", cause=" + cause);
+                }
+                return;
+            }
+        } else {
+            if (mCallback.isAutoTimeDetectionEnabled()) {
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time detection is enabled."
+                            + " origin=" + origin
+                            + ", time=" + time
+                            + ", cause=" + cause);
+                }
+                return;
+            }
+        }
+
+        mCallback.acquireWakeLock();
+        try {
+            setSystemClockUnderWakeLock(origin, time, cause);
+        } finally {
+            mCallback.releaseWakeLock();
+        }
+    }
+
+    private static boolean isOriginAutomatic(@Origin int origin) {
+        return origin == ORIGIN_PHONE;
+    }
+
+    @GuardedBy("this")
+    private void setSystemClockUnderWakeLock(
+            int origin, @NonNull TimestampedValue<Long> newTime, @NonNull Object cause) {
+
+        long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+        boolean isOriginAutomatic = isOriginAutomatic(origin);
+        long actualSystemClockMillis = mCallback.systemClockMillis();
+        if (isOriginAutomatic) {
+            // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
+            // may be setting the clock.
+            if (mLastAutoSystemClockTimeSet != null) {
+                long expectedTimeMillis = TimeDetectorStrategy.getTimeAt(
+                        mLastAutoSystemClockTimeSet, elapsedRealtimeMillis);
+                long absSystemClockDifference =
+                        Math.abs(expectedTimeMillis - actualSystemClockMillis);
+                if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) {
+                    Slog.w(LOG_TAG,
+                            "System clock has not tracked elapsed real time clock. A clock may"
+                                    + " be inaccurate or something unexpectedly set the system"
+                                    + " clock."
+                                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                                    + " expectedTimeMillis=" + expectedTimeMillis
+                                    + " actualTimeMillis=" + actualSystemClockMillis
+                                    + " cause=" + cause);
+                }
+            }
+        }
+
+        // Adjust for the time that has elapsed since the signal was received.
+        long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis);
+
+        // Check if the new signal would make sufficient difference to the system clock. If it's
+        // below the threshold then ignore it.
+        long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
+        long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
+        if (absTimeDifference < systemClockUpdateThreshold) {
+            if (DBG) {
+                Slog.d(LOG_TAG, "Not setting system clock. New time and"
+                        + " system clock are close enough."
+                        + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                        + " newTime=" + newTime
+                        + " cause=" + cause
+                        + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
+                        + " absTimeDifference=" + absTimeDifference);
+            }
+            return;
+        }
+
+        mCallback.setSystemClock(newSystemClockMillis);
+        String logMsg = "Set system clock using time=" + newTime
+                + " cause=" + cause
+                + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                + " newSystemClockMillis=" + newSystemClockMillis;
+        if (DBG) {
+            Slog.d(LOG_TAG, logMsg);
+        }
+        mTimeChangesLog.log(logMsg);
+
+        // CLOCK_PARANOIA : Record the last time this class set the system clock due to an auto-time
+        // signal, or clear the record it is being done manually.
+        if (isOriginAutomatic(origin)) {
+            mLastAutoSystemClockTimeSet = newTime;
+        } else {
+            mLastAutoSystemClockTimeSet = null;
+        }
+
+        // Historically, Android has sent a TelephonyIntents.ACTION_NETWORK_SET_TIME broadcast only
+        // when setting the time using NITZ.
+        if (origin == ORIGIN_PHONE) {
+            // Send a broadcast that telephony code used to send after setting the clock.
+            // TODO Remove this broadcast as soon as there are no remaining listeners.
+            Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+            intent.putExtra("time", newSystemClockMillis);
+            mCallback.sendStickyBroadcast(intent);
+        }
+    }
+
+    /**
+     * Returns the current best phone suggestion. Not intended for general use: it is used during
+     * tests to check strategy behavior.
+     */
+    @VisibleForTesting
+    @Nullable
+    public synchronized PhoneTimeSuggestion findBestPhoneSuggestionForTests() {
+        return findBestPhoneSuggestion();
+    }
+
+    /**
+     * A method used to inspect state during tests. Not intended for general use.
+     */
+    @VisibleForTesting
+    @Nullable
+    public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int phoneId) {
+        LinkedList<PhoneTimeSuggestion> suggestions = mSuggestionByPhoneId.get(phoneId);
+        if (suggestions == null) {
+            return null;
+        }
+        return suggestions.getFirst();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 84152e8..35b64e7 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -468,7 +468,7 @@
             if (resumedActivity == null || resumedActivity.app == null) {
                 // If previously resumed activity doesn't work either - find the topmost running
                 // activity that can be focused.
-                resumedActivity = focusedStack.topRunningActivityLocked(true /* focusableOnly */);
+                resumedActivity = focusedStack.topRunningActivity(true /* focusableOnly */);
             }
         }
         return resumedActivity;
@@ -832,7 +832,7 @@
         ActivityRecord topRunning = null;
         final ActivityStack focusedStack = getFocusedStack();
         if (focusedStack != null) {
-            topRunning = focusedStack.topRunningActivityLocked();
+            topRunning = focusedStack.topRunningActivity();
         }
 
         // Look in other focusable stacks.
@@ -843,7 +843,7 @@
                 if (stack == focusedStack || !stack.isFocusable()) {
                     continue;
                 }
-                topRunning = stack.topRunningActivityLocked();
+                topRunning = stack.topRunningActivity();
                 if (topRunning != null) {
                     break;
                 }
@@ -961,12 +961,6 @@
         super.onConfigurationChanged(newParentConfig);
     }
 
-    void onLockTaskPackagesUpdated() {
-        for (int i = getStackCount() - 1; i >= 0; --i) {
-            getStackAt(i).onLockTaskPackagesUpdated();
-        }
-    }
-
     /** Checks whether the given activity is in size compatibility mode and notifies the change. */
     void handleActivitySizeCompatModeIfNeeded(ActivityRecord r) {
         if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
@@ -1082,7 +1076,7 @@
         }
 
         final ActivityStack stack = getStackCount() == 1 ? getStackAt(0) : null;
-        if (stack != null && stack.isActivityTypeHome() && stack.getAllTasks().isEmpty()) {
+        if (stack != null && stack.isActivityTypeHome() && !stack.hasChild()) {
             // Release this display if an empty home stack is the only thing left.
             // Since it is the last stack, this display will be released along with the stack
             // removal.
@@ -1318,13 +1312,7 @@
 
     @VisibleForTesting
     void removeAllTasks() {
-        for (int i = getStackCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = getStackAt(i);
-            final ArrayList<Task> tasks = stack.getAllTasks();
-            for (int j = tasks.size() - 1; j >= 0; --j) {
-                stack.removeChild(tasks.get(j), "removeAllTasks");
-            }
-        }
+        mDisplayContent.forAllTasks((t) -> { t.getStack().removeChild(t, "removeAllTasks"); });
     }
 
     public void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 83c854b..bc2fa4e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -547,7 +547,7 @@
     private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
             new WindowState.UpdateReportedVisibilityResults();
 
-    private boolean mUseTransferredAnimation;
+    boolean mUseTransferredAnimation;
 
     /**
      * @see #currentLaunchCanTurnScreenOn()
@@ -1800,11 +1800,6 @@
         } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
             return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
         } else if (taskSwitch && allowTaskSnapshot) {
-            if (mWmService.mLowRamTaskSnapshotsAndRecents) {
-                // For low RAM devices, we use the splash screen starting window instead of the
-                // task snapshot starting window.
-                return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
-            }
             return snapshot == null ? STARTING_WINDOW_TYPE_NONE
                     : snapshotOrientationSameAsTask(snapshot) || fromRecents
                             ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
@@ -2190,7 +2185,7 @@
     }
 
     /**
-     * Sets if this AWT is in the process of closing or entering PIP.
+     * Sets if this {@link ActivityRecord} is in the process of closing or entering PIP.
      * {@link #mWillCloseOrEnterPip}}
      */
     void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
@@ -2198,7 +2193,7 @@
     }
 
     /**
-     * Returns whether this AWT is considered closing. Conditions are either
+     * Returns whether this {@link ActivityRecord} is considered closing. Conditions are either
      * 1. Is this app animating and was requested to be hidden
      * 2. App is delayed closing since it might enter PIP.
      */
@@ -2406,7 +2401,7 @@
             // We are finishing the top focused activity and its stack has nothing to be focused so
             // the next focusable stack should be focused.
             if (mayAdjustTop
-                    && (stack.topRunningActivityLocked() == null || !stack.isFocusable())) {
+                    && (stack.topRunningActivity() == null || !stack.isFocusable())) {
                 if (shouldAdjustGlobalFocus) {
                     // Move the entire hierarchy to top with updating global top resumed activity
                     // and focused application if needed.
@@ -3000,7 +2995,7 @@
 
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Removing app token: %s", this);
 
-        boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
+        commitVisibility(false /* visible */, true /* performLayout */);
 
         getDisplayContent().mOpeningApps.remove(this);
         getDisplayContent().mChangingApps.remove(this);
@@ -3008,6 +3003,8 @@
         mWmService.mTaskSnapshotController.onAppRemoved(this);
         mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
         waitingToShow = false;
+
+        boolean delayed = isAnimating(TRANSITION | CHILDREN);
         if (getDisplayContent().mClosingApps.contains(this)) {
             delayed = true;
         } else if (getDisplayContent().mAppTransition.isTransitionSet()) {
@@ -3152,16 +3149,6 @@
         updateLetterboxSurface(child);
     }
 
-    private boolean waitingForReplacement() {
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState candidate = mChildren.get(i);
-            if (candidate.waitingForReplacement()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void onWindowReplacementTimeout() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             (mChildren.get(i)).onWindowReplacementTimeout();
@@ -3417,7 +3404,7 @@
         // before the non-exiting app tokens. So, we skip the exiting app tokens here.
         // TODO: Investigate if we need to continue to do this or if we can just process them
         // in-order.
-        if (mIsExiting && !waitingForReplacement()) {
+        if (mIsExiting && !forAllWindowsUnchecked(WindowState::waitingForReplacement, true)) {
             return false;
         }
         return forAllWindowsUnchecked(callback, traverseTopToBottom);
@@ -3440,7 +3427,8 @@
     }
 
     @Override
-    ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom) {
+    ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom,
+            WindowContainer boundary) {
         return callback.test(this) ? this : null;
     }
 
@@ -3861,6 +3849,17 @@
         }
     }
 
+    /**
+     * Set visibility on this {@link ActivityRecord}
+     *
+     * <p class="note"><strong>Note: </strong>This function might not update the visibility of
+     * this {@link ActivityRecord} immediately. In case we are preparing an app transition, we
+     * delay changing the visibility of this {@link ActivityRecord} until we execute that
+     * transition.</p>
+     *
+     * @param visible {@code true} if the {@link ActivityRecord} should become visible, otherwise
+     *                this should become invisible.
+     */
     void setVisibility(boolean visible) {
         if (getParent() == null) {
             Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
@@ -3997,153 +3996,169 @@
             return;
         }
 
-        commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction);
+        commitVisibility(visible, true /* performLayout */);
         updateReportedVisibilityLocked();
     }
 
-    boolean commitVisibility(WindowManager.LayoutParams lp,
-            boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
+    @Override
+    boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
+            boolean isVoiceInteraction) {
+        if (mUseTransferredAnimation) {
+            return false;
+        }
+        return super.applyAnimation(lp, transit, enter, isVoiceInteraction);
+    }
 
-        boolean delayed = false;
-        // Reset the state of mVisibleSetFromTransferredStartingWindow since visibility is actually
+    /**
+     * Update visibility to this {@link ActivityRecord}.
+     *
+     * <p class="note"><strong>Note: </strong> Unlike {@link #setVisibility}, this immediately
+     * updates the visibility without starting an app transition. Since this function may start
+     * animation on {@link WindowState} depending on app transition animation status, an app
+     * transition animation must be started before calling this function if necessary.</p>
+     *
+     * @param visible {@code true} if this {@link ActivityRecord} should become visible, otherwise
+     *                this should become invisible.
+     * @param performLayout if {@code true}, perform surface placement after committing visibility.
+     */
+    void commitVisibility(boolean visible, boolean performLayout) {
+        // Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
         // been set by the app now.
         mVisibleSetFromTransferredStartingWindow = false;
-
-        // Allow for state changes and animation to be applied if:
-        // * token is transitioning visibility state
-        // * or the token was marked as hidden and is exiting before we had a chance to play the
-        // transition animation
-        // * or this is an opening app and windows are being replaced
-        // * or the token is the opening app and visible while opening task behind existing one.
-        final DisplayContent displayContent = getDisplayContent();
-        boolean visibilityChanged = false;
-        if (isVisible() != visible || (!isVisible() && mIsExiting)
-                || (visible && waitingForReplacement())
-                || (visible && displayContent.mOpeningApps.contains(this)
-                && displayContent.mAppTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND)) {
-            final AccessibilityController accessibilityController =
-                    mWmService.mAccessibilityController;
-            boolean changed = false;
-            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                    "Changing app %s visible=%b performLayout=%b", this, isVisible(),
-                    performLayout);
-
-            boolean runningAppAnimation = false;
-
-            if (transit != WindowManager.TRANSIT_UNSET) {
-                if (mUseTransferredAnimation) {
-                    runningAppAnimation = isAnimating();
-                } else if (applyAnimation(lp, transit, visible, isVoiceInteraction)) {
-                    runningAppAnimation = true;
-                }
-                delayed = runningAppAnimation;
-                final WindowState window = findMainWindow();
-                if (window != null && accessibilityController != null) {
-                    accessibilityController.onAppWindowTransitionLocked(window, transit);
-                }
-                changed = true;
-            }
-
-            final int windowsCount = mChildren.size();
-            for (int i = 0; i < windowsCount; i++) {
-                final WindowState win = mChildren.get(i);
-                changed |= win.onAppVisibilityChanged(visible, runningAppAnimation);
-            }
-
-            setVisible(visible);
-            mVisibleRequested = visible;
-            visibilityChanged = true;
-            if (!visible) {
-                stopFreezingScreen(true, true);
-            } else {
-                // If we are being set visible, and the starting window is not yet displayed,
-                // then make sure it doesn't get displayed.
-                if (startingWindow != null && !startingWindow.isDrawnLw()) {
-                    startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
-                    startingWindow.mLegacyPolicyVisibilityAfterAnim = false;
-                }
-
-                // We are becoming visible, so better freeze the screen with the windows that are
-                // getting visible so we also wait for them.
-                forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true);
-            }
-
-            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                    "commitVisibility: %s: visible=%b visibleRequested=%b", this,
-                    isVisible(), mVisibleRequested);
-
-            if (changed) {
-                displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
-                if (performLayout) {
-                    mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
-                            false /*updateInputWindows*/);
-                    mWmService.mWindowPlacerLocked.performSurfacePlacement();
-                }
-                displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
-            }
+        if (visible == isVisible()) {
+            return;
         }
+
+        final int windowsCount = mChildren.size();
+        for (int i = 0; i < windowsCount; i++) {
+            mChildren.get(i).onAppVisibilityChanged(visible, isAnimating(PARENTS));
+        }
+        setVisible(visible);
+        mVisibleRequested = visible;
+        if (!visible) {
+            stopFreezingScreen(true, true);
+        } else {
+            // If we are being set visible, and the starting window is not yet displayed,
+            // then make sure it doesn't get displayed.
+            if (startingWindow != null && !startingWindow.isDrawnLw()) {
+                startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+                startingWindow.mLegacyPolicyVisibilityAfterAnim = false;
+            }
+            // We are becoming visible, so better freeze the screen with the windows that are
+            // getting visible so we also wait for them.
+            forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true);
+        }
+        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+                "commitVisibility: %s: visible=%b mVisibleRequested=%b", this,
+                isVisible(), mVisibleRequested);
+        final DisplayContent displayContent = getDisplayContent();
+        displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
+        if (performLayout) {
+            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+                    false /*updateInputWindows*/);
+            mWmService.mWindowPlacerLocked.performSurfacePlacement();
+        }
+        displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
         mUseTransferredAnimation = false;
 
-        delayed = isAnimating(CHILDREN);
+        postApplyAnimation(visible);
+    }
+
+    /**
+     * Post process after applying an app transition animation.
+     *
+     * <p class="note"><strong>Note: </strong> This function must be called after the animations
+     * have been applied and {@link #commitVisibility}.</p>
+     *
+     * @param visible {@code true} if this {@link ActivityRecord} has become visible, otherwise
+     *                this has become invisible.
+     */
+    private void postApplyAnimation(boolean visible) {
+        final boolean delayed = isAnimating(PARENTS | CHILDREN);
         if (!delayed) {
-            // We aren't animating anything, but exiting windows rely on the animation finished
-            // callback being called in case the ActivityRecord was pretending to be animating,
+            // We aren't delayed anything, but exiting windows rely on the animation finished
+            // callback being called in case the ActivityRecord was pretending to be delayed,
             // which we might have done because we were in closing/opening apps list.
             onAnimationFinished();
-        }
-
-        if (visibilityChanged) {
-            if (visible && !delayed) {
+            if (visible) {
                 // The token was made immediately visible, there will be no entrance animation.
                 // We need to inform the client the enter animation was finished.
                 mEnteringAnimation = true;
                 mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
                         token);
             }
+        }
 
-            // If we're becoming visible, immediately change client visibility as well. there seem
-            // to be some edge cases where we change our visibility but client visibility never gets
-            // updated.
-            // If we're becoming invisible, update the client visibility if we are not running an
-            // animation. Otherwise, we'll update client visibility in onAnimationFinished.
-            if (visible || !isAnimating()) {
-                setClientVisible(visible);
-            }
+        // If we're becoming visible, immediately change client visibility as well. there seem
+        // to be some edge cases where we change our visibility but client visibility never gets
+        // updated.
+        // If we're becoming invisible, update the client visibility if we are not running an
+        // animation. Otherwise, we'll update client visibility in onAnimationFinished.
+        if (visible || !isAnimating(PARENTS)) {
+            setClientVisible(visible);
+        }
 
-            if (!displayContent.mClosingApps.contains(this)
-                    && !displayContent.mOpeningApps.contains(this)) {
-                // The token is not closing nor opening, so even if there is an animation set, that
-                // doesn't mean that it goes through the normal app transition cycle so we have
-                // to inform the docked controller about visibility change.
-                // TODO(multi-display): notify docked divider on all displays where visibility was
-                // affected.
-                displayContent.getDockedDividerController().notifyAppVisibilityChanged();
+        final DisplayContent displayContent = getDisplayContent();
+        if (!displayContent.mClosingApps.contains(this)
+                && !displayContent.mOpeningApps.contains(this)) {
+            // The token is not closing nor opening, so even if there is an animation set, that
+            // doesn't mean that it goes through the normal app transition cycle so we have
+            // to inform the docked controller about visibility change.
+            // TODO(multi-display): notify docked divider on all displays where visibility was
+            // affected.
+            displayContent.getDockedDividerController().notifyAppVisibilityChanged();
 
-                // Take the screenshot before possibly hiding the WSA, otherwise the screenshot
-                // will not be taken.
-                mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
-            }
+            // Take the screenshot before possibly hiding the WSA, otherwise the screenshot
+            // will not be taken.
+            mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
+        }
 
-            // If we are hidden but there is no delay needed we immediately
-            // apply the Surface transaction so that the ActivityManager
-            // can have some guarantee on the Surface state following
-            // setting the visibility. This captures cases like dismissing
-            // the docked or pinned stack where there is no app transition.
-            //
-            // In the case of a "Null" animation, there will be
-            // no animation but there will still be a transition set.
-            // We still need to delay hiding the surface such that it
-            // can be synchronized with showing the next surface in the transition.
-            if (!isVisible() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
-                SurfaceControl.openTransaction();
-                for (int i = mChildren.size() - 1; i >= 0; i--) {
-                    mChildren.get(i).mWinAnimator.hide("immediately hidden");
-                }
+        // If we are hidden but there is no delay needed we immediately
+        // apply the Surface transaction so that the ActivityManager
+        // can have some guarantee on the Surface state following
+        // setting the visibility. This captures cases like dismissing
+        // the docked or pinned stack where there is no app transition.
+        //
+        // In the case of a "Null" animation, there will be
+        // no animation but there will still be a transition set.
+        // We still need to delay hiding the surface such that it
+        // can be synchronized with showing the next surface in the transition.
+        if (!isVisible() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
+            SurfaceControl.openTransaction();
+            try {
+                forAllWindows(win -> {
+                    win.mWinAnimator.hide("immediately hidden"); }, true);
+            } finally {
                 SurfaceControl.closeTransaction();
             }
         }
+    }
 
-        return delayed;
+    /**
+     * Check if visibility of this {@link ActivityRecord} should be updated as part of an app
+     * transition.
+     *
+     * <p class="note><strong>Note:</strong> If the visibility of this {@link ActivityRecord} is
+     * already set to {@link #visible}, we don't need to update the visibility. So {@code false} is
+     * returned.</p>
+     *
+     * @param visible {@code true} if this {@link ActivityRecord} should become visible,
+     *                {@code false} if this should become invisible.
+     * @return {@code true} if visibility of this {@link ActivityRecord} should be updated, and
+     *         an app transition animation should be run.
+     */
+    boolean shouldApplyAnimation(boolean visible) {
+        // Allow for state update and animation to be applied if:
+        // * token is transitioning visibility state
+        // * or the token was marked as hidden and is exiting before we had a chance to play the
+        // transition animation
+        // * or this is an opening app and windows are being replaced
+        // * or the token is the opening app and visible while opening task behind existing one.
+        final DisplayContent displayContent = getDisplayContent();
+        return isVisible() != visible || (!isVisible() && mIsExiting)
+                || (visible && forAllWindows(WindowState::waitingForReplacement, true))
+                || (visible && displayContent.mOpeningApps.contains(this)
+                && displayContent.mAppTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND);
     }
 
     /**
@@ -5634,19 +5649,6 @@
         return task != null ? task.isChangingAppTransition() : super.isChangingAppTransition();
     }
 
-    @Override
-    boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
-            boolean isVoiceInteraction) {
-        if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
-            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
-                    "applyAnimation: transition animation is disabled or skipped. "
-                            + "container=%s", this);
-            cancelAnimation();
-            return false;
-        }
-        return super.applyAnimation(lp, transit, enter, isVoiceInteraction);
-    }
-
     /**
      * Creates a layer to apply crop to an animation.
      */
@@ -5913,14 +5915,14 @@
     protected void onAnimationFinished() {
         super.onAnimationFinished();
 
-        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#onAnimationFinished");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AR#onAnimationFinished");
         mTransit = TRANSIT_UNSET;
         mTransitFlags = 0;
         mNeedsZBoost = false;
         mNeedsAnimationBoundsLayer = false;
 
         setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
-                "AppWindowToken");
+                "ActivityRecord");
 
         clearThumbnail();
         setClientVisible(isVisible() || mVisibleRequested);
@@ -6138,10 +6140,9 @@
         if (mCompatDisplayInsets == null || !shouldUseSizeCompatMode()) {
             return false;
         }
-        final Configuration resolvedConfig = getResolvedOverrideConfiguration();
-        final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
-        if (resolvedAppBounds == null) {
-            // The override configuration has not been resolved yet.
+        final Rect appBounds = getConfiguration().windowConfiguration.getAppBounds();
+        if (appBounds == null) {
+            // The app bounds hasn't been computed yet.
             return false;
         }
 
@@ -6149,13 +6150,13 @@
         // Although colorMode, screenLayout, smallestScreenWidthDp are also fixed, generally these
         // fields should be changed with density and bounds, so here only compares the most
         // significant field.
-        if (parentConfig.densityDpi != resolvedConfig.densityDpi) {
+        if (parentConfig.densityDpi != getConfiguration().densityDpi) {
             return true;
         }
 
         final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
-        final int appWidth = resolvedAppBounds.width();
-        final int appHeight = resolvedAppBounds.height();
+        final int appWidth = appBounds.width();
+        final int appHeight = appBounds.height();
         final int parentAppWidth = parentAppBounds.width();
         final int parentAppHeight = parentAppBounds.height();
         if (parentAppWidth == appWidth && parentAppHeight == appHeight) {
@@ -6174,7 +6175,8 @@
         // The rest of the condition is that only one side is smaller than the parent, but it still
         // needs to exclude the cases where the size is limited by the fixed aspect ratio.
         if (info.maxAspectRatio > 0) {
-            final float aspectRatio = Math.max(appWidth, appHeight) / Math.min(appWidth, appHeight);
+            final float aspectRatio =
+                    (float) Math.max(appWidth, appHeight) / Math.min(appWidth, appHeight);
             if (aspectRatio >= info.maxAspectRatio) {
                 // The current size has reached the max aspect ratio.
                 return false;
@@ -6526,7 +6528,7 @@
         } else if (mCompatDisplayInsets != null) {
             // The override changes can only be obtained from display, because we don't have the
             // difference of full configuration in each hierarchy.
-            final int displayChanges = display.getLastOverrideConfigurationChanges();
+            final int displayChanges = display.getCurrentOverrideConfigurationChanges();
             final int orientationChanges = CONFIG_WINDOW_CONFIGURATION
                     | CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION;
             final boolean hasNonOrienSizeChanged = hasResizeChange(displayChanges)
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index cc45671..eb1f638 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
@@ -191,7 +192,7 @@
 /**
  * State and management of a single stack of activities.
  */
-class ActivityStack extends WindowContainer<Task> implements BoundsAnimationTarget {
+class ActivityStack extends WindowContainer<WindowContainer> implements BoundsAnimationTarget {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
     static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_APP = TAG + POSTFIX_APP;
@@ -583,7 +584,7 @@
                 return true;
             }
 
-            final ActivityRecord topActivity = topRunningActivityLocked();
+            final ActivityRecord topActivity = topRunningActivity();
             final PooledFunction f = PooledLambda.obtainFunction(
                     CheckBehindFullscreenActivityHelper::processActivity, this,
                     PooledLambda.__(ActivityRecord.class), topActivity);
@@ -870,7 +871,7 @@
                 final boolean isMinimizedDock =
                         display.mDisplayContent.getDockedDividerController().isMinimizedDock();
                 if (isMinimizedDock) {
-                    Task topTask = display.getSplitScreenPrimaryStack().topTask();
+                    Task topTask = display.getSplitScreenPrimaryStack().getTopMostTask();
                     if (topTask != null) {
                         dockedBounds = topTask.getBounds();
                     }
@@ -946,7 +947,7 @@
         final int currentMode = getWindowingMode();
         final int currentOverrideMode = getRequestedOverrideWindowingMode();
         final ActivityDisplay display = getDisplay();
-        final Task topTask = topTask();
+        final Task topTask = getTopMostTask();
         final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
         int windowingMode = preferredWindowingMode;
         if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
@@ -1174,11 +1175,11 @@
         return false;
     }
 
-    ActivityRecord topRunningActivityLocked() {
-        return topRunningActivityLocked(false /* focusableOnly */);
+    ActivityRecord topRunningActivity() {
+        return topRunningActivity(false /* focusableOnly */);
     }
 
-    ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
+    ActivityRecord topRunningActivity(boolean focusableOnly) {
         // Split into 2 to avoid object creation due to variable capture.
         if (focusableOnly) {
             return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
@@ -1212,7 +1213,7 @@
      *
      * @return Returns the HistoryRecord of the next activity on the stack.
      */
-    final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
+    ActivityRecord topRunningActivity(IBinder token, int taskId) {
         final PooledPredicate p = PooledLambda.obtainPredicate(ActivityStack::isTopRunning,
                 PooledLambda.__(ActivityRecord.class), taskId, token);
         final ActivityRecord r = getActivity(p);
@@ -1221,31 +1222,13 @@
     }
 
     private static boolean isTopRunning(ActivityRecord r, int taskId, IBinder notTop) {
-        return r.getTask().mTaskId == taskId && r.appToken != notTop && r.canBeTopRunning();
+        return r.getTask().mTaskId != taskId && r.appToken != notTop && r.canBeTopRunning();
     }
 
     ActivityRecord getTopNonFinishingActivity() {
         return getTopActivity(false /*includeFinishing*/, true /*includeOverlays*/);
     }
 
-    final Task topTask() {
-        final int size = getChildCount();
-        if (size > 0) {
-            return getChildAt(size - 1);
-        }
-        return null;
-    }
-
-    Task taskForIdLocked(int id) {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            if (task.mTaskId == id) {
-                return task;
-            }
-        }
-        return null;
-    }
-
     ActivityRecord isInStackLocked(IBinder token) {
         final ActivityRecord r = ActivityRecord.forTokenLocked(token);
         return isInStackLocked(r);
@@ -1292,9 +1275,7 @@
     }
 
     private boolean returnsToHomeStack() {
-        return !inMultiWindowMode()
-                && hasChild()
-                && getChildAt(0).returnsToHomeStack();
+        return !inMultiWindowMode() && hasChild() && getBottomMostTask().returnsToHomeStack();
     }
 
     void moveToFront(String reason) {
@@ -1367,7 +1348,7 @@
     }
 
     boolean isFocusable() {
-        final ActivityRecord r = topRunningActivityLocked();
+        final ActivityRecord r = topRunningActivity();
         return mRootActivityContainer.isFocusable(this, r != null && r.isFocusable());
     }
 
@@ -1390,15 +1371,12 @@
         mCurrentUser = userId;
 
         super.switchUser(userId);
-        int top = mChildren.size();
-        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
-            Task task = mChildren.get(taskNdx);
-            if (mWmService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
-                mChildren.remove(taskNdx);
-                mChildren.add(task);
-                --top;
+        forAllTasks((t) -> {
+            if (t.mWmService.isCurrentProfileLocked(t.mUserId) || t.showForAllUsers()) {
+                mChildren.remove(t);
+                mChildren.add(t);
             }
-        }
+        });
     }
 
     void minimalResumeActivityLocked(ActivityRecord r) {
@@ -1719,7 +1697,7 @@
                 mRootActivityContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
             } else {
                 checkReadyForSleep();
-                ActivityRecord top = topStack.topRunningActivityLocked();
+                ActivityRecord top = topStack.topRunningActivity();
                 if (top == null || (prev != null && top != prev)) {
                     // If there are no more activities available to run, do resume anyway to start
                     // something. Also if the top activity on the stack is not the just paused
@@ -1849,7 +1827,7 @@
         final boolean isAssistantType = isActivityTypeAssistant();
         for (int i = display.getStackCount() - 1; i >= 0; --i) {
             final ActivityStack other = display.getStackAt(i);
-            final boolean hasRunningActivities = other.topRunningActivityLocked() != null;
+            final boolean hasRunningActivities = other.topRunningActivity() != null;
             if (other == this) {
                 // Should be visible if there is no other stack occluding it, unless it doesn't
                 // have any running activities, not starting one and not home stack.
@@ -2001,7 +1979,7 @@
 
     @Override
     public boolean supportsSplitScreenWindowingMode() {
-        final Task topTask = topTask();
+        final Task topTask = getTopMostTask();
         return super.supportsSplitScreenWindowingMode()
                 && (topTask == null || topTask.supportsSplitScreenWindowingMode());
     }
@@ -2185,7 +2163,7 @@
             // to ensure any necessary pause logic occurs. In the case where the Activity will be
             // shown regardless of the lock screen, the call to
             // {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped.
-            final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
+            final ActivityRecord next = topRunningActivity(true /* focusableOnly */);
             if (next == null || !next.canTurnScreenOn()) {
                 checkReadyForSleep();
             }
@@ -2224,7 +2202,7 @@
         // Find the next top-most activity to resume in this stack that is not finishing and is
         // focusable. If it is not focusable, we will fall into the case below to resume the
         // top activity in the next focusable task.
-        ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
+        ActivityRecord next = topRunningActivity(true /* focusableOnly */);
 
         final boolean hasRunningActivity = next != null;
 
@@ -2535,7 +2513,7 @@
                 // We should be all done, but let's just make sure our activity
                 // is still at the top and schedule another run if something
                 // weird happened.
-                ActivityRecord nextNext = topRunningActivityLocked();
+                ActivityRecord nextNext = topRunningActivity();
                 if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
                         "Activity config changed during resume: " + next
                                 + ", new next: " + nextNext);
@@ -2672,41 +2650,28 @@
     void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
             boolean newTask, boolean keepCurTransition, ActivityOptions options) {
         Task rTask = r.getTask();
-        final int taskId = rTask.mTaskId;
         final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
+        final boolean hasTask = hasChild(rTask);
         // mLaunchTaskBehind tasks get placed at the back of the task stack.
-        if (!r.mLaunchTaskBehind && allowMoveToFront
-                && (taskForIdLocked(taskId) == null || newTask)) {
+        if (!r.mLaunchTaskBehind && allowMoveToFront && (!hasTask || newTask)) {
             // Last activity in task had been removed or ActivityManagerService is reusing task.
             // Insert or replace.
             // Might not even be in.
             positionChildAtTop(rTask);
         }
         Task task = null;
-        if (!newTask) {
-            // If starting in an existing task, find where that is...
-            boolean isOccluded = false;
-            for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-                task = getChildAt(taskNdx);
-                if (task.getTopNonFinishingActivity() == null) {
-                    // All activities in task are finishing.
-                    continue;
-                }
-                if (task == rTask) {
-                    // Here it is!  Now, if this is not yet visible (occluded by another task) to
-                    // the user, then just add it without starting; it will get started when the
-                    // user navigates back to it.
-                    if (isOccluded) {
-                        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
-                                + task, new RuntimeException("here").fillInStackTrace());
-                        rTask.positionChildAtTop(r);
-                        ActivityOptions.abort(options);
-                        return;
-                    }
-                    break;
-                } else if (!isOccluded) {
-                    isOccluded = task.getActivity(ActivityRecord::occludesParent) != null;
-                }
+        if (!newTask && hasTask) {
+            final ActivityRecord occludingActivity = getActivity(
+                    (ar) -> !ar.finishing && ar.occludesParent(), true, rTask);
+            if (occludingActivity != null) {
+                // Here it is!  Now, if this is not yet visible (occluded by another task) to the
+                // user, then just add it without starting; it will get started when the user
+                // navigates back to it.
+                if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task " + task,
+                        new RuntimeException("here").fillInStackTrace());
+                rTask.positionChildAtTop(r);
+                ActivityOptions.abort(options);
+                return;
             }
         }
 
@@ -2892,7 +2857,7 @@
             return null;
         }
 
-        final ActivityRecord top = stack.topRunningActivityLocked();
+        final ActivityRecord top = stack.topRunningActivity();
 
         if (stack.isActivityTypeHome() && (top == null || !top.mVisibleRequested)) {
             // If we will be focusing on the home stack next and its current top activity isn't
@@ -2922,7 +2887,7 @@
      *         not belong to the crashed app.
      */
     final Task finishTopCrashedActivityLocked(WindowProcessController app, String reason) {
-        ActivityRecord r = topRunningActivityLocked();
+        final ActivityRecord r = topRunningActivity();
         if (r == null || r.app != app) {
             return null;
         }
@@ -3033,12 +2998,11 @@
                 return true;
             }
             // We now need to get the task below it to determine what to do.
-            int taskIdx = mChildren.indexOf(task);
-            if (taskIdx <= 0) {
+            final Task prevTask = getTaskBelow(task);
+            if (prevTask == null) {
                 Slog.w(TAG, "shouldUpRecreateTask: task not in history for " + srec);
                 return false;
             }
-            final Task prevTask = getChildAt(taskIdx);
             if (!task.affinity.equals(prevTask.affinity)) {
                 // These are different apps, so need to recreate.
                 return true;
@@ -3073,7 +3037,7 @@
         // We should consolidate.
         IActivityController controller = mService.mController;
         if (controller != null) {
-            ActivityRecord next = topRunningActivityLocked(srec.appToken, 0);
+            ActivityRecord next = topRunningActivity(srec.appToken, INVALID_TASK_ID);
             if (next != null) {
                 // ask watcher if this is allowed
                 boolean resumeOK = true;
@@ -3287,7 +3251,7 @@
 
     private void updateTransitLocked(int transit, ActivityOptions options) {
         if (options != null) {
-            ActivityRecord r = topRunningActivityLocked();
+            ActivityRecord r = topRunningActivity();
             if (r != null && !r.isState(RESUMED)) {
                 r.updateOptionsLocked(options);
             } else {
@@ -3344,7 +3308,7 @@
             }
 
             // Set focus to the top running activity of this stack.
-            final ActivityRecord r = topRunningActivityLocked();
+            final ActivityRecord r = topRunningActivity();
             if (r != null) {
                 r.moveFocusableActivityToTop(reason);
             }
@@ -3386,15 +3350,10 @@
      * If a watcher is installed, the action is preflighted and the watcher has an opportunity
      * to premeptively cancel the move.
      *
-     * @param taskId The taskId to collect and move to the bottom.
+     * @param tr The task to collect and move to the bottom.
      * @return Returns true if the move completed, false if not.
      */
-    final boolean moveTaskToBackLocked(int taskId) {
-        final Task tr = taskForIdLocked(taskId);
-        if (tr == null) {
-            Slog.i(TAG, "moveTaskToBack: bad taskId=" + taskId);
-            return false;
-        }
+    boolean moveTaskToBack(Task tr) {
         Slog.i(TAG, "moveTaskToBack: " + tr);
 
         // In LockTask mode, moving a locked task to the back of the stack may expose unlocked
@@ -3407,9 +3366,9 @@
         // for *other* available tasks, but if none are available, then try again allowing the
         // current task to be selected.
         if (isTopStackOnDisplay() && mService.mController != null) {
-            ActivityRecord next = topRunningActivityLocked(null, taskId);
+            ActivityRecord next = topRunningActivity(null, tr.mTaskId);
             if (next == null) {
-                next = topRunningActivityLocked(null, 0);
+                next = topRunningActivity(null, INVALID_TASK_ID);
             }
             if (next != null) {
                 // ask watcher if this is allowed
@@ -3426,7 +3385,8 @@
             }
         }
 
-        if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task=" + taskId);
+        if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task="
+                + tr.mTaskId);
 
         getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
         moveToBack("moveTaskToBackLocked", tr);
@@ -3471,24 +3431,16 @@
         try {
             // Update override configurations of all tasks in the stack.
             final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
-            for (int i = getChildCount() - 1; i >= 0; i--) {
-                final Task task = getChildAt(i);
-                if (task.isResizeable()) {
-                    if (tempTaskInsetBounds != null && !tempTaskInsetBounds.isEmpty()) {
-                        task.setOverrideDisplayedBounds(taskBounds);
-                        task.setBounds(tempTaskInsetBounds);
-                    } else {
-                        task.setOverrideDisplayedBounds(null);
-                        task.setBounds(taskBounds);
-                    }
-                }
-            }
+            final PooledConsumer c = PooledLambda.obtainConsumer(
+                    ActivityStack::processTaskResizeBounds, PooledLambda.__(Task.class),
+                    taskBounds, tempTaskInsetBounds);
+            forAllTasks(c);
+            c.recycle();
 
             setBounds(bounds);
 
             if (!deferResume) {
-                ensureVisibleActivitiesConfiguration(
-                        topRunningActivityLocked(), preserveWindows);
+                ensureVisibleActivitiesConfiguration(topRunningActivity(), preserveWindows);
             }
         } finally {
             mService.continueWindowLayout();
@@ -3496,39 +3448,51 @@
         }
     }
 
+    private static void processTaskResizeBounds(Task task, Rect bounds, Rect insetBounds) {
+        if (!task.isResizeable()) return;
+
+        if (insetBounds != null && !insetBounds.isEmpty()) {
+            task.setOverrideDisplayedBounds(bounds);
+            task.setBounds(insetBounds);
+        } else {
+            task.setOverrideDisplayedBounds(null);
+            task.setBounds(bounds);
+        }
+    }
+
     /**
      * Until we can break this "set task bounds to same as stack bounds" behavior, this
      * basically resizes both stack and task bounds to the same bounds.
      */
-    void setTaskBounds(Rect bounds) {
+   private void setTaskBounds(Rect bounds) {
         if (!updateBoundsAllowed(bounds)) {
             return;
         }
 
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            final Task task = getChildAt(i);
-            if (task.isResizeable()) {
-                task.setBounds(bounds);
-            } else {
-                task.setBounds(null);
-            }
-        }
+        final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskBounds,
+                PooledLambda.__(Task.class), bounds);
+        forAllTasks(c);
+        c.recycle();
+    }
+
+    private static void setTaskBounds(Task task, Rect bounds) {
+        task.setBounds(task.isResizeable() ? bounds : null);
     }
 
     /** Helper to setDisplayedBounds on all child tasks */
-    void setTaskDisplayedBounds(Rect bounds) {
+    private void setTaskDisplayedBounds(Rect bounds) {
         if (!updateDisplayedBoundsAllowed(bounds)) {
             return;
         }
 
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            final Task task = getChildAt(i);
-            if (bounds == null || bounds.isEmpty()) {
-                task.setOverrideDisplayedBounds(null);
-            } else if (task.isResizeable()) {
-                task.setOverrideDisplayedBounds(bounds);
-            }
-        }
+        final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskDisplayedBounds,
+                PooledLambda.__(Task.class), bounds);
+        forAllTasks(c);
+        c.recycle();
+    }
+
+    private static void setTaskDisplayedBounds(Task task, Rect bounds) {
+        task.setOverrideDisplayedBounds(bounds == null || bounds.isEmpty() ? null : bounds);
     }
 
     boolean willActivityBeVisible(IBinder token) {
@@ -3548,56 +3512,6 @@
         return !r.finishing;
     }
 
-    /**
-     * @return The set of running tasks through {@param tasksOut} that are available to the caller.
-     *         If {@param ignoreActivityType} or {@param ignoreWindowingMode} are not undefined,
-     *         then skip running tasks that match those types.
-     */
-    void getRunningTasks(List<Task> tasksOut, @ActivityType int ignoreActivityType,
-            @WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed,
-            boolean crossUser, ArraySet<Integer> profileIds) {
-        boolean focusedStack = mRootActivityContainer.getTopDisplayFocusedStack() == this;
-        boolean topTask = true;
-        int userId = UserHandle.getUserId(callingUid);
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            if (task.getTopNonFinishingActivity() == null) {
-                // Skip if there are no activities in the task
-                continue;
-            }
-            if (task.effectiveUid != callingUid) {
-                if (task.mUserId != userId && !crossUser && !profileIds.contains(task.mUserId)) {
-                    // Skip if the caller does not have cross user permission or cannot access
-                    // the task's profile
-                    continue;
-                }
-                if (!allowed && !task.isActivityTypeHome()) {
-                    // Skip if the caller isn't allowed to fetch this task, except for the home
-                    // task which we always return.
-                    continue;
-                }
-            }
-            if (ignoreActivityType != ACTIVITY_TYPE_UNDEFINED
-                    && task.getActivityType() == ignoreActivityType) {
-                // Skip ignored activity type
-                continue;
-            }
-            if (ignoreWindowingMode != WINDOWING_MODE_UNDEFINED
-                    && task.getWindowingMode() == ignoreWindowingMode) {
-                // Skip ignored windowing mode
-                continue;
-            }
-            if (focusedStack && topTask) {
-                // For the focused stack top task, update the last stack active time so that it can
-                // be used to determine the order of the tasks (it may not be set for newly created
-                // tasks)
-                task.touchActiveTime();
-                topTask = false;
-            }
-            tasksOut.add(task);
-        }
-    }
-
     void unhandledBackLocked() {
         final ActivityRecord topActivity = getTopMostActivity();
         if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
@@ -3717,7 +3631,7 @@
     }
 
     ActivityRecord restartPackage(String packageName) {
-        ActivityRecord starting = topRunningActivityLocked();
+        ActivityRecord starting = topRunningActivity();
 
         // All activities that came from the package must be
         // restarted as if there was a config change.
@@ -3745,7 +3659,7 @@
      * @param child to remove.
      * @param reason for removal.
      */
-    void removeChild(Task child, String reason) {
+    void removeChild(WindowContainer child, String reason) {
         if (!mChildren.contains(child)) {
             // Not really in this stack anymore...
             return;
@@ -3757,7 +3671,7 @@
 
         super.removeChild(child);
 
-        EventLogTags.writeWmRemoveTask(child.mTaskId, mStackId);
+        EventLogTags.writeWmRemoveTask(((Task) child).mTaskId, mStackId);
 
         if (display.isSingleTaskInstance()) {
             mService.notifySingleTaskDisplayEmpty(display.mDisplayId);
@@ -3767,15 +3681,15 @@
 
         if (!hasChild()) {
             // Stack is now empty...
-            removeIfPossible();
+          removeIfPossible();
         }
 
         moveHomeStackToFrontIfNeeded(topFocused, display, reason);
     }
 
     @Override
-    void removeChild(Task task) {
-        removeChild(task, "removeChild");
+    void removeChild(WindowContainer child) {
+        removeChild(child, "removeChild");
     }
 
     void moveHomeStackToFrontIfNeeded(
@@ -3816,10 +3730,6 @@
         return task;
     }
 
-    ArrayList<Task> getAllTasks() {
-        return new ArrayList<>(mChildren);
-    }
-
     void addChild(final Task task, final boolean toTop, boolean showForAllUsers) {
         if (isSingleTaskInstance() && hasChild()) {
             throw new IllegalStateException("Can only have one child on stack=" + this);
@@ -4015,7 +3925,7 @@
         }
 
         mWindowManager.inSurfaceTransaction(() -> {
-            final Task task = mChildren.get(0);
+            final Task task = getBottomMostTask();
             setWindowingMode(WINDOWING_MODE_UNDEFINED);
 
             getDisplay().positionStackAtTop(this, false /* includingParents */);
@@ -4026,7 +3936,7 @@
         });
     }
 
-    void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+    private void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
             boolean forceUpdate) {
         // It is guaranteed that the activities requiring the update will be in the pinned stack at
         // this point (either reparented before the animation into PiP, or before reparenting after
@@ -4034,29 +3944,19 @@
         if (!isAttached()) {
             return;
         }
-        ArrayList<Task> tasks = getAllTasks();
-        for (int i = 0; i < tasks.size(); i++) {
-            mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
-                    forceUpdate);
-        }
+        final PooledConsumer c = PooledLambda.obtainConsumer(
+                ActivityStackSupervisor::updatePictureInPictureMode, mStackSupervisor,
+                PooledLambda.__(Task.class), targetStackBounds, forceUpdate);
+        forAllTasks(c);
+        c.recycle();
     }
 
     public int getStackId() {
         return mStackId;
     }
 
-    Task findHomeTask() {
-        if (!isActivityTypeHome() || mChildren.isEmpty()) {
-            return null;
-        }
-        return mChildren.get(mChildren.size() - 1);
-    }
-
     void prepareFreezingTaskBounds() {
-        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = mChildren.get(taskNdx);
-            task.prepareFreezingBounds();
-        }
+        forAllTasks(Task::prepareFreezingBounds);
     }
 
     /**
@@ -4082,26 +3982,19 @@
                 insetBounds = mFullyAdjustedImeBounds;
             }
         }
-        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : getRawBounds(), insetBounds);
+
+        if (!matchParentBounds()) {
+            final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP;
+            final PooledConsumer c = PooledLambda.obtainConsumer(Task::alignToAdjustedBounds,
+                    PooledLambda.__(Task.class), adjusted ? mAdjustedBounds : getRawBounds(),
+                    insetBounds, alignBottom);
+            c.recycle();
+        }
+
         mDisplayContent.setLayoutNeeded();
-
         updateSurfaceBounds();
     }
 
-    private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
-        if (matchParentBounds()) {
-            return;
-        }
-
-        final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP;
-
-        // Update bounds of containing tasks.
-        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = mChildren.get(taskNdx);
-            task.alignToAdjustedBounds(adjustedBounds, tempInsetBounds, alignBottom);
-        }
-    }
-
     @Override
     public int setBounds(Rect bounds) {
         return setBounds(getRequestedOverrideBounds(), bounds);
@@ -4366,17 +4259,17 @@
      * @param position Target position to add the task to.
      * @param showForAllUsers Whether to show the task regardless of the current user.
      */
-    void addChild(Task task, int position, boolean showForAllUsers, boolean moveParents) {
+    private void addChild(Task task, int position, boolean showForAllUsers, boolean moveParents) {
         // Add child task.
         addChild(task, null);
 
         // Move child to a proper position, as some restriction for position might apply.
-        position = positionChildAt(
-                position, task, moveParents /* includingParents */, showForAllUsers);
+        positionChildAt(position, task, moveParents /* includingParents */, showForAllUsers);
     }
 
     @Override
-    void addChild(Task task, int position) {
+    void addChild(WindowContainer child, int position) {
+        final Task task = (Task) child;
         addChild(task, position, task.showForAllUsers(), false /* includingParents */);
     }
 
@@ -4415,14 +4308,15 @@
     }
 
     @Override
-    void positionChildAt(int position, Task child, boolean includingParents) {
-        positionChildAt(position, child, includingParents, child.showForAllUsers());
+    void positionChildAt(int position, WindowContainer child, boolean includingParents) {
+        final Task task = (Task) child;
+        positionChildAt(position, task, includingParents, task.showForAllUsers());
     }
 
     /**
-     * Overridden version of {@link ActivityStack#positionChildAt(int, Task, boolean)}. Used in
-     * {@link ActivityStack#addChild(Task, int, boolean showForAllUsers, boolean)}, as it can
-     * receive showForAllUsers param from {@link ActivityRecord} instead of
+     * Overridden version of {@link ActivityStack#positionChildAt(int, WindowContainer, boolean)}.
+     * Used in {@link ActivityStack#addChild(Task, int, boolean showForAllUsers, boolean)}, as it
+     * can receive showForAllUsers param from {@link ActivityRecord} instead of
      * {@link Task#showForAllUsers()}.
      */
     private int positionChildAt(int position, Task child, boolean includingParents,
@@ -4538,9 +4432,10 @@
      *                  We will start adjusting up from here.
      *  @param size The size of the current task list.
      */
+    // TODO(task-hierarchy): Move user to their own window container.
     private int computeMinPosition(int minPosition, int size) {
         while (minPosition < size) {
-            final Task tmpTask = mChildren.get(minPosition);
+            final Task tmpTask = (Task) mChildren.get(minPosition);
             final boolean canShowTmpTask =
                     tmpTask.showForAllUsers()
                             || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
@@ -4557,9 +4452,10 @@
      *  @param maxPosition The maximum position the caller is suggesting.
      *                  We will start adjusting down from here.
      */
+    // TODO(task-hierarchy): Move user to their own window container.
     private int computeMaxPosition(int maxPosition) {
         while (maxPosition > 0) {
-            final Task tmpTask = mChildren.get(maxPosition);
+            final Task tmpTask = (Task) mChildren.get(maxPosition);
             final boolean canShowTmpTask =
                     tmpTask.showForAllUsers()
                             || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
@@ -4664,7 +4560,7 @@
 
         // When the home stack is resizable, should always have the same stack and task bounds
         if (isActivityTypeHome()) {
-            final Task homeTask = findHomeTask();
+            final Task homeTask = getTopMostTask();
             if (homeTask == null || homeTask.isResizeable()) {
                 // Calculate the home stack bounds when in docked mode and the home stack is
                 // resizeable.
@@ -4894,25 +4790,20 @@
      * to the list of to be drawn windows the service is waiting for.
      */
     void beginImeAdjustAnimation() {
-        for (int j = mChildren.size() - 1; j >= 0; j--) {
-            final Task task = mChildren.get(j);
-            if (task.hasContentToDisplay()) {
-                task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
-                task.setWaitingForDrawnIfResizingChanged();
+        forAllTasks((t) -> {
+            if (t.hasContentToDisplay()) {
+                t.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+                t.setWaitingForDrawnIfResizingChanged();
             }
-        }
+        });
     }
 
-    /**
-     * Resets the resizing state of all windows.
-     */
+    /** Resets the resizing state of all windows. */
     void endImeAdjustAnimation() {
-        for (int j = mChildren.size() - 1; j >= 0; j--) {
-            mChildren.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
-        }
+        forAllTasks((t) -> { t.setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER); });
     }
 
-    int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
+    private int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
         return displayContentRect.top + (int)
                 ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN);
     }
@@ -5024,7 +4915,7 @@
         return true;
     }
 
-    private boolean isMinimizedDockAndHomeStackResizable() {
+    boolean isMinimizedDockAndHomeStackResizable() {
         return mDisplayContent.mDividerControllerLocked.isMinimizedDock()
                 && mDisplayContent.mDividerControllerLocked.isHomeStackResizable();
     }
@@ -5092,13 +4983,7 @@
      *         recents animation); {@code false} otherwise.
      */
     boolean isTaskAnimating() {
-        for (int j = mChildren.size() - 1; j >= 0; j--) {
-            final Task task = mChildren.get(j);
-            if (task.isTaskAnimating()) {
-                return true;
-            }
-        }
-        return false;
+        return getTask(Task::isTaskAnimating) != null;
     }
 
     @Override
@@ -5173,102 +5058,11 @@
     }
 
     boolean hasTaskForUser(int userId) {
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final Task task = mChildren.get(i);
-            if (task.mUserId == userId) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    void findTaskForResizePoint(int x, int y, int delta,
-            DisplayContent.TaskForResizePointSearchResult results) {
-        if (!getWindowConfiguration().canResizeTask()) {
-            results.searchDone = true;
-            return;
-        }
-
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final Task task = mChildren.get(i);
-            if (task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
-                results.searchDone = true;
-                return;
-            }
-
-            // We need to use the task's dim bounds (which is derived from the visible bounds of
-            // its apps windows) for any touch-related tests. Can't use the task's original
-            // bounds because it might be adjusted to fit the content frame. One example is when
-            // the task is put to top-left quadrant, the actual visible area would not start at
-            // (0,0) after it's adjusted for the status bar.
-            task.getDimBounds(mTmpRect);
-            mTmpRect.inset(-delta, -delta);
-            if (mTmpRect.contains(x, y)) {
-                mTmpRect.inset(delta, delta);
-
-                results.searchDone = true;
-
-                if (!mTmpRect.contains(x, y)) {
-                    results.taskForResize = task;
-                    return;
-                }
-                // User touched inside the task. No need to look further,
-                // focus transfer will be handled in ACTION_UP.
-                return;
-            }
-        }
-    }
-
-    void setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion,
-            Rect contentRect, Rect postExclude) {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final Task task = mChildren.get(i);
-            ActivityRecord topVisibleActivity = task.getTopVisibleActivity();
-            if (topVisibleActivity == null || !topVisibleActivity.hasContentToDisplay()) {
-                continue;
-            }
-
-            /**
-             * Exclusion region is the region that TapDetector doesn't care about.
-             * Here we want to remove all non-focused tasks from the exclusion region.
-             * We also remove the outside touch area for resizing for all freeform
-             * tasks (including the focused).
-             *
-             * We save the focused task region once we find it, and add it back at the end.
-             *
-             * If the task is home stack and it is resizable in the minimized state, we want to
-             * exclude the docked stack from touch so we need the entire screen area and not just a
-             * small portion which the home stack currently is resized to.
-             */
-
-            if (task.isActivityTypeHome() && isMinimizedDockAndHomeStackResizable()) {
-                mDisplayContent.getBounds(mTmpRect);
-            } else {
-                task.getDimBounds(mTmpRect);
-            }
-
-            if (task == focusedTask) {
-                // Add the focused task rect back into the exclude region once we are done
-                // processing stacks.
-                postExclude.set(mTmpRect);
-            }
-
-            final boolean isFreeformed = task.inFreeformWindowingMode();
-            if (task != focusedTask || isFreeformed) {
-                if (isFreeformed) {
-                    // If the task is freeformed, enlarge the area to account for outside
-                    // touch area for resize.
-                    mTmpRect.inset(-delta, -delta);
-                    // Intersect with display content rect. If we have system decor (status bar/
-                    // navigation bar), we want to exclude that from the tap detection.
-                    // Otherwise, if the app is partially placed under some system button (eg.
-                    // Recents, Home), pressing that button would cause a full series of
-                    // unwanted transfer focus/resume/pause, before we could go home.
-                    mTmpRect.intersect(contentRect);
-                }
-                touchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
-            }
-        }
+        final PooledPredicate p = PooledLambda.obtainPredicate(
+                Task::isTaskForUser, PooledLambda.__(Task.class), userId);
+        final Task task = getTask(p);
+        p.recycle();
+        return task != null;
     }
 
     public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
@@ -5429,10 +5223,7 @@
     /** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */
     void onPipAnimationEndResize() {
         mBoundsAnimating = false;
-        for (int i = 0; i < mChildren.size(); i++) {
-            final Task t = mChildren.get(i);
-            t.clearPreserveNonFloatingState();
-        }
+        forAllTasks(Task::clearPreserveNonFloatingState, false);
         mWmService.requestTraversal();
     }
 
@@ -5453,7 +5244,7 @@
             if (homeStack == null) {
                 return true;
             }
-            final Task homeTask = homeStack.getTopChild();
+            final Task homeTask = homeStack.getTopMostTask();
             if (homeTask == null) {
                 return true;
             }
@@ -5585,7 +5376,7 @@
     @Override
     void getAnimationFrames(Rect outFrame, Rect outInsets, Rect outStableInsets,
             Rect outSurfaceInsets) {
-        final Task task = getTopChild();
+        final Task task = getTopMostTask();
         if (task != null) {
             task.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
         } else {
@@ -5596,7 +5387,7 @@
     @Override
     RemoteAnimationTarget createRemoteAnimationTarget(
             RemoteAnimationController.RemoteAnimationRecord record) {
-        final Task task = getTopChild();
+        final Task task = getTopMostTask();
         return task != null ? task.createRemoteAnimationTarget(record) : null;
     }
 
@@ -5611,12 +5402,6 @@
                 + getChildCount() + " tasks}";
     }
 
-    void onLockTaskPackagesUpdated() {
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            getChildAt(taskNdx).setLockTaskAuth();
-        }
-    }
-
     void executeAppTransition(ActivityOptions options) {
         getDisplay().mDisplayContent.executeAppTransition();
         ActivityOptions.abort(options);
@@ -5644,10 +5429,10 @@
         final long token = proto.start(fieldId);
         dumpDebugInnerStackOnly(proto, STACK, logLevel);
         proto.write(com.android.server.am.ActivityStackProto.ID, mStackId);
-        for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
-            final Task task = getChildAt(taskNdx);
-            task.dumpDebug(proto, com.android.server.am.ActivityStackProto.TASKS, logLevel);
-        }
+
+        forAllTasks((t) -> {
+            t.dumpDebug(proto, com.android.server.am.ActivityStackProto.TASKS, logLevel);
+        });
         if (mResumedActivity != null) {
             mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
         }
@@ -5672,9 +5457,7 @@
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
         proto.write(StackProto.ID, mStackId);
-        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
-            mChildren.get(taskNdx).dumpDebugInnerTaskOnly(proto, StackProto.TASKS, logLevel);
-        }
+        forAllTasks((t) -> { t.dumpDebugInnerTaskOnly(proto, StackProto.TASKS, logLevel); });
         proto.write(FILLS_PARENT, matchParentBounds());
         getRawBounds().dumpDebug(proto, StackProto.BOUNDS);
         proto.write(DEFER_REMOVAL, mDeferRemoval);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 7356368..8c5fd8c 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -165,28 +165,28 @@
     static final String TAG_TASKS = TAG + POSTFIX_TASKS;
 
     /** How long we wait until giving up on the last activity telling us it is idle. */
-    static final int IDLE_TIMEOUT = 10 * 1000;
+    private static final int IDLE_TIMEOUT = 10 * 1000;
 
     /** How long we can hold the sleep wake lock before giving up. */
-    static final int SLEEP_TIMEOUT = 5 * 1000;
+    private static final int SLEEP_TIMEOUT = 5 * 1000;
 
     // How long we can hold the launch wake lock before giving up.
-    static final int LAUNCH_TIMEOUT = 10 * 1000;
+    private static final int LAUNCH_TIMEOUT = 10 * 1000;
 
     /** How long we wait until giving up on the activity telling us it released the top state. */
-    static final int TOP_RESUMED_STATE_LOSS_TIMEOUT = 500;
+    private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT = 500;
 
-    static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG;
-    static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1;
-    static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
-    static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
-    static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
-    static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
-    static final int RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
-    static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
-    static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
-    static final int REPORT_HOME_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 16;
-    static final int TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 17;
+    private static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG;
+    private static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1;
+    private static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
+    private static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
+    private static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
+    private static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
+    private static final int RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
+    private static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
+    private static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
+    private static final int REPORT_HOME_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 16;
+    private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 17;
 
     // Used to indicate that windows of activities should be preserved during the resize.
     static final boolean PRESERVE_WINDOWS = true;
@@ -237,7 +237,7 @@
 
     // For debugging to make sure the caller when acquiring/releasing our
     // wake lock is the system process.
-    static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
+    private static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
     /** The number of distinct task ids that can be assigned to the tasks of a single user */
     private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
 
@@ -250,11 +250,11 @@
     /** Helper class to abstract out logic for fetching the set of currently running tasks */
     private RunningTasks mRunningTasks;
 
-    final ActivityStackSupervisorHandler mHandler;
+    private final ActivityStackSupervisorHandler mHandler;
     final Looper mLooper;
 
     /** Short cut */
-    WindowManagerService mWindowManager;
+    private WindowManagerService mWindowManager;
 
      /** Common synchronization logic used to save things to disks. */
     PersisterQueue mPersisterQueue;
@@ -286,11 +286,11 @@
 
     /** List of activities whose multi-window mode changed that we need to report to the
      * application */
-    final ArrayList<ActivityRecord> mMultiWindowModeChangedActivities = new ArrayList<>();
+    private final ArrayList<ActivityRecord> mMultiWindowModeChangedActivities = new ArrayList<>();
 
     /** List of activities whose picture-in-picture mode changed that we need to report to the
      * application */
-    final ArrayList<ActivityRecord> mPipModeChangedActivities = new ArrayList<>();
+    private final ArrayList<ActivityRecord> mPipModeChangedActivities = new ArrayList<>();
 
     /**
      * Animations that for the current transition have requested not to
@@ -312,7 +312,7 @@
 
     /** The target stack bounds for the picture-in-picture mode changed that we need to report to
      * the application */
-    Rect mPipModeChangedTargetStackBounds;
+    private Rect mPipModeChangedTargetStackBounds;
 
     /** Used on user changes */
     final ArrayList<UserState> mStartingUsers = new ArrayList<>();
@@ -398,6 +398,52 @@
 
     private boolean mInitialized;
 
+    private final MoveTaskToFullscreenHelper mMoveTaskToFullscreenHelper =
+            new MoveTaskToFullscreenHelper();
+    private class MoveTaskToFullscreenHelper {
+        private ActivityDisplay mToDisplay;
+        private boolean mOnTop;
+        private Task mTopTask;
+        private boolean mSchedulePictureInPictureModeChange;
+
+        void process(ActivityStack fromStack, ActivityDisplay toDisplay, boolean onTop,
+                boolean schedulePictureInPictureModeChange) {
+            mSchedulePictureInPictureModeChange = schedulePictureInPictureModeChange;
+            mToDisplay = toDisplay;
+            mOnTop = onTop;
+            mTopTask = fromStack.getTopMostTask();
+
+            final PooledConsumer c = PooledLambda.obtainConsumer(
+                    MoveTaskToFullscreenHelper::processTask, this, PooledLambda.__(Task.class));
+            fromStack.forAllTasks(c, false);
+            c.recycle();
+            mToDisplay = null;
+            mTopTask = null;
+        }
+
+        private void processTask(Task task) {
+            final ActivityStack toStack = mToDisplay.getOrCreateStack(
+                    null, mTmpOptions, task, task.getActivityType(), mOnTop);
+
+            if (mOnTop) {
+                final boolean isTopTask = task == mTopTask;
+                // Defer resume until all the tasks have been moved to the fullscreen stack
+                task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, isTopTask /*animate*/,
+                        DEFER_RESUME, mSchedulePictureInPictureModeChange,
+                        "moveTasksToFullscreenStack - onTop");
+                MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext,
+                        task.effectiveUid, task.realActivity.flattenToString());
+            } else {
+                // Position the tasks in the fullscreen stack in order at the bottom of the
+                // stack. Also defer resume until all the tasks have been moved to the
+                // fullscreen stack.
+                task.reparent(toStack, ON_TOP, REPARENT_LEAVE_STACK_IN_PLACE,
+                        !ANIMATE, DEFER_RESUME, mSchedulePictureInPictureModeChange,
+                        "moveTasksToFullscreenStack - NOT_onTop");
+            }
+        }
+    }
+
     /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
@@ -1522,35 +1568,11 @@
             // the picture-in-picture mode.
             final boolean schedulePictureInPictureModeChange =
                     windowingMode == WINDOWING_MODE_PINNED;
-            final ArrayList<Task> tasks = fromStack.getAllTasks();
 
-            if (!tasks.isEmpty()) {
+            if (fromStack.hasChild()) {
                 mTmpOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
-                final int size = tasks.size();
-                for (int i = 0; i < size; ++i) {
-                    final Task task = tasks.get(i);
-                    final ActivityStack toStack = toDisplay.getOrCreateStack(
-                                null, mTmpOptions, task, task.getActivityType(), onTop);
-
-                    if (onTop) {
-                        final boolean isTopTask = i == (size - 1);
-                        // Defer resume until all the tasks have been moved to the fullscreen stack
-                        task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
-                                isTopTask /* animate */, DEFER_RESUME,
-                                schedulePictureInPictureModeChange,
-                                "moveTasksToFullscreenStack - onTop");
-                        MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext,
-                                task.effectiveUid, task.realActivity.flattenToString());
-                    } else {
-                        // Position the tasks in the fullscreen stack in order at the bottom of the
-                        // stack. Also defer resume until all the tasks have been moved to the
-                        // fullscreen stack.
-                        task.reparent(toStack, ON_TOP,
-                                REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
-                                schedulePictureInPictureModeChange,
-                                "moveTasksToFullscreenStack - NOT_onTop");
-                    }
-                }
+                mMoveTaskToFullscreenHelper.process(
+                        fromStack, toDisplay, onTop, schedulePictureInPictureModeChange);
             }
 
             mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
@@ -1562,12 +1584,8 @@
     }
 
     void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) {
-        moveTasksToFullscreenStackLocked(fromStack, DEFAULT_DISPLAY, onTop);
-    }
-
-    void moveTasksToFullscreenStackLocked(ActivityStack fromStack, int toDisplayId, boolean onTop) {
         mWindowManager.inSurfaceTransaction(() ->
-                moveTasksToFullscreenStackInSurfaceTransaction(fromStack, toDisplayId, onTop));
+                moveTasksToFullscreenStackInSurfaceTransaction(fromStack, DEFAULT_DISPLAY, onTop));
     }
 
     void setSplitScreenResizing(boolean resizing) {
@@ -1630,7 +1648,7 @@
         try {
             // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
             mAllowDockedStackResize = false;
-            ActivityRecord r = stack.topRunningActivityLocked();
+            ActivityRecord r = stack.topRunningActivity();
             stack.resize(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
                     !PRESERVE_WINDOWS, DEFER_RESUME);
 
@@ -1731,7 +1749,6 @@
     }
 
     private void removeStackInSurfaceTransaction(ActivityStack stack) {
-        final ArrayList<Task> tasks = stack.getAllTasks();
         if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
             /**
              * Workaround: Force-stop all the activities in the pinned stack before we reparent them
@@ -1746,18 +1763,22 @@
             stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
             stack.mForceHidden = false;
             activityIdleInternalLocked(null, false /* fromTimeout */,
-                    true /* processPausingActivites */, null /* configuration */);
+                    true /* processPausingActivities */, null /* configuration */);
 
             // Move all the tasks to the bottom of the fullscreen stack
             moveTasksToFullscreenStackLocked(stack, !ON_TOP);
         } else {
-            for (int i = tasks.size() - 1; i >= 0; i--) {
-                removeTaskByIdLocked(tasks.get(i).mTaskId, true /* killProcess */,
-                        REMOVE_FROM_RECENTS, "remove-stack");
-            }
+            final PooledConsumer c = PooledLambda.obtainConsumer(
+                    ActivityStackSupervisor::processRemoveTask, this, PooledLambda.__(Task.class));
+            stack.forAllTasks(c);
+            c.recycle();
         }
     }
 
+    private void processRemoveTask(Task task) {
+        removeTask(task, true /* killProcess */, REMOVE_FROM_RECENTS, "remove-stack");
+    }
+
     /**
      * Removes the stack associated with the given {@param stack}. If the {@param stack} is the
      * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
@@ -1775,24 +1796,28 @@
      * @param removeFromRecents Whether to also remove the task from recents.
      * @return Returns true if the given task was found and removed.
      */
-    boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
+    boolean removeTaskById(int taskId, boolean killProcess, boolean removeFromRecents,
             String reason) {
         final Task task =
                 mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
         if (task != null) {
-            task.removeTaskActivitiesLocked(reason);
-            cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
-            mService.getLockTaskController().clearLockedTask(task);
-            mService.getTaskChangeNotificationController().notifyTaskStackChanged();
-            if (task.isPersistable) {
-                mService.notifyTaskPersisterLocked(null, true);
-            }
+            removeTask(task, killProcess, removeFromRecents, reason);
             return true;
         }
         Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
         return false;
     }
 
+    void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) {
+        task.removeTaskActivitiesLocked(reason);
+        cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
+        mService.getLockTaskController().clearLockedTask(task);
+        mService.getTaskChangeNotificationController().notifyTaskStackChanged();
+        if (task.isPersistable) {
+            mService.notifyTaskPersisterLocked(null, true);
+        }
+    }
+
     void cleanUpRemovedTaskLocked(Task task, boolean killProcess, boolean removeFromRecents) {
         if (removeFromRecents) {
             mRecentTasks.remove(task);
@@ -1899,7 +1924,7 @@
         if (wasTrimmed) {
             // Task was trimmed from the recent tasks list -- remove the active task record as well
             // since the user won't really be able to go back to it
-            removeTaskByIdLocked(task.mTaskId, killProcess, false /* removeFromRecents */,
+            removeTaskById(task.mTaskId, killProcess, false /* removeFromRecents */,
                     "recent-task-trimmed");
         }
         task.removedFromRecents();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index baa2955..23083c9 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1434,7 +1434,7 @@
                 // If there is no state change (e.g. a resumed activity is reparented to top of
                 // another display) to trigger a visibility/configuration checking, we have to
                 // update the configuration for changing to different display.
-                final ActivityRecord currentTop = startedActivityStack.topRunningActivityLocked();
+                final ActivityRecord currentTop = startedActivityStack.topRunningActivity();
                 if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
                     mRootActivityContainer.ensureVisibilityAndConfig(
                             currentTop, currentTop.getDisplayId(),
@@ -2307,7 +2307,7 @@
                     ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
             final Task topTask = curTop != null ? curTop.getTask() : null;
             differentTopTask = topTask != intentActivity.getTask()
-                    || (focusStack != null && topTask != focusStack.topTask());
+                    || (focusStack != null && topTask != focusStack.getTopMostTask());
         } else {
             // The existing task should always be different from those in other displays.
             differentTopTask = true;
@@ -2371,7 +2371,7 @@
                     mMovedToFront = true;
                 }
 
-                if (launchStack != null && launchStack.topTask() == null) {
+                if (launchStack != null && launchStack.getTopMostTask() == null) {
                     // The task does not need to be reparented to the launch stack. Remove the
                     // launch stack if there is no activity in it.
                     Slog.w(TAG, "Removing an empty stack: " + launchStack);
@@ -2588,7 +2588,7 @@
             // If task's parent stack is not focused - use it during adjacent launch.
             return parentStack;
         } else {
-            if (focusedStack != null && task == focusedStack.topTask()) {
+            if (focusedStack != null && task == focusedStack.getTopMostTask()) {
                 // If task is already on top of focused stack - use it. We don't want to move the
                 // existing focused task to adjacent stack, just deliver new intent in this case.
                 return focusedStack;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index efd21ec..46c4d87 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -698,7 +698,7 @@
 
     private final Runnable mUpdateOomAdjRunnable = new Runnable() {
         @Override
-	public void run() {
+        public void run() {
             mAmInternal.updateOomAdj();
         }
     };
@@ -1591,7 +1591,8 @@
             // We should consolidate.
             if (mController != null) {
                 // Find the first activity that is not finishing.
-                final ActivityRecord next = r.getActivityStack().topRunningActivityLocked(token, 0);
+                final ActivityRecord next =
+                        r.getActivityStack().topRunningActivity(token, INVALID_TASK_ID);
                 if (next != null) {
                     // ask watcher if this is allowed
                     boolean resumeOK = true;
@@ -1628,11 +1629,9 @@
                     // because we don't support returning them across task boundaries. Also, to
                     // keep backwards compatibility we remove the task from recents when finishing
                     // task with root activity.
-                    res = mStackSupervisor.removeTaskByIdLocked(tr.mTaskId, false /* killProcess */,
+                    mStackSupervisor.removeTask(tr, false /*killProcess*/,
                             finishWithRootActivity, "finish-activity");
-                    if (!res) {
-                        Slog.i(TAG, "Removing task failed to finish activity");
-                    }
+                    res = true;
                     // Explicitly dismissing the activity so reset its relaunch flag.
                     r.mRelaunchReason = RELAUNCH_REASON_NONE;
                 } else {
@@ -1901,7 +1900,7 @@
     public boolean isTopActivityImmersive() {
         enforceNotIsolatedCaller("isTopActivityImmersive");
         synchronized (mGlobalLock) {
-            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
+            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivity();
             return (r != null) ? r.immersive : false;
         }
     }
@@ -1931,7 +1930,7 @@
     public int getFrontActivityScreenCompatMode() {
         enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
         synchronized (mGlobalLock) {
-            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
+            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivity();
             if (r == null) {
                 return ActivityManager.COMPAT_MODE_UNKNOWN;
             }
@@ -1945,7 +1944,7 @@
                 "setFrontActivityScreenCompatMode");
         ApplicationInfo ai;
         synchronized (mGlobalLock) {
-            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
+            final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivity();
             if (r == null) {
                 Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
                 return;
@@ -2078,7 +2077,7 @@
                     Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
                     return;
                 }
-                final ActivityRecord r = stack.topRunningActivityLocked();
+                final ActivityRecord r = stack.topRunningActivity();
                 if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) {
                     mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
@@ -2133,7 +2132,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS,
+                return mStackSupervisor.removeTaskById(taskId, true, REMOVE_FROM_RECENTS,
                         "remove-task");
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -2207,7 +2206,7 @@
                 int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
                 final Task task = mRootActivityContainer.anyTaskForId(taskId);
                 if (task != null) {
-                    return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
+                    return ActivityRecord.getStackLocked(token).moveTaskToBack(task);
                 }
             } finally {
                 Binder.restoreCallingIdentity(origId);
@@ -2918,7 +2917,7 @@
         }
 
         final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
-        if (stack == null || task != stack.topTask()) {
+        if (stack == null || task != stack.getTopMostTask()) {
             throw new IllegalArgumentException("Invalid task, not in foreground");
         }
 
@@ -5787,7 +5786,7 @@
                 // If the configuration changed, and the caller is not already
                 // in the process of starting an activity, then find the top
                 // activity to check if its configuration needs to change.
-                starting = mainStack.topRunningActivityLocked();
+                starting = mainStack.topRunningActivity();
             }
 
             if (starting != null) {
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 93a22ca..6d9584c 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -62,7 +62,7 @@
             long origId = Binder.clearCallingIdentity();
             try {
                 // We remove the task from recents to preserve backwards
-                if (!mService.mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
+                if (!mService.mStackSupervisor.removeTaskById(mTaskId, false,
                         REMOVE_FROM_RECENTS, "finish-and-remove-task")) {
                     throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
                 }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index e0c5fd05..3a33a3d 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -130,6 +130,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils.Dump;
 import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.AttributeCache;
 import com.android.server.protolog.common.ProtoLog;
 import com.android.server.wm.animation.ClipRectLRAnimation;
@@ -1897,7 +1898,10 @@
                 for (int i = 0; i < specs.length; i++) {
                     AppTransitionAnimationSpec spec = specs[i];
                     if (spec != null) {
-                        final WindowContainer container = findTask(spec.taskId);
+                        final PooledPredicate p = PooledLambda.obtainPredicate(
+                                Task::isTaskId, PooledLambda.__(Task.class), spec.taskId);
+                        final WindowContainer container = mDisplayContent.getTask(p);
+                        p.recycle();
                         if (container == null) {
                             continue;
                         }
@@ -1918,21 +1922,6 @@
         }
     }
 
-    private Task findTask(int taskId) {
-        if (taskId < 0) {
-            return null;
-        }
-        final ArrayList<Task> tasks = new ArrayList<>();
-        mDisplayContent.forAllTasks(task -> {
-            if (task.mTaskId == taskId) {
-                tasks.add(task);
-                return true;
-            }
-            return false;
-        });
-        return tasks.size() == 1 ? tasks.get(0) : null;
-    }
-
     void overridePendingAppTransitionMultiThumbFuture(
             IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
             boolean scaleUp) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index d7f4b34..6ea0650 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -48,6 +48,8 @@
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
 import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -61,6 +63,7 @@
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.TransitionType;
 import android.view.animation.Animation;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -177,9 +180,15 @@
         final int layoutRedo;
         mService.mSurfaceAnimationRunner.deferStartingAnimations();
         try {
-            handleClosingApps(transit, animLp, voiceInteraction);
-            handleOpeningApps(transit, animLp, voiceInteraction);
-            handleChangingApps(transit, animLp, voiceInteraction);
+            // TODO: Apply an app transition animation on TaskStack instead of ActivityRecord when
+            //  appropriate.
+            applyAnimations(mDisplayContent.mClosingApps, transit, false /* visible */,
+                    animLp, voiceInteraction);
+            applyAnimations(mDisplayContent.mOpeningApps, transit, true /* visible */,
+                    animLp, voiceInteraction);
+            handleClosingApps();
+            handleOpeningApps();
+            handleChangingApps(transit);
 
             appTransition.setLastAppTransition(transit, topOpeningApp,
                     topClosingApp, topChangingApp);
@@ -227,8 +236,8 @@
         return mainWindow != null ? mainWindow.mAttrs : null;
     }
 
-    RemoteAnimationAdapter getRemoteAnimationOverride(ActivityRecord animLpActivity, int transit,
-            ArraySet<Integer> activityTypes) {
+    RemoteAnimationAdapter getRemoteAnimationOverride(ActivityRecord animLpActivity,
+            @TransitionType int transit, ArraySet<Integer> activityTypes) {
         final RemoteAnimationDefinition definition = animLpActivity.getRemoteAnimationDefinition();
         if (definition != null) {
             final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
@@ -246,8 +255,8 @@
      * Overrides the pending transition with the remote animation defined for the transition in the
      * set of defined remote animations in the app window token.
      */
-    private void overrideWithRemoteAnimationIfSet(ActivityRecord animLpActivity, int transit,
-            ArraySet<Integer> activityTypes) {
+    private void overrideWithRemoteAnimationIfSet(ActivityRecord animLpActivity,
+            @TransitionType int transit, ArraySet<Integer> activityTypes) {
         if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
             // The crash transition has higher priority than any involved remote animations.
             return;
@@ -266,7 +275,7 @@
     /**
      * @return The window token that determines the animation theme.
      */
-    private ActivityRecord findAnimLayoutParamsToken(@WindowManager.TransitionType int transit,
+    private ActivityRecord findAnimLayoutParamsToken(@TransitionType int transit,
             ArraySet<Integer> activityTypes) {
         ActivityRecord result;
         final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
@@ -340,26 +349,60 @@
         return false;
     }
 
-    private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
+    /**
+     * Apply an app transition animation on a set of {@link ActivityRecord}
+     *
+     * @param apps The list of apps to which an app transition animation applies.
+     * @param transit The current transition type.
+     * @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes
+     *                invisible.
+     * @param animLp Layout parameters in which an app transition animation runs.
+     * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
+     *                         interaction session driving task.
+     */
+    private void applyAnimations(ArraySet<ActivityRecord> apps, @TransitionType int transit,
+            boolean visible, LayoutParams animLp, boolean voiceInteraction) {
+        final int appsCount = apps.size();
+        for (int i = 0; i < appsCount; i++) {
+            final ActivityRecord app = apps.valueAt(i);
+            if (transit != WindowManager.TRANSIT_UNSET && app.shouldApplyAnimation(visible)) {
+                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Changing app %s visible=%b performLayout=%b",
+                        app, app.isVisible(), false);
+                if (!app.mUseTransferredAnimation) {
+                    app.applyAnimation(animLp, transit, visible, voiceInteraction);
+                }
+                final WindowState window = app.findMainWindow();
+                final AccessibilityController accessibilityController =
+                        app.mWmService.mAccessibilityController;
+                if (window != null && accessibilityController != null) {
+                    accessibilityController.onAppWindowTransitionLocked(window, transit);
+                }
+            }
+        }
+    }
+
+    private void handleOpeningApps() {
         final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
         final int appsCount = openingApps.size();
-        for (int i = 0; i < appsCount; i++) {
-            ActivityRecord wtoken = openingApps.valueAt(i);
-            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", wtoken);
 
-            if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) {
+        for (int i = 0; i < appsCount; i++) {
+            final ActivityRecord app = openingApps.valueAt(i);
+            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", app);
+
+            app.commitVisibility(true /* visible */, false /* performLayout */);
+            if (!app.isAnimating(PARENTS | CHILDREN)) {
                 // This token isn't going to be animating. Add it to the list of tokens to
                 // be notified of app transition complete since the notification will not be
                 // sent be the app window animator.
-                mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
+                mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(app.token);
             }
-            wtoken.updateReportedVisibilityLocked();
-            wtoken.waitingToShow = false;
+            app.updateReportedVisibilityLocked();
+            app.waitingToShow = false;
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     ">>> OPEN TRANSACTION handleAppTransitionReady()");
             mService.openSurfaceTransaction();
             try {
-                wtoken.showAllWindowsLocked();
+                app.showAllWindowsLocked();
             } finally {
                 mService.closeSurfaceTransaction("handleAppTransitionReady");
                 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
@@ -367,41 +410,40 @@
             }
 
             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) {
-                wtoken.attachThumbnailAnimation();
+                app.attachThumbnailAnimation();
             } else if (mDisplayContent.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) {
-                wtoken.attachCrossProfileAppsThumbnailAnimation();
+                app.attachCrossProfileAppsThumbnailAnimation();
             }
         }
     }
 
-    private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
+    private void handleClosingApps() {
         final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
         final int appsCount = closingApps.size();
-        for (int i = 0; i < appsCount; i++) {
-            ActivityRecord wtoken = closingApps.valueAt(i);
 
-            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", wtoken);
-            // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
-            //       animating?
-            wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction);
-            wtoken.updateReportedVisibilityLocked();
+        for (int i = 0; i < appsCount; i++) {
+            final ActivityRecord app = closingApps.valueAt(i);
+            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", app);
+
+            app.commitVisibility(false /* visible */, false /* performLayout */);
+            app.updateReportedVisibilityLocked();
             // Force the allDrawn flag, because we want to start
             // this guy's animations regardless of whether it's
             // gotten drawn.
-            wtoken.allDrawn = true;
+            app.allDrawn = true;
             // Ensure that apps that are mid-starting are also scheduled to have their
             // starting windows removed after the animation is complete
-            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) {
-                wtoken.removeStartingWindow();
+            if (app.startingWindow != null && !app.startingWindow.mAnimatingExit) {
+                app.removeStartingWindow();
             }
 
             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
-                wtoken.attachThumbnailAnimation();
+                app.attachThumbnailAnimation();
             }
         }
     }
 
-    private void handleChangingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
+    private void handleChangingApps(@TransitionType int transit) {
         final ArraySet<ActivityRecord> apps = mDisplayContent.mChangingApps;
         final int appsCount = apps.size();
         for (int i = 0; i < appsCount; i++) {
@@ -419,7 +461,7 @@
         }
     }
 
-    private void handleNonAppWindowsInTransition(int transit, int flags) {
+    private void handleNonAppWindowsInTransition(@TransitionType int transit, int flags) {
         if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
             if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
                     && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
@@ -510,8 +552,8 @@
         return true;
     }
 
-    private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper,
-            boolean closingAppHasWallpaper) {
+    private int maybeUpdateTransitToWallpaper(@TransitionType int transit,
+            boolean openingAppHasWallpaper, boolean closingAppHasWallpaper) {
         // Given no app transition pass it through instead of a wallpaper transition.
         // Never convert the crashing transition.
         // Never update the transition for the wallpaper if we are just docking from recents
@@ -604,7 +646,7 @@
      *         situation.
      */
     @VisibleForTesting
-    int maybeUpdateTransitToTranslucentAnim(int transit) {
+    int maybeUpdateTransitToTranslucentAnim(@TransitionType int transit) {
         if (AppTransition.isChangeTransit(transit)) {
             // There's no special animation to handle change animations with translucent apps
             return transit;
@@ -644,7 +686,7 @@
      * to determine whether animations should be clipped to the task bounds instead of stack bounds.
      */
     @VisibleForTesting
-    boolean isTransitWithinTask(int transit, Task task) {
+    boolean isTransitWithinTask(@TransitionType int transit, Task task) {
         if (task == null
                 || !mDisplayContent.mChangingApps.isEmpty()) {
             // if there is no task, then we can't constrain to the task.
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index bfa72e0..bd0ea3d 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -81,9 +81,6 @@
      */
     private Configuration mFullConfiguration = new Configuration();
 
-    /** The bit mask of the last override fields of full configuration. */
-    private int mLastOverrideConfigurationChanges;
-
     /**
      * Contains merged override configuration settings from the top of the hierarchy down to this
      * particular instance. It is different from {@link #mFullConfiguration} because it starts from
@@ -121,11 +118,6 @@
         return mFullConfiguration;
     }
 
-    /** Returns the last changes from applying override configuration. */
-    int getLastOverrideConfigurationChanges() {
-        return mLastOverrideConfigurationChanges;
-    }
-
     /**
      * Notify that parent config changed and we need to update full configuration.
      * @see #mFullConfiguration
@@ -141,8 +133,7 @@
         mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
         resolveOverrideConfiguration(newParentConfig);
         mFullConfiguration.setTo(newParentConfig);
-        mLastOverrideConfigurationChanges =
-                mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
+        mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
         if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
             onMergedOverrideConfigurationChanged();
             // This depends on the assumption that change-listeners don't do
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8e126b5..aa0e973 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -55,9 +55,8 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE;
-import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
@@ -93,6 +92,7 @@
 import static com.android.server.wm.DisplayContentProto.ID;
 import static com.android.server.wm.DisplayContentProto.IME_WINDOWS;
 import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
+import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
 import static com.android.server.wm.DisplayContentProto.PINNED_STACK_CONTROLLER;
 import static com.android.server.wm.DisplayContentProto.ROTATION;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
@@ -194,6 +194,7 @@
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.AnimationThread;
 import com.android.server.policy.WindowManagerPolicy;
@@ -240,7 +241,21 @@
     /** Unique identifier of this display. */
     private final int mDisplayId;
 
-    /** The containers below are the only child containers the display can have. */
+    /**
+     * Most surfaces will be a child of this window. There are some special layers and windows
+     * which are always on top of others and omitted from Screen-Magnification, for example the
+     * strict mode flash or the magnification overlay itself. Those layers will be children of
+     * {@link #mOverlayContainers} where mWindowContainers contains everything else.
+     */
+    private final WindowContainers mWindowContainers =
+            new WindowContainers("mWindowContainers", mWmService);
+
+    // Contains some special windows which are always on top of others and omitted from
+    // Screen-Magnification, for example the WindowMagnification windows.
+    private final NonAppWindowContainers mOverlayContainers =
+            new NonAppWindowContainers("mOverlayContainers", mWmService);
+
+    /** The containers below are the only child containers {@link #mWindowContainers} can have. */
     // Contains all window containers that are related to apps (Activities)
     private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mWmService);
     // Contains all non-app window containers that should be displayed above the app containers
@@ -260,7 +275,6 @@
 
     private WindowState mTmpWindow;
     private WindowState mTmpWindow2;
-    private boolean mTmpRecoveringMemory;
     private boolean mUpdateImeTarget;
     private boolean mTmpInitial;
     private int mMaxUiWidth;
@@ -346,6 +360,9 @@
     /** The desired scaling factor for compatible apps. */
     float mCompatibleScreenScale;
 
+    /** @see #getCurrentOverrideConfigurationChanges */
+    private int mCurrentOverrideConfigurationChanges;
+
     /**
      * Orientation forced by some window. If there is no visible window that specifies orientation
      * it is set to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED}.
@@ -490,20 +507,6 @@
     private ScreenRotationAnimation mScreenRotationAnimation;
 
     /**
-     * We organize all top-level Surfaces in to the following layers.
-     * mOverlayLayer contains a few Surfaces which are always on top of others
-     * and omitted from Screen-Magnification, for example the strict mode flash or
-     * the magnification overlay itself.
-     * {@link #mWindowingLayer} contains everything else.
-     */
-    private SurfaceControl mOverlayLayer;
-
-    /**
-     * See {@link #mOverlayLayer}
-     */
-    private SurfaceControl mWindowingLayer;
-
-    /**
      * Sequence number for the current layout pass.
      */
     int mLayoutSeq = 0;
@@ -910,24 +913,19 @@
                 .setOpaque(true)
                 .setContainerLayer();
         mSurfaceControl = b.setName("Root").setContainerLayer().build();
-        mWindowingLayer = b.setName("Display Windows").setParent(mSurfaceControl).build();
-        mOverlayLayer = b.setName("Display Overlays").setParent(mSurfaceControl).build();
 
         getPendingTransaction()
                 .setLayer(mSurfaceControl, 0)
                 .setLayerStack(mSurfaceControl, mDisplayId)
-                .show(mSurfaceControl)
-                .setLayer(mWindowingLayer, 0)
-                .show(mWindowingLayer)
-                .setLayer(mOverlayLayer, 1)
-                .show(mOverlayLayer);
+                .show(mSurfaceControl);
         getPendingTransaction().apply();
 
         // These are the only direct children we should ever have and they are permanent.
-        super.addChild(mBelowAppWindowsContainers, null);
-        super.addChild(mTaskStackContainers, null);
-        super.addChild(mAboveAppWindowsContainers, null);
-        super.addChild(mImeWindowsContainers, null);
+        super.addChild(mWindowContainers, null);
+        super.addChild(mOverlayContainers, null);
+
+        mWindowContainers.addChildren();
+
         // Sets the display content for the children.
         onDisplayChanged(this);
 
@@ -1001,6 +999,9 @@
                 case TYPE_INPUT_METHOD_DIALOG:
                     mImeWindowsContainers.addChild(token);
                     break;
+                case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
+                    mOverlayContainers.addChild(token);
+                    break;
                 default:
                     mAboveAppWindowsContainers.addChild(token);
                     break;
@@ -1867,6 +1868,25 @@
         mTaskStackContainers.onStackWindowingModeChanged(stack);
     }
 
+    /**
+     * The value is only valid in the scope {@link #onRequestedOverrideConfigurationChanged} of the
+     * changing hierarchy and the {@link #onConfigurationChanged} of its children.
+     *
+     * @return The current changes ({@link android.content.pm.ActivityInfo.Config}) of requested
+     *         override configuration.
+     */
+    int getCurrentOverrideConfigurationChanges() {
+        return mCurrentOverrideConfigurationChanges;
+    }
+
+    @Override
+    public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
+        mCurrentOverrideConfigurationChanges =
+                getRequestedOverrideConfiguration().diff(overrideConfiguration);
+        super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
+        mCurrentOverrideConfigurationChanges = 0;
+    }
+
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         final int lastOrientation = getConfiguration().orientation;
@@ -1878,8 +1898,8 @@
         if (lastOrientation != getConfiguration().orientation) {
             getMetricsLogger().write(
                     new LogMaker(MetricsEvent.ACTION_PHONE_ORIENTATION_CHANGED)
-                    .setSubtype(getConfiguration().orientation)
-                    .addTaggedData(MetricsEvent.FIELD_DISPLAY_ID, getDisplayId()));
+                            .setSubtype(getConfiguration().orientation)
+                            .addTaggedData(MetricsEvent.FIELD_DISPLAY_ID, getDisplayId()));
         }
 
         // If there was no pinned stack, we still need to notify the controller of the display info
@@ -1936,49 +1956,6 @@
         setWindowingMode(windowingMode);
     }
 
-    /**
-     * In split-screen mode we process the IME containers above the docked divider
-     * rather than directly above their target.
-     */
-    private boolean skipTraverseChild(WindowContainer child) {
-        if (child == mImeWindowsContainers && mInputMethodTarget != null
-                && !hasSplitScreenPrimaryStack()) {
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
-        // Special handling so we can process IME windows with #forAllImeWindows above their IME
-        // target, or here in order if there isn't an IME target.
-        if (traverseTopToBottom) {
-            for (int i = mChildren.size() - 1; i >= 0; --i) {
-                final DisplayChildWindowContainer child = mChildren.get(i);
-                if (skipTraverseChild(child)) {
-                    continue;
-                }
-
-                if (child.forAllWindows(callback, traverseTopToBottom)) {
-                    return true;
-                }
-            }
-        } else {
-            final int count = mChildren.size();
-            for (int i = 0; i < count; i++) {
-                final DisplayChildWindowContainer child = mChildren.get(i);
-                if (skipTraverseChild(child)) {
-                    continue;
-                }
-
-                if (child.forAllWindows(callback, traverseTopToBottom)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     boolean forAllImeWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
         return mImeWindowsContainers.forAllWindows(callback, traverseTopToBottom);
     }
@@ -2000,7 +1977,7 @@
             if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "Display id=%d is frozen, return %d", mDisplayId,
-                                mLastWindowForcedOrientation);
+                        mLastWindowForcedOrientation);
                 // If the display is frozen, some activities may be in the middle of restarting, and
                 // thus have removed their old window. If the window has the flag to hide the lock
                 // screen, then the lock screen can re-appear and inflict its own orientation on us.
@@ -2014,7 +1991,7 @@
                 // momentarily unavailable due to activity relaunch.
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "Display id=%d is frozen while keyguard locked, return %d",
-                                mDisplayId, getLastOrientation());
+                        mDisplayId, getLastOrientation());
                 return getLastOrientation();
             }
         } else {
@@ -2268,7 +2245,7 @@
         forAllWindows(fn, true /* traverseTopToBottom */);
         fn.recycle();
         return FIRST_APPLICATION_WINDOW <= targetWindowType[0]
-                        && targetWindowType[0] <= LAST_APPLICATION_WINDOW;
+                && targetWindowType[0] <= LAST_APPLICATION_WINDOW;
     }
 
     /**
@@ -2277,19 +2254,7 @@
      */
     Task findTaskForResizePoint(int x, int y) {
         final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
-        mTmpTaskForResizePointSearchResult.reset();
-        for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mTaskStackContainers.getChildAt(stackNdx);
-            if (!stack.getWindowConfiguration().canResizeTask()) {
-                return null;
-            }
-
-            stack.findTaskForResizePoint(x, y, delta, mTmpTaskForResizePointSearchResult);
-            if (mTmpTaskForResizePointSearchResult.searchDone) {
-                return mTmpTaskForResizePointSearchResult.taskForResize;
-            }
-        }
-        return null;
+        return mTmpTaskForResizePointSearchResult.process(mTaskStackContainers, x, y, delta);
     }
 
     void updateTouchExcludeRegion() {
@@ -2299,13 +2264,15 @@
         } else {
             mTouchExcludeRegion.set(mBaseDisplayRect);
             final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
+            mTmpRect.setEmpty();
             mTmpRect2.setEmpty();
-            for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0;
-                    --stackNdx) {
-                final ActivityStack stack = mTaskStackContainers.getChildAt(stackNdx);
-                stack.setTouchExcludeRegion(focusedTask, delta, mTouchExcludeRegion,
-                        mDisplayFrames.mContent, mTmpRect2);
-            }
+
+            final PooledConsumer c = PooledLambda.obtainConsumer(
+                    DisplayContent::processTaskForTouchExcludeRegion, this,
+                    PooledLambda.__(Task.class), focusedTask, delta);
+            mTaskStackContainers.forAllTasks(c);
+            c.recycle();
+
             // If we removed the focused task above, add it back and only leave its
             // outside touch area in the exclusion. TapDetector is not interested in
             // any touch inside the focused task itself.
@@ -2335,12 +2302,56 @@
         mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion);
     }
 
+    private void processTaskForTouchExcludeRegion(Task task, Task focusedTask, int delta) {
+        final ActivityRecord topVisibleActivity = task.getTopVisibleActivity();
+
+        if (topVisibleActivity == null || !topVisibleActivity.hasContentToDisplay()) {
+            return;
+        }
+
+        // Exclusion region is the region that TapDetector doesn't care about.
+        // Here we want to remove all non-focused tasks from the exclusion region.
+        // We also remove the outside touch area for resizing for all freeform
+        // tasks (including the focused).
+        // We save the focused task region once we find it, and add it back at the end.
+        // If the task is home stack and it is resizable in the minimized state, we want to
+        // exclude the docked stack from touch so we need the entire screen area and not just a
+        // small portion which the home stack currently is resized to.
+        if (task.isActivityTypeHome() && task.getStack().isMinimizedDockAndHomeStackResizable()) {
+            mDisplayContent.getBounds(mTmpRect);
+        } else {
+            task.getDimBounds(mTmpRect);
+        }
+
+        if (task == focusedTask) {
+            // Add the focused task rect back into the exclude region once we are done
+            // processing stacks.
+            mTmpRect2.set(mTmpRect);
+        }
+
+        final boolean isFreeformed = task.inFreeformWindowingMode();
+        if (task != focusedTask || isFreeformed) {
+            if (isFreeformed) {
+                // If the task is freeformed, enlarge the area to account for outside
+                // touch area for resize.
+                mTmpRect.inset(-delta, -delta);
+                // Intersect with display content rect. If we have system decor (status bar/
+                // navigation bar), we want to exclude that from the tap detection.
+                // Otherwise, if the app is partially placed under some system button (eg.
+                // Recents, Home), pressing that button would cause a full series of
+                // unwanted transfer focus/resume/pause, before we could go home.
+                mTmpRect.intersect(mDisplayFrames.mContent);
+            }
+            mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+        }
+    }
+
     /**
      * Union the region with all the tap exclude region provided by windows on this display.
      *
      * @param inOutRegion The region to be amended.
      */
-    void amendWindowTapExcludeRegion(Region inOutRegion) {
+    private void amendWindowTapExcludeRegion(Region inOutRegion) {
         for (int i = mTapExcludeProvidingWindows.size() - 1; i >= 0; i--) {
             final WindowState win = mTapExcludeProvidingWindows.valueAt(i);
             win.amendTapExcludeRegion(inOutRegion);
@@ -2383,8 +2394,6 @@
             mPointerEventDispatcher.dispose();
             setRotationAnimation(null);
             mWmService.mAnimator.removeDisplayLocked(mDisplayId);
-            mWindowingLayer.release();
-            mOverlayLayer.release();
             mInputMonitor.onDisplayRemoved();
             // TODO(display-merge): Remove cast
             mWmService.mDisplayNotificationController
@@ -2499,7 +2508,7 @@
         // the minimized docked stack bounds.
         final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock()
                 || (topDockedTask != null && imeOnBottom && !dockedStack.isAdjustedForIme()
-                        && dockedStack.getBounds().height() < topDockedTask.getBounds().height());
+                && dockedStack.getBounds().height() < topDockedTask.getBounds().height());
 
         // The divider could be adjusted for IME position, or be thinner than usual,
         // or both. There are three possible cases:
@@ -2630,6 +2639,10 @@
             final WindowToken windowToken = mImeWindowsContainers.getChildAt(i);
             windowToken.dumpDebug(proto, IME_WINDOWS, logLevel);
         }
+        for (int i = mOverlayContainers.getChildCount() - 1; i >= 0; --i) {
+            final WindowToken windowToken = mOverlayContainers.getChildAt(i);
+            windowToken.dumpDebug(proto, OVERLAY_WINDOWS, logLevel);
+        }
         proto.write(DPI, mBaseDisplayDensity);
         mDisplayInfo.dumpDebug(proto, DISPLAY_INFO);
         proto.write(ROTATION, getRotation());
@@ -2660,31 +2673,31 @@
         pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
         final String subPrefix = "  " + prefix;
         pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
-            pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
-            pw.print("dpi");
-            if (mInitialDisplayWidth != mBaseDisplayWidth
-                    || mInitialDisplayHeight != mBaseDisplayHeight
-                    || mInitialDisplayDensity != mBaseDisplayDensity) {
-                pw.print(" base=");
-                pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight);
-                pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi");
-            }
-            if (mDisplayScalingDisabled) {
-                pw.println(" noscale");
-            }
-            pw.print(" cur=");
-            pw.print(mDisplayInfo.logicalWidth);
-            pw.print("x"); pw.print(mDisplayInfo.logicalHeight);
-            pw.print(" app=");
-            pw.print(mDisplayInfo.appWidth);
-            pw.print("x"); pw.print(mDisplayInfo.appHeight);
-            pw.print(" rng="); pw.print(mDisplayInfo.smallestNominalAppWidth);
-            pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
-            pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
-            pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
-            pw.print(subPrefix + "deferred=" + mDeferredRemoval
-                    + " mLayoutNeeded=" + mLayoutNeeded);
-            pw.println(" mTouchExcludeRegion=" + mTouchExcludeRegion);
+        pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
+        pw.print("dpi");
+        if (mInitialDisplayWidth != mBaseDisplayWidth
+                || mInitialDisplayHeight != mBaseDisplayHeight
+                || mInitialDisplayDensity != mBaseDisplayDensity) {
+            pw.print(" base=");
+            pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight);
+            pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi");
+        }
+        if (mDisplayScalingDisabled) {
+            pw.println(" noscale");
+        }
+        pw.print(" cur=");
+        pw.print(mDisplayInfo.logicalWidth);
+        pw.print("x"); pw.print(mDisplayInfo.logicalHeight);
+        pw.print(" app=");
+        pw.print(mDisplayInfo.appWidth);
+        pw.print("x"); pw.print(mDisplayInfo.appHeight);
+        pw.print(" rng="); pw.print(mDisplayInfo.smallestNominalAppWidth);
+        pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
+        pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
+        pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
+        pw.print(subPrefix + "deferred=" + mDeferredRemoval
+                + " mLayoutNeeded=" + mLayoutNeeded);
+        pw.println(" mTouchExcludeRegion=" + mTouchExcludeRegion);
 
         pw.println();
         pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
@@ -2841,7 +2854,7 @@
         }
         final WindowState win = getWindow(w ->
                 w.mAttrs.type == TYPE_TOAST && w.mOwnerUid == uid && !w.mPermanentlyHidden
-                && !w.mWindowRemovalAllowed);
+                        && !w.mWindowRemovalAllowed);
         return win == null;
     }
 
@@ -2921,7 +2934,7 @@
         }
 
         ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
-                    mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
+                mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
         final WindowState oldFocus = mCurrentFocus;
         mCurrentFocus = newFocus;
         mLosingFocus.remove(newFocus);
@@ -3223,7 +3236,7 @@
         // target app together.
         final boolean shouldAttachToDisplay = (mMagnificationSpec != null);
         final SurfaceControl newParent =
-                shouldAttachToDisplay ? mWindowingLayer : computeImeParent();
+                shouldAttachToDisplay ? mWindowContainers.getSurfaceControl() : computeImeParent();
         if (newParent != null) {
             getPendingTransaction().reparent(mImeWindowsContainers.mSurfaceControl, newParent);
             scheduleAnimation();
@@ -3248,39 +3261,7 @@
         }
 
         // Otherwise, we just attach it to the display.
-        return mWindowingLayer;
-    }
-
-    boolean getNeedsMenu(WindowState top, WindowManagerPolicy.WindowState bottom) {
-        if (top.mAttrs.needsMenuKey != NEEDS_MENU_UNSET) {
-            return top.mAttrs.needsMenuKey == NEEDS_MENU_SET_TRUE;
-        }
-
-        // Used to indicate we have reached the first window in the range we are interested in.
-        mTmpWindow = null;
-
-        // TODO: Figure-out a more efficient way to do this.
-        final WindowState candidate = getWindow(w -> {
-            if (w == top) {
-                // Reached the first window in the range we are interested in.
-                mTmpWindow = w;
-            }
-            if (mTmpWindow == null) {
-                return false;
-            }
-
-            if (w.mAttrs.needsMenuKey != NEEDS_MENU_UNSET) {
-                return true;
-            }
-            // If we reached the bottom of the range of windows we are considering,
-            // assume no menu is needed.
-            if (w == bottom) {
-                return true;
-            }
-            return false;
-        });
-
-        return candidate != null && candidate.mAttrs.needsMenuKey == NEEDS_MENU_SET_TRUE;
+        return mWindowContainers.getSurfaceControl();
     }
 
     void setLayoutNeeded() {
@@ -3395,7 +3376,7 @@
         boolean wallpaperEnabled = mWmService.mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_enableWallpaperService)
                 && mWmService.mContext.getResources().getBoolean(
-                        com.android.internal.R.bool.config_checkWallpaperAtBoot)
+                com.android.internal.R.bool.config_checkWallpaperAtBoot)
                 && !mWmService.mOnlyCore;
 
         final boolean haveBootMsg = drawnWindowTypes.get(TYPE_BOOT_PROGRESS);
@@ -3586,13 +3567,11 @@
             }
             if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
                     "after finishPostLayoutPolicyLw", pendingLayoutChanges);
-                mInsetsStateController.onPostLayout();
+            mInsetsStateController.onPostLayout();
         } while (pendingLayoutChanges != 0);
 
         mTmpApplySurfaceChangesTransactionState.reset();
 
-        mTmpRecoveringMemory = recoveringMemory;
-
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
         try {
             forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
@@ -3852,12 +3831,56 @@
     }
 
     static final class TaskForResizePointSearchResult {
-        boolean searchDone;
-        Task taskForResize;
+        private Task taskForResize;
+        private int x;
+        private int y;
+        private int delta;
+        private Rect mTmpRect = new Rect();
 
-        void reset() {
-            searchDone = false;
+        Task process(WindowContainer root, int x, int y, int delta) {
             taskForResize = null;
+            this.x = x;
+            this.y = y;
+            this.delta = delta;
+            mTmpRect.setEmpty();
+
+            final PooledFunction f = PooledLambda.obtainFunction(
+                    TaskForResizePointSearchResult::processTask, this, PooledLambda.__(Task.class));
+            root.forAllTasks(f);
+            f.recycle();
+
+            return taskForResize;
+        }
+
+        private boolean processTask(Task task) {
+            if (!task.getStack().getWindowConfiguration().canResizeTask()) {
+                return true;
+            }
+
+            if (task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+                return true;
+            }
+
+            // We need to use the task's dim bounds (which is derived from the visible bounds of
+            // its apps windows) for any touch-related tests. Can't use the task's original
+            // bounds because it might be adjusted to fit the content frame. One example is when
+            // the task is put to top-left quadrant, the actual visible area would not start at
+            // (0,0) after it's adjusted for the status bar.
+            task.getDimBounds(mTmpRect);
+            mTmpRect.inset(-delta, -delta);
+            if (mTmpRect.contains(x, y)) {
+                mTmpRect.inset(delta, delta);
+
+                if (!mTmpRect.contains(x, y)) {
+                    taskForResize = task;
+                    return true;
+                }
+                // User touched inside the task. No need to look further,
+                // focus transfer will be handled in ACTION_UP.
+                return true;
+            }
+
+            return false;
         }
     }
 
@@ -4297,7 +4320,7 @@
                 if (mHomeStack != null && mHomeStack.isVisible()
                         && mDividerControllerLocked.isMinimizedDock()
                         && !(mDividerControllerLocked.isHomeStackResizable()
-                            && mHomeStack.matchParentBounds())) {
+                        && mHomeStack.matchParentBounds())) {
                     final int orientation = mHomeStack.getOrientation();
                     if (orientation != SCREEN_ORIENTATION_UNSET) {
                         return orientation;
@@ -4310,14 +4333,14 @@
             if (orientation != SCREEN_ORIENTATION_UNSET
                     && orientation != SCREEN_ORIENTATION_BEHIND) {
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
-                                "App is requesting an orientation, return %d for display id=%d",
-                                orientation, mDisplayId);
+                        "App is requesting an orientation, return %d for display id=%d",
+                        orientation, mDisplayId);
                 return orientation;
             }
 
             ProtoLog.v(WM_DEBUG_ORIENTATION,
-                            "No app is requesting an orientation, return %d for display id=%d",
-                            getLastOrientation(), mDisplayId);
+                    "No app is requesting an orientation, return %d for display id=%d",
+                    getLastOrientation(), mDisplayId);
             // The next app has not been requested to be visible, so we keep the current orientation
             // to prevent freezing/unfreezing the display too early.
             return getLastOrientation();
@@ -4486,7 +4509,7 @@
                         wt.windowType, wt.mOwnerCanManageAppTokens);
 
                 if (needAssignIme && layer >= mWmService.mPolicy.getWindowLayerFromTypeLw(
-                                TYPE_INPUT_METHOD_DIALOG, true)) {
+                        TYPE_INPUT_METHOD_DIALOG, true)) {
                     imeContainer.assignRelativeLayer(t, wt.getSurfaceControl(), -1);
                     needAssignIme = false;
                 }
@@ -4497,6 +4520,126 @@
         }
     }
 
+    private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> {
+        private final String mName;
+
+        WindowContainers(String name, WindowManagerService service) {
+            super(service);
+            mName = name;
+        }
+
+        @Override
+        void assignChildLayers(SurfaceControl.Transaction t) {
+            mBelowAppWindowsContainers.assignLayer(t, 0);
+            mTaskStackContainers.assignLayer(t, 1);
+            mAboveAppWindowsContainers.assignLayer(t, 2);
+
+            final WindowState imeTarget = mInputMethodTarget;
+            boolean needAssignIme = true;
+
+            // In the case where we have an IME target that is not in split-screen mode IME
+            // assignment is easy. We just need the IME to go directly above the target. This way
+            // children of the target will naturally go above the IME and everyone is happy.
+            //
+            // In the case of split-screen windowing mode, we need to elevate the IME above the
+            // docked divider while keeping the app itself below the docked divider, so instead
+            // we use relative layering of the IME targets child windows, and place the IME in
+            // the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}).
+            //
+            // In the case the IME target is animating, the animation Z order may be different
+            // than the WindowContainer Z order, so it's difficult to be sure we have the correct
+            // IME target. In this case we just layer the IME over all transitions by placing it
+            // in the above applications layer.
+            //
+            // In the case where we have no IME target we assign it where its base layer would
+            // place it in the AboveAppWindowContainers.
+            //
+            // Keep IME window in mAboveAppWindowsContainers as long as app's starting window
+            // exists so it get's layered above the starting window.
+            if (imeTarget != null && !(imeTarget.mActivityRecord != null
+                    && imeTarget.mActivityRecord.hasStartingWindow()) && (
+                    !(imeTarget.inSplitScreenWindowingMode()
+                            || imeTarget.mToken.isAppTransitioning()) && (
+                            imeTarget.getSurfaceControl() != null))) {
+                mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
+                        // TODO: We need to use an extra level on the app surface to ensure
+                        // this is always above SurfaceView but always below attached window.
+                        1);
+                needAssignIme = false;
+            }
+
+            // Above we have assigned layers to our children, now we ask them to assign
+            // layers to their children.
+            mBelowAppWindowsContainers.assignChildLayers(t);
+            mTaskStackContainers.assignChildLayers(t);
+            mAboveAppWindowsContainers.assignChildLayers(t,
+                    needAssignIme ? mImeWindowsContainers : null);
+            mImeWindowsContainers.assignChildLayers(t);
+        }
+
+        @Override
+        String getName() {
+            return mName;
+        }
+
+        void addChildren() {
+            addChild(mBelowAppWindowsContainers, null);
+            addChild(mTaskStackContainers, null);
+            addChild(mAboveAppWindowsContainers, null);
+            addChild(mImeWindowsContainers, null);
+        }
+
+        /**
+         * In split-screen mode we process the IME containers above the docked divider
+         * rather than directly above their target.
+         */
+        private boolean skipTraverseChild(WindowContainer child) {
+            return child == mImeWindowsContainers && mInputMethodTarget != null
+                    && !hasSplitScreenPrimaryStack();
+        }
+
+        @Override
+        boolean forAllWindows(ToBooleanFunction<WindowState> callback,
+                boolean traverseTopToBottom) {
+            // Special handling so we can process IME windows with #forAllImeWindows above their IME
+            // target, or here in order if there isn't an IME target.
+            if (traverseTopToBottom) {
+                for (int i = mChildren.size() - 1; i >= 0; --i) {
+                    final WindowContainer child = mChildren.get(i);
+                    if (skipTraverseChild(child)) {
+                        continue;
+                    }
+
+                    if (child.forAllWindows(callback, traverseTopToBottom)) {
+                        return true;
+                    }
+                }
+            } else {
+                final int count = mChildren.size();
+                for (int i = 0; i < count; i++) {
+                    Slog.d(TAG, "child " + mChildren.get(i));
+                    final WindowContainer child = mChildren.get(i);
+                    if (skipTraverseChild(child)) {
+                        Slog.d(TAG, "child skipped");
+                        continue;
+                    }
+
+                    if (child.forAllWindows(callback, traverseTopToBottom)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        @Override
+        void positionChildAt(int position, WindowContainer child, boolean includingParents) {
+            // Children of the WindowContainers are statically ordered, so the real intention here
+            // is to perform the operation on the display and not the static direct children.
+            getParent().positionChildAt(position, this, includingParents);
+        }
+    }
+
     /**
      * Window container class that contains all containers on this display that are not related to
      * Apps. E.g. status bar.
@@ -4510,7 +4653,7 @@
                 // Tokens with higher base layer are z-ordered on-top.
                 mWmService.mPolicy.getWindowLayerFromTypeLw(token1.windowType,
                         token1.mOwnerCanManageAppTokens)
-                < mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
+                        < mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
                         token2.mOwnerCanManageAppTokens) ? -1 : 1;
 
         private final Predicate<WindowState> mGetOrientingWindow = w -> {
@@ -4562,7 +4705,7 @@
                 }
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "%s forcing orientation to %d for display id=%d", win, req,
-                                mDisplayId);
+                        mDisplayId);
                 return (mLastWindowForcedOrientation = req);
             }
 
@@ -4602,11 +4745,6 @@
         }
     }
 
-    SurfaceControl.Builder makeSurface(SurfaceSession s) {
-        return mWmService.makeSurfaceBuilder(s)
-                .setParent(mWindowingLayer);
-    }
-
     @Override
     SurfaceSession getSession() {
         return mSession;
@@ -4621,7 +4759,7 @@
         }
 
         return b.setName(child.getName())
-                .setParent(mWindowingLayer);
+                .setParent(mSurfaceControl);
     }
 
     /**
@@ -4632,14 +4770,14 @@
      */
     SurfaceControl.Builder makeOverlay() {
         return mWmService.makeSurfaceBuilder(mSession)
-            .setParent(mOverlayLayer);
+                .setParent(mOverlayContainers.getSurfaceControl());
     }
 
     /**
-     * Reparents the given surface to mOverlayLayer.
+     * Reparents the given surface to {@link #mOverlayContainers}' SurfaceControl.
      */
     void reparentToOverlay(Transaction transaction, SurfaceControl surface) {
-        transaction.reparent(surface, mOverlayLayer);
+        transaction.reparent(surface, mOverlayContainers.getSurfaceControl());
     }
 
     void applyMagnificationSpec(MagnificationSpec spec) {
@@ -4671,54 +4809,11 @@
 
     @Override
     void assignChildLayers(SurfaceControl.Transaction t) {
+        mWindowContainers.assignLayer(t, 0);
+        mOverlayContainers.assignLayer(t, 1);
 
-        // These are layers as children of "mWindowingLayer"
-        mBelowAppWindowsContainers.assignLayer(t, 0);
-        mTaskStackContainers.assignLayer(t, 1);
-        mAboveAppWindowsContainers.assignLayer(t, 2);
-
-        final WindowState imeTarget = mInputMethodTarget;
-        boolean needAssignIme = true;
-
-        // In the case where we have an IME target that is not in split-screen
-        // mode IME assignment is easy. We just need the IME to go directly above
-        // the target. This way children of the target will naturally go above the IME
-        // and everyone is happy.
-        //
-        // In the case of split-screen windowing mode, we need to elevate the IME above the
-        // docked divider while keeping the app itself below the docked divider, so instead
-        // we use relative layering of the IME targets child windows, and place the
-        // IME in the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}).
-        //
-        // In the case the IME target is animating, the animation Z order may be different
-        // than the WindowContainer Z order, so it's difficult to be sure we have the correct
-        // IME target. In this case we just layer the IME over all transitions by placing it in the
-        // above applications layer.
-        //
-        // In the case where we have no IME target we assign it where it's base layer would
-        // place it in the AboveAppWindowContainers.
-        //
-        // Keep IME window in mAboveAppWindowsContainers as long as app's starting window exists
-        // so it get's layered above the starting window.
-        if (imeTarget != null
-                && !(imeTarget.mActivityRecord != null && imeTarget.mActivityRecord.hasStartingWindow())
-                && (!(imeTarget.inSplitScreenWindowingMode()
-                             || imeTarget.mToken.isAppTransitioning())
-                && (imeTarget.getSurfaceControl() != null))) {
-            mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
-                    // TODO: We need to use an extra level on the app surface to ensure
-                    // this is always above SurfaceView but always below attached window.
-                    1);
-            needAssignIme = false;
-        }
-
-        // Above we have assigned layers to our children, now we ask them to assign
-        // layers to their children.
-        mBelowAppWindowsContainers.assignChildLayers(t);
-        mTaskStackContainers.assignChildLayers(t);
-        mAboveAppWindowsContainers.assignChildLayers(t,
-                needAssignIme == true ? mImeWindowsContainers : null);
-        mImeWindowsContainers.assignChildLayers(t);
+        mWindowContainers.assignChildLayers(t);
+        mOverlayContainers.assignChildLayers(t);
     }
 
     /**
@@ -4820,7 +4915,7 @@
         if (mAppTransition.isTransitionSet()) {
             ProtoLog.w(WM_DEBUG_APP_TRANSITIONS,
                     "Execute app transition: %s, displayId: %d Callers=%s",
-                        mAppTransition, mDisplayId, Debug.getCallers(5));
+                    mAppTransition, mDisplayId, Debug.getCallers(5));
             mAppTransition.setReady();
             mWmService.mWindowPlacerLocked.requestTraversal();
         }
@@ -4885,8 +4980,8 @@
     }
 
     /**
-     * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
-     * {@link #mOverlayLayer} to the specified SurfaceControl.
+     * Re-parent the DisplayContent's top surface, {@link #mSurfaceControl} to the specified
+     * SurfaceControl.
      *
      * @param win The window which owns the SurfaceControl. This indicates the z-order of the
      *            windows of this display against the windows on the parent display.
@@ -4964,11 +5059,11 @@
 
     @VisibleForTesting
     SurfaceControl getWindowingLayer() {
-        return mWindowingLayer;
+        return mWindowContainers.getSurfaceControl();
     }
 
     SurfaceControl getOverlayLayer() {
-        return mOverlayLayer;
+        return mOverlayContainers.getSurfaceControl();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 1a1a7d4..6b5859d 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -259,7 +259,7 @@
         if (homeStack == null) {
             return false;
         }
-        final Task homeTask = homeStack.findHomeTask();
+        final Task homeTask = homeStack.getTopMostTask();
         return homeTask != null && homeTask.isResizeable();
     }
 
@@ -708,7 +708,7 @@
         if (homeStack == null) {
             return;
         }
-        final Task homeTask = homeStack.findHomeTask();
+        final Task homeTask = homeStack.getTopMostTask();
         if (homeTask == null || !isWithinDisplay(homeTask)) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 3aae1b1..949ff19 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -45,7 +45,7 @@
     void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
             boolean notifyClients) {
         mStarting = starting;
-        mTop = mContiner.topRunningActivityLocked();
+        mTop = mContiner.topRunningActivity();
         // If the top activity is not fullscreen, then we need to make sure any activities under it
         // are now visible.
         mAboveTop = mTop != null;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index ac5c96b..3a0696f 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -494,7 +494,7 @@
             if (stack != null) {
                 final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
                 mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null
-                        && stack.topRunningActivityLocked() == topDismissing
+                        && stack.topRunningActivity() == topDismissing
                         && controller.canShowWhileOccluded(
                                 true /* dismissKeyguard */,
                                 false /* showWhenLocked */));
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 2ece6e2..7a72b43 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -150,7 +150,7 @@
      * The first task in the list, which started the current LockTask session, is called the root
      * task. It coincides with the Home task in a typical multi-app kiosk deployment. When there are
      * more than one locked tasks, the root task can't be finished. Nor can it be moved to the back
-     * of the stack by {@link ActivityStack#moveTaskToBackLocked(int)};
+     * of the stack by {@link ActivityStack#moveTaskToBack(Task)};
      *
      * Calling {@link Activity#stopLockTask()} on the root task will finish all tasks but itself in
      * this list, and the device will exit LockTask mode.
@@ -251,7 +251,7 @@
 
     /**
      * @return whether the given task can be moved to the back of the stack with
-     * {@link ActivityStack#moveTaskToBackLocked(int)}
+     * {@link ActivityStack#moveTaskToBack(Task)}
      * @see #mLockTaskModeTasks
      */
     boolean canMoveTaskToBack(Task task) {
@@ -653,10 +653,7 @@
             taskChanged = true;
         }
 
-        for (int displayNdx = mSupervisor.mRootActivityContainer.getChildCount() - 1;
-             displayNdx >= 0; --displayNdx) {
-            mSupervisor.mRootActivityContainer.getChildAt(displayNdx).onLockTaskPackagesUpdated();
-        }
+        mSupervisor.mRootActivityContainer.mRootWindowContainer.forAllTasks(Task::setLockTaskAuth);
 
         final ActivityRecord r = mSupervisor.mRootActivityContainer.topRunningActivity();
         final Task task = (r != null) ? r.getTask() : null;
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index f0e441f..e199b28 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -211,7 +211,7 @@
                     final DisplayContent dc = rac.getActivityDisplay(displayId).mDisplayContent;
                     if (dc.pointWithinAppWindow(x, y)) {
                         final ActivityStack stack = mService.getTopDisplayFocusedStack();
-                        final Task topTask = stack != null ? stack.topTask() : null;
+                        final Task topTask = stack != null ? stack.getTopMostTask() : null;
                         resetFreezeTaskListReordering(topTask);
                     }
                 }
@@ -319,7 +319,7 @@
     void resetFreezeTaskListReorderingOnTimeout() {
         synchronized (mService.mGlobalLock) {
             final ActivityStack focusedStack = mService.getTopDisplayFocusedStack();
-            final Task topTask = focusedStack != null ? focusedStack.topTask() : null;
+            final Task topTask = focusedStack != null ? focusedStack.getTopMostTask() : null;
             resetFreezeTaskListReordering(topTask);
         }
     }
@@ -630,8 +630,7 @@
                     && task.realActivitySuspended != suspended) {
                task.realActivitySuspended = suspended;
                if (suspended) {
-                   mSupervisor.removeTaskByIdLocked(task.mTaskId, false,
-                           REMOVE_FROM_RECENTS, "suspended-package");
+                   mSupervisor.removeTask(task, false, REMOVE_FROM_RECENTS, "suspended-package");
                }
                notifyTaskPersisterLocked(task, false);
             }
@@ -658,8 +657,7 @@
             if (task.mUserId != userId) continue;
             if (!taskPackageName.equals(packageName)) continue;
 
-            mSupervisor.removeTaskByIdLocked(task.mTaskId, true, REMOVE_FROM_RECENTS,
-                    "remove-package-task");
+            mSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-package-task");
         }
     }
 
@@ -687,8 +685,7 @@
             final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
                     && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
             if (sameComponent) {
-                mSupervisor.removeTaskByIdLocked(task.mTaskId, false,
-                        REMOVE_FROM_RECENTS, "disabled-package");
+                mSupervisor.removeTask(task, false, REMOVE_FROM_RECENTS, "disabled-package");
             }
         }
     }
@@ -1147,6 +1144,7 @@
 
         // Trim the set of tasks to the active set
         trimInactiveRecentTasks();
+        notifyTaskPersisterLocked(task, false /* flush */);
     }
 
     /**
@@ -1306,9 +1304,9 @@
             case WINDOWING_MODE_PINNED:
                 return false;
             case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().topTask());
+                if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().getTopMostTask());
                 final ActivityStack stack = task.getStack();
-                if (stack != null && stack.topTask() == task) {
+                if (stack != null && stack.getTopMostTask() == task) {
                     // Only the non-top task of the primary split screen mode is visible
                     return false;
                 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index d5bbe6b..b398626 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -216,7 +216,7 @@
                 // and default launchers coexisting), then move the task to the top as a part of
                 // moving the stack to the front
                 final Task task = targetActivity.getTask();
-                if (targetStack.topTask() != task) {
+                if (targetStack.getTopMostTask() != task) {
                     targetStack.positionChildAtTop(task);
                 }
             } else {
@@ -432,7 +432,7 @@
         // cases:
         // 1) The next launching task is not being animated by the recents animation
         // 2) The next task is home activity. (i.e. pressing home key to back home in recents).
-        if ((!controller.isAnimatingTask(stack.getTopChild())
+        if ((!controller.isAnimatingTask(stack.getTopMostTask())
                 || controller.isTargetApp(stack.getTopNonFinishingActivity()))
                 && controller.shouldDeferCancelUntilNextTransition()) {
             // Always prepare an app transition since we rely on the transition callbacks to cleanup
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 5653ec0..704a4b7 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -129,6 +129,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.Function;
 
 /**
  * Root node for activity containers.
@@ -168,8 +169,8 @@
     WindowManagerService mWindowManager;
     DisplayManager mDisplayManager;
     private DisplayManagerInternal mDisplayManagerInternal;
-    // TODO: Remove after object merge with RootWindowContainer.
-    private RootWindowContainer mRootWindowContainer;
+    // TODO(root-unify): Remove after object merge with RootWindowContainer.
+    RootWindowContainer mRootWindowContainer;
 
     /**
      * List of displays which contain activities, sorted by z-order.
@@ -214,7 +215,7 @@
     private RemoteException mTmpRemoteException;
 
     private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
-    static class FindTaskResult implements ToBooleanFunction<Task> {
+    static class FindTaskResult implements Function<Task, Boolean> {
         ActivityRecord mRecord;
         boolean mIdealMatch;
 
@@ -259,7 +260,7 @@
         }
 
         @Override
-        public boolean apply(Task task) {
+        public Boolean apply(Task task) {
             if (task.voiceSession != null) {
                 // We never match voice sessions; those always run independently.
                 if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": voice session");
@@ -899,7 +900,7 @@
             mTmpBoolean = false; // Set to true if an activity was started.
             final PooledFunction c = PooledLambda.obtainFunction(
                     RootActivityContainer::startActivityForAttachedApplicationIfNeeded, this,
-                    PooledLambda.__(ActivityRecord.class), app, stack.topRunningActivityLocked());
+                    PooledLambda.__(ActivityRecord.class), app, stack.topRunningActivity());
             stack.forAllActivities(c);
             c.recycle();
             if (mTmpRemoteException != null) {
@@ -992,7 +993,7 @@
             for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = display.getStackAt(stackNdx);
                 stack.switchUser(userId);
-                Task task = stack.topTask();
+                Task task = stack.getTopMostTask();
                 if (task != null) {
                     stack.positionChildAtTop(task);
                 }
@@ -1075,7 +1076,7 @@
                     "moveTopStackActivityToPinnedStack: Unknown stackId=" + stackId);
         }
 
-        final ActivityRecord r = stack.topRunningActivityLocked();
+        final ActivityRecord r = stack.topRunningActivity();
         if (r == null) {
             Slog.w(TAG, "moveTopStackActivityToPinnedStack: No top running activity"
                     + " in stack=" + stack);
@@ -1263,7 +1264,7 @@
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
             for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = display.getStackAt(stackNdx);
-                final ActivityRecord topRunningActivity = stack.topRunningActivityLocked();
+                final ActivityRecord topRunningActivity = stack.topRunningActivity();
                 if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
                     continue;
                 }
@@ -1401,32 +1402,37 @@
         info.position = display != null ? display.getIndexOf(stack) : 0;
         info.configuration.setTo(stack.getConfiguration());
 
-        ArrayList<Task> tasks = stack.getAllTasks();
-        final int numTasks = tasks.size();
-        int[] taskIds = new int[numTasks];
-        String[] taskNames = new String[numTasks];
-        Rect[] taskBounds = new Rect[numTasks];
-        int[] taskUserIds = new int[numTasks];
-        for (int i = 0; i < numTasks; ++i) {
-            final Task task = tasks.get(i);
-            taskIds[i] = task.mTaskId;
-            taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
-                    : task.realActivity != null ? task.realActivity.flattenToString()
-                    : task.getTopNonFinishingActivity() != null
-                            ? task.getTopNonFinishingActivity().packageName : "unknown";
-            taskBounds[i] = mService.getTaskBounds(task.mTaskId);
-            taskUserIds[i] = task.mUserId;
-        }
-        info.taskIds = taskIds;
-        info.taskNames = taskNames;
-        info.taskBounds = taskBounds;
-        info.taskUserIds = taskUserIds;
+        final int numTasks = stack.getChildCount();
+        info.taskIds = new int[numTasks];
+        info.taskNames = new String[numTasks];
+        info.taskBounds = new Rect[numTasks];
+        info.taskUserIds = new int[numTasks];
+        final int[] currenIndex = {0};
 
-        final ActivityRecord top = stack.topRunningActivityLocked();
+        final PooledConsumer c = PooledLambda.obtainConsumer(
+                RootActivityContainer::processTaskForStackInfo, PooledLambda.__(Task.class), info,
+                currenIndex);
+        stack.forAllTasks(c, false);
+        c.recycle();
+
+        final ActivityRecord top = stack.topRunningActivity();
         info.topActivity = top != null ? top.intent.getComponent() : null;
         return info;
     }
 
+    private static void processTaskForStackInfo(
+            Task task, ActivityManager.StackInfo info, int[] currentIndex) {
+        int i = currentIndex[0];
+        info.taskIds[i] = task.mTaskId;
+        info.taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
+                : task.realActivity != null ? task.realActivity.flattenToString()
+                        : task.getTopNonFinishingActivity() != null
+                                ? task.getTopNonFinishingActivity().packageName : "unknown";
+        info.taskBounds[i] = task.mAtmService.getTaskBounds(task.mTaskId);
+        info.taskUserIds[i] = task.mUserId;
+        currentIndex[0] = ++i;
+    }
+
     ActivityManager.StackInfo getStackInfo(int stackId) {
         ActivityStack stack = getStack(stackId);
         if (stack != null) {
@@ -1905,7 +1911,7 @@
                 }
                 if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
                         && display.getSplitScreenPrimaryStack() == stack
-                        && candidateTask == stack.topTask()) {
+                        && candidateTask == stack.getTopMostTask()) {
                     // This is a special case when we try to launch an activity that is currently on
                     // top of split-screen primary stack, but is targeting split-screen secondary.
                     // In this case we don't want to move it to another stack.
@@ -2338,24 +2344,11 @@
     void lockAllProfileTasks(@UserIdInt int userId) {
         mService.deferWindowLayout();
         try {
-            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getStackAt(stackNdx);
-                    final List<Task> tasks = stack.getAllTasks();
-                    for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
-                        final Task task = tasks.get(taskNdx);
-
-                        // Check the task for a top activity belonging to userId, or returning a
-                        // result to an activity belonging to userId. Example case: a document
-                        // picker for personal files, opened by a work app, should still get locked.
-                        if (taskTopActivityIsUser(task, userId)) {
-                            mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
-                                    task.mTaskId, userId);
-                        }
-                    }
-                }
-            }
+            final PooledConsumer c = PooledLambda.obtainConsumer(
+                    RootActivityContainer::taskTopActivityIsUser, this, PooledLambda.__(Task.class),
+                    userId);
+            mRootWindowContainer.forAllTasks(c);
+            c.recycle();
         } finally {
             mService.continueWindowLayout();
         }
@@ -2372,13 +2365,19 @@
      *
      * @return {@code true} if the top activity looks like it belongs to {@param userId}.
      */
-    private boolean taskTopActivityIsUser(Task task, @UserIdInt int userId) {
+    private void taskTopActivityIsUser(Task task, @UserIdInt int userId) {
         // To handle the case that work app is in the task but just is not the top one.
         final ActivityRecord activityRecord = task.getTopNonFinishingActivity();
         final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
 
-        return (activityRecord != null && activityRecord.mUserId == userId)
-                || (resultTo != null && resultTo.mUserId == userId);
+        // Check the task for a top activity belonging to userId, or returning a
+        // result to an activity belonging to userId. Example case: a document
+        // picker for personal files, opened by a work app, should still get locked.
+        if ((activityRecord != null && activityRecord.mUserId == userId)
+                || (resultTo != null && resultTo.mUserId == userId)) {
+            mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
+                    task.mTaskId, userId);
+        }
     }
 
     void cancelInitializingActivities() {
@@ -2414,29 +2413,25 @@
                     + " lookup");
         }
 
-        int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                final Task task = stack.taskForIdLocked(id);
-                if (task == null) {
-                    continue;
+        final PooledPredicate p = PooledLambda.obtainPredicate(
+                Task::isTaskId, PooledLambda.__(Task.class), id);
+        Task task = mRootWindowContainer.getTask(p);
+        p.recycle();
+
+        if (task != null) {
+            if (aOptions != null) {
+                // Resolve the stack the task should be placed in now based on options
+                // and reparent if needed.
+                final ActivityStack launchStack =
+                        getLaunchStack(null, aOptions, task, onTop);
+                if (launchStack != null && task.getStack() != launchStack) {
+                    final int reparentMode = onTop
+                            ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
+                    task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
+                            "anyTaskForId");
                 }
-                if (aOptions != null) {
-                    // Resolve the stack the task should be placed in now based on options
-                    // and reparent if needed.
-                    final ActivityStack launchStack =
-                            getLaunchStack(null, aOptions, task, onTop);
-                    if (launchStack != null && stack != launchStack) {
-                        final int reparentMode = onTop
-                                ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
-                        task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
-                                "anyTaskForId");
-                    }
-                }
-                return task;
             }
+            return task;
         }
 
         // If we are matching stack tasks only, return now
@@ -2447,7 +2442,7 @@
         // Otherwise, check the recent tasks and return if we find it there and we are not restoring
         // the task from recents
         if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
-        final Task task = mStackSupervisor.mRecentTasks.getTask(id);
+        task = mStackSupervisor.mRecentTasks.getTask(id);
 
         if (task == null) {
             if (DEBUG_RECENTS) {
@@ -2492,7 +2487,7 @@
             @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
             boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) {
         mStackSupervisor.getRunningTasks().getTasks(maxNum, list, ignoreActivityType,
-                ignoreWindowingMode, mActivityDisplays, callingUid, allowed, crossUser, profileIds);
+                ignoreWindowingMode, this, callingUid, allowed, crossUser, profileIds);
     }
 
     void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index ca9d91e..5783713 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -16,11 +16,18 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.WindowConfiguration.ActivityType;
 import android.app.WindowConfiguration.WindowingMode;
+import android.os.UserHandle;
 import android.util.ArraySet;
 
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
+
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.Iterator;
@@ -39,8 +46,17 @@
     private final TreeSet<Task> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR);
     private final ArrayList<Task> mTmpStackTasks = new ArrayList<>();
 
+    private int mCallingUid;
+    private int mUserId;
+    private boolean mCrossUser;
+    private ArraySet<Integer> mProfileIds;
+    private boolean mAllowed;
+    private int mIgnoreActivityType;
+    private int mIgnoreWindowingMode;
+    private ActivityStack mTopDisplayFocusStack;
+
     void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType,
-            @WindowingMode int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
+            @WindowingMode int ignoreWindowingMode, RootActivityContainer root,
             int callingUid, boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) {
         // Return early if there are no tasks to fetch
         if (maxNum <= 0) {
@@ -49,17 +65,19 @@
 
         // Gather all of the tasks across all of the tasks, and add them to the sorted set
         mTmpSortedSet.clear();
-        final int numDisplays = activityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = activityDisplays.get(displayNdx);
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                mTmpStackTasks.clear();
-                stack.getRunningTasks(mTmpStackTasks, ignoreActivityType, ignoreWindowingMode,
-                        callingUid, allowed, crossUser, profileIds);
-                mTmpSortedSet.addAll(mTmpStackTasks);
-            }
-        }
+        mCallingUid = callingUid;
+        mUserId = UserHandle.getUserId(callingUid);
+        mCrossUser = crossUser;
+        mProfileIds = profileIds;
+        mAllowed = allowed;
+        mIgnoreActivityType = ignoreActivityType;
+        mIgnoreWindowingMode = ignoreWindowingMode;
+        mTopDisplayFocusStack = root.getTopDisplayFocusedStack();
+
+        final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
+                PooledLambda.__(Task.class));
+        root.mRootWindowContainer.forAllTasks(c, false);
+        c.recycle();
 
         // Take the first {@param maxNum} tasks and create running task infos for them
         final Iterator<Task> iter = mTmpSortedSet.iterator();
@@ -74,9 +92,45 @@
         }
     }
 
-    /**
-     * Constructs a {@link RunningTaskInfo} from a given {@param task}.
-     */
+    private void processTask(Task task) {
+        if (task.getTopNonFinishingActivity() == null) {
+            // Skip if there are no activities in the task
+            return;
+        }
+        if (task.effectiveUid != mCallingUid) {
+            if (task.mUserId != mUserId && !mCrossUser && !mProfileIds.contains(task.mUserId)) {
+                // Skip if the caller does not have cross user permission or cannot access
+                // the task's profile
+                return;
+            }
+            if (!mAllowed && !task.isActivityTypeHome()) {
+                // Skip if the caller isn't allowed to fetch this task, except for the home
+                // task which we always return.
+                return;
+            }
+        }
+        if (mIgnoreActivityType != ACTIVITY_TYPE_UNDEFINED
+                && task.getActivityType() == mIgnoreActivityType) {
+            // Skip ignored activity type
+            return;
+        }
+        if (mIgnoreWindowingMode != WINDOWING_MODE_UNDEFINED
+                && task.getWindowingMode() == mIgnoreWindowingMode) {
+            // Skip ignored windowing mode
+            return;
+        }
+
+        final ActivityStack stack = task.getStack();
+        if (stack == mTopDisplayFocusStack && stack.getTopMostTask() == task) {
+            // For the focused stack top task, update the last stack active time so that it can be
+            // used to determine the order of the tasks (it may not be set for newly created tasks)
+            task.touchActiveTime();
+        }
+
+        mTmpSortedSet.add(task);
+    }
+
+    /** Constructs a {@link RunningTaskInfo} from a given {@param task}. */
     private RunningTaskInfo createRunningTaskInfo(Task task) {
         final RunningTaskInfo rti = new RunningTaskInfo();
         task.fillTaskInfo(rti);
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 1a7d214..399c5d3 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -172,7 +172,7 @@
                     .setContainerLayer()
                     .build();
 
-            mSurfaceControl = displayContent.makeSurface(null)
+            mSurfaceControl = mService.makeSurfaceBuilder(null)
                     .setName("ScreenshotSurface")
                     .setParent(mRotationLayer)
                     .setBufferSize(mWidth, mHeight)
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b1d0692..0e07e52 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -155,6 +155,7 @@
 import java.util.ArrayList;
 import java.util.Objects;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 class Task extends WindowContainer<WindowContainer> {
@@ -684,7 +685,7 @@
 
         final boolean toTopOfStack = position == MAX_VALUE;
         if (toTopOfStack && toStack.getResumedActivity() != null
-                && toStack.topRunningActivityLocked() != null) {
+                && toStack.topRunningActivity() != null) {
             // Pause the resumed activity on the target stack while re-parenting task on top of it.
             toStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
                     null /* resuming */);
@@ -720,7 +721,7 @@
             // Whenever we are moving the top activity from the front stack we want to make sure to
             // move the stack to the front.
             final boolean wasFront = r != null && sourceStack.isTopStackOnDisplay()
-                    && (sourceStack.topRunningActivityLocked() == r);
+                    && (sourceStack.topRunningActivity() == r);
 
             final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
                     || (moveStackMode == REPARENT_KEEP_STACK_AT_FRONT && (wasFocused || wasFront));
@@ -1256,7 +1257,7 @@
                 // work.
                 // TODO: If the callers to removeChild() changes such that we have multiple places
                 //       where we are destroying the task, move this back into removeChild()
-                mAtmService.mStackSupervisor.removeTaskByIdLocked(mTaskId, false /* killProcess */,
+                mAtmService.mStackSupervisor.removeTask(this, false /* killProcess */,
                         !REMOVE_FROM_RECENTS, reason);
             }
         } else if (!mReuseTask) {
@@ -2659,12 +2660,13 @@
     }
 
     @Override
-    void forAllTasks(Consumer<Task> callback) {
+    void forAllTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
+        // TODO(task-hierarchy): Change to traverse children when tasks can contain other tasks.
         callback.accept(this);
     }
 
     @Override
-    boolean forAllTasks(ToBooleanFunction<Task> callback) {
+    boolean forAllTasks(Function<Task, Boolean> callback) {
         return callback.apply(this);
     }
 
@@ -2707,6 +2709,10 @@
         return mDimmer;
     }
 
+    boolean isTaskForUser(int userId) {
+        return mUserId == userId;
+    }
+
     @Override
     void prepareSurfaces() {
         mDimmer.resetDimStates();
@@ -2800,6 +2806,10 @@
         return info;
     }
 
+    boolean isTaskId(int taskId) {
+        return mTaskId == taskId;
+    }
+
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("userId="); pw.print(mUserId);
         pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 5915590..10f2996 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -54,7 +54,6 @@
     private static final String REDUCED_POSTFIX = "_reduced";
     private static final float REDUCED_SCALE = .5f;
     private static final float LOW_RAM_REDUCED_SCALE = .6f;
-    private static final float LOW_RAM_RECENTS_REDUCED_SCALE = .1f;
     static final boolean DISABLE_FULL_SIZED_BITMAPS = ActivityManager.isLowRamDeviceStatic();
     private static final long DELAY_MS = 100;
     private static final int QUALITY = 95;
@@ -85,14 +84,8 @@
 
     TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
         mDirectoryResolver = resolver;
-        if (service.mLowRamTaskSnapshotsAndRecents) {
-            // Use very low res snapshots if we are using Go version of recents.
-            mReducedScale = LOW_RAM_RECENTS_REDUCED_SCALE;
-        } else {
-            // TODO(122671846) Replace the low RAM value scale with the above when it is fully built
-            mReducedScale = ActivityManager.isLowRamDeviceStatic()
-                    ? LOW_RAM_REDUCED_SCALE : REDUCED_SCALE;
-        }
+        mReducedScale = ActivityManager.isLowRamDeviceStatic()
+                ? LOW_RAM_REDUCED_SCALE : REDUCED_SCALE;
         mUse16BitFormat = service.mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat);
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e80f3b8..d73cb50f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1202,9 +1202,17 @@
     }
 
     ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom) {
+        return getActivity(callback, traverseTopToBottom, null /*boundary*/);
+    }
+
+    ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom,
+            WindowContainer boundary) {
         if (traverseTopToBottom) {
             for (int i = mChildren.size() - 1; i >= 0; --i) {
-                final ActivityRecord r = mChildren.get(i).getActivity(callback, traverseTopToBottom);
+                final WindowContainer wc = mChildren.get(i);
+                if (wc == boundary) return null;
+
+                final ActivityRecord r = wc.getActivity(callback, traverseTopToBottom, boundary);
                 if (r != null) {
                     return r;
                 }
@@ -1212,7 +1220,10 @@
         } else {
             final int count = mChildren.size();
             for (int i = 0; i < count; i++) {
-                final ActivityRecord r = mChildren.get(i).getActivity(callback, traverseTopToBottom);
+                final WindowContainer wc = mChildren.get(i);
+                if (wc == boundary) return null;
+
+                final ActivityRecord r = wc.getActivity(callback, traverseTopToBottom, boundary);
                 if (r != null) {
                     return r;
                 }
@@ -1320,14 +1331,57 @@
     /**
      * For all tasks at or below this container call the callback.
      *
+     * @param callback Calls the {@link ToBooleanFunction#apply} method for each task found and
+     *                 stops the search if {@link ToBooleanFunction#apply} returns {@code true}.
+     */
+    boolean forAllTasks(Function<Task, Boolean> callback) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            if (mChildren.get(i).forAllTasks(callback)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * For all tasks at or below this container call the callback.
+     *
      * @param callback Callback to be called for every task.
      */
     void forAllTasks(Consumer<Task> callback) {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            mChildren.get(i).forAllTasks(callback);
+        forAllTasks(callback, true /*traverseTopToBottom*/);
+    }
+
+    void forAllTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
+        final int count = mChildren.size();
+        if (traverseTopToBottom) {
+            for (int i = count - 1; i >= 0; --i) {
+                mChildren.get(i).forAllTasks(callback, traverseTopToBottom);
+            }
+        } else {
+            for (int i = 0; i < count; i++) {
+                mChildren.get(i).forAllTasks(callback, traverseTopToBottom);
+            }
         }
     }
 
+    Task getTaskAbove(Task t) {
+        return getTask(
+                (above) -> true, t, false /*includeBoundary*/, false /*traverseTopToBottom*/);
+    }
+
+    Task getTaskBelow(Task t) {
+        return getTask((below) -> true, t, false /*includeBoundary*/, true /*traverseTopToBottom*/);
+    }
+
+    Task getBottomMostTask() {
+        return getTask((t) -> true, false /*traverseTopToBottom*/);
+    }
+
+    Task getTopMostTask() {
+        return getTask((t) -> true, true /*traverseTopToBottom*/);
+    }
+
     Task getTask(Predicate<Task> callback) {
         return getTask(callback, true /*traverseTopToBottom*/);
     }
@@ -1354,18 +1408,59 @@
     }
 
     /**
-     * For all tasks at or below this container call the callback.
+     * Gets an task in a branch of the tree.
      *
-     * @param callback Calls the {@link ToBooleanFunction#apply} method for each task found and
-     *                 stops the search if {@link ToBooleanFunction#apply} returns {@code true}.
+     * @param callback called to test if this is the task that should be returned.
+     * @param boundary We don't return tasks via {@param callback} until we get to this node in
+     *                 the tree.
+     * @param includeBoundary If the boundary from be processed to return tasks.
+     * @param traverseTopToBottom direction to traverse the tree.
+     * @return The task if found or null.
      */
-    boolean forAllTasks(ToBooleanFunction<Task> callback) {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            if (mChildren.get(i).forAllTasks(callback)) {
-                return true;
+    final Task getTask(Predicate<Task> callback, WindowContainer boundary, boolean includeBoundary,
+            boolean traverseTopToBottom) {
+        return getTask(callback, boundary, includeBoundary, traverseTopToBottom, new boolean[1]);
+    }
+
+    private Task getTask(Predicate<Task> callback,
+            WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
+            boolean[] boundaryFound) {
+        if (traverseTopToBottom) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final Task t = processGetTaskWithBoundary(callback, boundary,
+                        includeBoundary, traverseTopToBottom, boundaryFound, mChildren.get(i));
+                if (t != null) {
+                    return t;
+                }
+            }
+        } else {
+            final int count = mChildren.size();
+            for (int i = 0; i < count; i++) {
+                final Task t = processGetTaskWithBoundary(callback, boundary,
+                        includeBoundary, traverseTopToBottom, boundaryFound, mChildren.get(i));
+                if (t != null) {
+                    return t;
+                }
             }
         }
-        return false;
+
+        return null;
+    }
+
+    private Task processGetTaskWithBoundary(Predicate<Task> callback,
+            WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
+            boolean[] boundaryFound, WindowContainer wc) {
+        if (wc == boundary || boundary == null) {
+            boundaryFound[0] = true;
+            if (!includeBoundary) return null;
+        }
+
+        if (boundaryFound[0]) {
+            return wc.getTask(callback, traverseTopToBottom);
+        }
+
+        return wc.getTask(
+                callback, boundary, includeBoundary, traverseTopToBottom, boundaryFound);
     }
 
     WindowState getWindow(Predicate<WindowState> callback) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 95eb4dd..d1bd745 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.Manifest.permission.ACCESS_SURFACE_FLINGER;
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
@@ -492,13 +491,6 @@
     final long mDrawLockTimeoutMillis;
     final boolean mAllowAnimationsInLowPowerMode;
 
-    // TODO(b/122671846) Remove the flag below in favor of isLowRam once feature is stable
-    /**
-     * Use very low resolution task snapshots. Replaces task snapshot starting windows with
-     * splashscreen starting windows. Used on low RAM devices to save memory.
-     */
-    final boolean mLowRamTaskSnapshotsAndRecents;
-
     final boolean mAllowBootMessages;
 
     final boolean mLimitedAlphaCompositing;
@@ -1115,8 +1107,6 @@
                 com.android.internal.R.bool.config_disableTransitionAnimation);
         mPerDisplayFocusEnabled = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_perDisplayFocusEnabled);
-        mLowRamTaskSnapshotsAndRecents = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_lowRamTaskSnapshotsAndRecents);
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
 
@@ -7815,8 +7805,8 @@
 
     @Override
     public boolean mirrorDisplay(int displayId, SurfaceControl outSurfaceControl) {
-        if (!checkCallingPermission(ACCESS_SURFACE_FLINGER, "mirrorDisplay()")) {
-            throw new SecurityException("Requires ACCESS_SURFACE_FLINGER permission");
+        if (!checkCallingPermission(READ_FRAME_BUFFER, "mirrorDisplay()")) {
+            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
 
         final SurfaceControl displaySc;
@@ -7827,7 +7817,7 @@
                 return false;
             }
 
-            displaySc = displayContent.getSurfaceControl();
+            displaySc = displayContent.getWindowingLayer();
         }
 
         final SurfaceControl mirror = SurfaceControl.mirrorSurface(displaySc);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6a1aa02..5b7f36d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1239,11 +1239,6 @@
     }
 
     @Override
-    public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) {
-        return getDisplayContent().getNeedsMenu(this, bottom);
-    }
-
-    @Override
     public int getSystemUiVisibility() {
         return mSystemUiVisibility;
     }
@@ -1738,28 +1733,22 @@
         super.onMovedByResize();
     }
 
-    boolean onAppVisibilityChanged(boolean visible, boolean runningAppAnimation) {
-        boolean changed = false;
-
+    void onAppVisibilityChanged(boolean visible, boolean runningAppAnimation) {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = mChildren.get(i);
-            changed |= c.onAppVisibilityChanged(visible, runningAppAnimation);
+            mChildren.get(i).onAppVisibilityChanged(visible, runningAppAnimation);
         }
 
+        final boolean isVisibleNow = isVisibleNow();
         if (mAttrs.type == TYPE_APPLICATION_STARTING) {
             // Starting window that's exiting will be removed when the animation finishes.
             // Mark all relevant flags for that onExitAnimationDone will proceed all the way
             // to actually remove it.
-            if (!visible && isVisibleNow() && mActivityRecord.isAnimating(TRANSITION)) {
+            if (!visible && isVisibleNow && mActivityRecord.isAnimating(TRANSITION)) {
                 mAnimatingExit = true;
                 mRemoveOnExit = true;
                 mWindowRemovalAllowed = true;
             }
-            return changed;
-        }
-
-        final boolean isVisibleNow = isVisibleNow();
-        if (visible != isVisibleNow) {
+        } else if (visible != isVisibleNow) {
             // Run exit animation if:
             // 1. App visibility and WS visibility are different
             // 2. App is not running an animation
@@ -1773,11 +1762,8 @@
                     accessibilityController.onWindowTransitionLocked(this, winTransit);
                 }
             }
-            changed = true;
             setDisplayLayoutNeeded();
         }
-
-        return changed;
     }
 
     boolean onSetAppExiting() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ee0449d..eb1753b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -990,6 +990,7 @@
                 "cross-profile-calendar-packages";
         private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL =
                 "cross-profile-calendar-packages-null";
+        private static final String TAG_CROSS_PROFILE_PACKAGES = "cross-profile-packages";
 
         DeviceAdminInfo info;
 
@@ -1104,6 +1105,11 @@
         // is whitelisted.
         List<String> mCrossProfileCalendarPackages = Collections.emptyList();
 
+        // The whitelist of packages that the admin has enabled to be able to request consent from
+        // the user to communicate cross-profile. By default, no packages are whitelisted, which is
+        // represented as an empty list.
+        List<String> mCrossProfilePackages = Collections.emptyList();
+
         ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
             info = _info;
             isParent = parent;
@@ -1329,6 +1335,7 @@
                 writePackageListToXml(out, TAG_CROSS_PROFILE_CALENDAR_PACKAGES,
                         mCrossProfileCalendarPackages);
             }
+            writePackageListToXml(out, TAG_CROSS_PROFILE_PACKAGES, mCrossProfilePackages);
         }
 
         void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
@@ -1560,6 +1567,8 @@
                     mCrossProfileCalendarPackages = readPackageList(parser, tag);
                 } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL.equals(tag)) {
                     mCrossProfileCalendarPackages = null;
+                } else if (TAG_CROSS_PROFILE_PACKAGES.equals(tag)) {
+                    mCrossProfilePackages = readPackageList(parser, tag);
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -1783,6 +1792,8 @@
                 pw.print("mCrossProfileCalendarPackages=");
                 pw.println(mCrossProfileCalendarPackages);
             }
+            pw.print("mCrossProfilePackages=");
+            pw.println(mCrossProfilePackages);
         }
     }
 
@@ -14645,6 +14656,35 @@
     }
 
     @Override
+    public void setCrossProfilePackages(ComponentName who, List<String> packageNames) {
+        if (!mHasFeature) {
+            return;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkNotNull(packageNames, "Package names is null");
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin =
+                    getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            admin.mCrossProfilePackages = packageNames;
+            saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+        }
+    }
+
+    @Override
+    public List<String> getCrossProfilePackages(ComponentName who) {
+        if (!mHasFeature) {
+            return Collections.emptyList();
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getActiveAdminForCallerLocked(
+                    who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            return admin.mCrossProfilePackages;
+        }
+    }
+
+    @Override
     public boolean isManagedKiosk() {
         if (!mHasFeature) {
             return false;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 21cacd45..b6e501a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -109,6 +109,7 @@
 import com.android.server.inputmethod.InputMethodManagerService;
 import com.android.server.inputmethod.InputMethodSystemProperty;
 import com.android.server.inputmethod.MultiClientInputMethodManagerService;
+import com.android.server.integrity.AppIntegrityManagerService;
 import com.android.server.lights.LightsService;
 import com.android.server.media.MediaResourceMonitorService;
 import com.android.server.media.MediaRouterService;
@@ -1129,6 +1130,10 @@
             SignedConfigService.registerUpdateReceiver(mSystemContext);
             t.traceEnd();
 
+            t.traceBegin("AppIntegrityService");
+            mSystemServiceManager.startService(AppIntegrityManagerService.class);
+            t.traceEnd();
+
         } catch (Throwable e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service");
diff --git a/services/net/java/android/net/NetworkMonitorManager.java b/services/net/java/android/net/NetworkMonitorManager.java
index 0f41302..0f66981 100644
--- a/services/net/java/android/net/NetworkMonitorManager.java
+++ b/services/net/java/android/net/NetworkMonitorManager.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.Hide;
 import android.annotation.NonNull;
 import android.os.Binder;
 import android.os.RemoteException;
@@ -33,6 +34,7 @@
  * wrapper methods in this class return a boolean that callers can use to determine whether
  * RemoteException was thrown.
  */
+@Hide
 public class NetworkMonitorManager {
 
     @NonNull private final INetworkMonitor mNetworkMonitor;
diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java
index 4b7ed3c..09e333e 100644
--- a/services/net/java/android/net/ip/IpClientManager.java
+++ b/services/net/java/android/net/ip/IpClientManager.java
@@ -16,6 +16,7 @@
 
 package android.net.ip;
 
+import android.annotation.Hide;
 import android.annotation.NonNull;
 import android.net.NattKeepalivePacketData;
 import android.net.ProxyInfo;
@@ -38,6 +39,7 @@
  * wrapper methods in this class return a boolean that callers can use to determine whether
  * RemoteException was thrown.
  */
+@Hide
 public class IpClientManager {
     @NonNull private final IIpClient mIpClient;
     @NonNull private final String mTag;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 8e6114a..4635c08 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -142,7 +142,7 @@
         sService.mConstants = new ActivityManagerConstants(sContext, sService,
                 sContext.getMainThreadHandler());
         ProcessList pr = new ProcessList();
-        pr.init(sService, new ActiveUids(sService, false));
+        pr.init(sService, new ActiveUids(sService, false), null);
         setFieldValue(ActivityManagerService.class, sService, "mProcessList",
                 pr);
         setFieldValue(ActivityManagerService.class, sService, "mHandler",
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 3518dc5..05b655a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -120,11 +120,14 @@
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         // This should be public
-        assertDisplay(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), PORT_A, false);
-        // This should be private
-        assertDisplay(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), PORT_B, true);
+        assertDisplayPrivateFlag(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(),
+                PORT_A, false);
         // This should be public
-        assertDisplay(mListener.addedDisplays.get(2).getDisplayDeviceInfoLocked(), PORT_C, false);
+        assertDisplayPrivateFlag(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(),
+                PORT_B, true);
+        // This should be public
+        assertDisplayPrivateFlag(mListener.addedDisplays.get(2).getDisplayDeviceInfoLocked(),
+                PORT_C, false);
     }
 
     /**
@@ -141,12 +144,14 @@
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         // This should be public
-        assertDisplay(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), PORT_A, false);
+        assertDisplayPrivateFlag(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(),
+                PORT_A, false);
         // This should be public
-        assertDisplay(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), PORT_C, false);
+        assertDisplayPrivateFlag(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(),
+                PORT_C, false);
     }
 
-    private static void assertDisplay(
+    private static void assertDisplayPrivateFlag(
             DisplayDeviceInfo info, int expectedPort, boolean shouldBePrivate) {
         final DisplayAddress.Physical address = (DisplayAddress.Physical) info.address;
         assertNotNull(address);
@@ -155,6 +160,39 @@
         assertEquals(shouldBePrivate, (info.flags & DisplayDeviceInfo.FLAG_PRIVATE) != 0);
     }
 
+    /**
+     * Confirm that external display uses physical density.
+     */
+    @Test
+    public void testDpiValues() throws Exception {
+        // needs default one always
+        setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_A), createDummyDisplayInfo()));
+        setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_B), createDummyDisplayInfo()));
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        assertDisplayDpi(
+                mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), PORT_A, 100, 100,
+                16000);
+        assertDisplayDpi(
+                mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), PORT_B, 100, 100,
+                16000);
+    }
+
+    private void assertDisplayDpi(DisplayDeviceInfo info, int expectedPort,
+                                  float expectedXdpi,
+                                  float expectedYDpi,
+                                  int expectedDensityDpi) {
+        final DisplayAddress.Physical physical = (DisplayAddress.Physical) info.address;
+        assertNotNull(physical);
+        assertEquals((byte) expectedPort, physical.getPort());
+        assertEquals(expectedXdpi, info.xDpi, 0.01);
+        assertEquals(expectedYDpi, info.yDpi, 0.01);
+        assertEquals(expectedDensityDpi, info.densityDpi);
+    }
+
     private class DisplayConfig {
         public final DisplayAddress.Physical address;
         public final IBinder displayToken = new Binder();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index d70e164..96d9c47 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -73,7 +73,7 @@
 
     @Mock private AccessibilityUserState.ServiceInfoChangeListener mMockListener;
 
-    @Mock private Context mContext;
+    @Mock private Context mMockContext;
 
     private MockContentResolver mMockResolver;
 
@@ -85,11 +85,11 @@
         FakeSettingsProvider.clearSettingsProvider();
         mMockResolver = new MockContentResolver();
         mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
-        when(mContext.getContentResolver()).thenReturn(mMockResolver);
+        when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
         when(mMockServiceInfo.getComponentName()).thenReturn(COMPONENT_NAME);
         when(mMockConnection.getServiceInfo()).thenReturn(mMockServiceInfo);
 
-        mUserState = new AccessibilityUserState(USER_ID, mContext, mMockListener);
+        mUserState = new AccessibilityUserState(USER_ID, mMockContext, mMockListener);
     }
 
     @After
@@ -109,11 +109,11 @@
         mUserState.setInteractiveUiTimeoutLocked(30);
         mUserState.mEnabledServices.add(COMPONENT_NAME);
         mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME);
+        mUserState.mAccessibilityShortcutKeyTargets.add(COMPONENT_NAME.flattenToString());
+        mUserState.mAccessibilityButtonTargets.add(COMPONENT_NAME.flattenToString());
         mUserState.setTouchExplorationEnabledLocked(true);
         mUserState.setDisplayMagnificationEnabledLocked(true);
         mUserState.setNavBarMagnificationEnabledLocked(true);
-        mUserState.setServiceAssignedToAccessibilityButtonLocked(COMPONENT_NAME);
-        mUserState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
         mUserState.setAutoclickEnabledLocked(true);
         mUserState.setUserNonInteractiveUiTimeoutLocked(30);
         mUserState.setUserInteractiveUiTimeoutLocked(30);
@@ -128,11 +128,11 @@
         assertEquals(0, mUserState.getInteractiveUiTimeoutLocked());
         assertTrue(mUserState.mEnabledServices.isEmpty());
         assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty());
+        assertTrue(mUserState.mAccessibilityShortcutKeyTargets.isEmpty());
+        assertTrue(mUserState.mAccessibilityButtonTargets.isEmpty());
         assertFalse(mUserState.isTouchExplorationEnabledLocked());
         assertFalse(mUserState.isDisplayMagnificationEnabledLocked());
         assertFalse(mUserState.isNavBarMagnificationEnabledLocked());
-        assertNull(mUserState.getServiceAssignedToAccessibilityButtonLocked());
-        assertFalse(mUserState.isNavBarMagnificationAssignedToAccessibilityButtonLocked());
         assertFalse(mUserState.isAutoclickEnabledLocked());
         assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
         assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
@@ -287,6 +287,19 @@
         verify(mMockConnection).notifySoftKeyboardShowModeChangedLocked(eq(SHOW_MODE_HIDDEN));
     }
 
+    @Test
+    public void isShortcutTargetInstalledLocked_returnTrue() {
+        mUserState.mInstalledServices.add(mMockServiceInfo);
+        assertTrue(mUserState.isShortcutTargetInstalledLocked(COMPONENT_NAME.flattenToString()));
+    }
+
+    @Test
+    public void isShortcutTargetInstalledLocked_invalidTarget_returnFalse() {
+        final ComponentName invalidTarget =
+                new ComponentName("com.android.server.accessibility", "InvalidTarget");
+        assertFalse(mUserState.isShortcutTargetInstalledLocked(invalidTarget.flattenToString()));
+    }
+
     private int getSecureIntForUser(String key, int userId) {
         return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId);
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 43d8f92..3f09f57 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5548,6 +5548,38 @@
         mServiceContext.binder.restoreCallingIdentity(ident);
     }
 
+    public void testGetCrossProfilePackages_notSet_returnsEmpty() {
+        setAsProfileOwner(admin1);
+        assertTrue(dpm.getCrossProfilePackages(admin1).isEmpty());
+    }
+
+    public void testGetCrossProfilePackages_notSet_dpmsReinitialized_returnsEmpty() {
+        setAsProfileOwner(admin1);
+
+        initializeDpms();
+
+        assertTrue(dpm.getCrossProfilePackages(admin1).isEmpty());
+    }
+
+    public void testGetCrossProfilePackages_whenSet_returnsEqual() {
+        setAsProfileOwner(admin1);
+        Set<String> packages = Collections.singleton("TEST_PACKAGE");
+
+        dpm.setCrossProfilePackages(admin1, packages);
+
+        assertEquals(packages, dpm.getCrossProfilePackages(admin1));
+    }
+
+    public void testGetCrossProfilePackages_whenSet_dpmsReinitialized_returnsEqual() {
+        setAsProfileOwner(admin1);
+        Set<String> packages = Collections.singleton("TEST_PACKAGE");
+
+        dpm.setCrossProfilePackages(admin1, packages);
+        initializeDpms();
+
+        assertEquals(packages, dpm.getCrossProfilePackages(admin1));
+    }
+
     // admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one.
     private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception {
         writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM,
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
new file mode 100644
index 0000000..f6c4d3a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.display;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AutomaticBrightnessControllerTest {
+
+    private static final int BRIGHTNESS_MIN = 1;
+    private static final int BRIGHTNESS_MAX = 255;
+    private static final int LIGHT_SENSOR_RATE = 20;
+    private static final int INITIAL_LIGHT_SENSOR_RATE = 20;
+    private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 0;
+    private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 0;
+    private static final int SHORT_TERM_MODEL_TIMEOUT = 0;
+    private static final float DOZE_SCALE_FACTOR = 0.0f;
+    private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
+
+    private Context mContext;
+    @Mock SensorManager mSensorManager;
+    @Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
+    @Mock HysteresisLevels mAmbientBrightnessThresholds;
+    @Mock HysteresisLevels mScreenBrightnessThresholds;
+    @Mock PackageManager mPackageManager;
+    @Mock Handler mNoopHandler;
+
+    private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = InstrumentationRegistry.getContext();
+    }
+
+    private AutomaticBrightnessController setupController(Sensor lightSensor) {
+        AutomaticBrightnessController controller = new AutomaticBrightnessController(
+                new AutomaticBrightnessController.Injector() {
+                    @Override
+                    public Handler getBackgroundThreadHandler() {
+                        return mNoopHandler;
+                    }
+                },
+                () -> { }, mContext.getMainLooper(), mSensorManager, lightSensor,
+                mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN,
+                BRIGHTNESS_MAX, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, INITIAL_LIGHT_SENSOR_RATE,
+                BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, DARKENING_LIGHT_DEBOUNCE_CONFIG,
+                RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, mAmbientBrightnessThresholds,
+                mScreenBrightnessThresholds, SHORT_TERM_MODEL_TIMEOUT, mPackageManager);
+        controller.setLoggingEnabled(true);
+
+        // Configure the brightness controller and grab an instance of the sensor listener,
+        // through which we can deliver fake (for test) sensor values.
+        controller.configure(true /* enable */, null /* configuration */,
+                0 /* brightness */, false /* userChangedBrightness */, 0 /* adjustment */,
+                false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+
+        return controller;
+    }
+
+    @Test
+    public void testNoHysteresisAtMinBrightness() throws Exception {
+        Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+        AutomaticBrightnessController controller = setupController(lightSensor);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Set up system to return 5 as a brightness value
+        float lux1 = 100.0f;
+        float normalizedBrightness1 = 0.02f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness1);
+
+        // This is the important bit: When the new brightness is set, make sure the new
+        // brightening threshold is beyond the maximum brightness value...so that we can test that
+        // our threshold clamping works.
+        when(mScreenBrightnessThresholds.getBrighteningThreshold(5)).thenReturn(1.0f);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1));
+        assertEquals(5, controller.getAutomaticScreenBrightness());
+
+
+        // Set up system to return 255 as a brightness value
+        float lux2 = 10.0f;
+        float normalizedBrightness2 = 0.0f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness2);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2));
+        assertEquals(1, controller.getAutomaticScreenBrightness());
+    }
+
+    @Test
+    public void testNoHysteresisAtMaxBrightness() throws Exception {
+        Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+        AutomaticBrightnessController controller = setupController(lightSensor);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Set up system to return 250 as a brightness value
+        float lux1 = 100.0f;
+        float normalizedBrightness1 = 0.98f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness1);
+
+        // This is the important bit: When the new brightness is set, make sure the new
+        // brightening threshold is beyond the maximum brightness value...so that we can test that
+        // our threshold clamping works.
+        when(mScreenBrightnessThresholds.getBrighteningThreshold(250)).thenReturn(260.0f);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1));
+        assertEquals(250, controller.getAutomaticScreenBrightness());
+
+
+        // Set up system to return 255 as a brightness value
+        float lux2 = 110.0f;
+        float normalizedBrightness2 = 1.0f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness2);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2));
+        assertEquals(255, controller.getAutomaticScreenBrightness());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 4742a73..8d5939a 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -11,7 +11,7 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
 package com.android.server.display;
diff --git a/services/tests/servicestests/src/com/android/server/display/TestUtils.java b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
new file mode 100644
index 0000000..859dfe3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
@@ -0,0 +1,60 @@
+/*
+ * 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.display;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.os.SystemClock;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public final class TestUtils {
+
+    public static SensorEvent createSensorEvent(Sensor sensor, int lux) throws Exception {
+        final Constructor<SensorEvent> constructor =
+                SensorEvent.class.getDeclaredConstructor(int.class);
+        constructor.setAccessible(true);
+        final SensorEvent event = constructor.newInstance(1);
+        event.sensor = sensor;
+        event.values[0] = lux;
+        event.timestamp = SystemClock.elapsedRealtimeNanos();
+        return event;
+    }
+
+
+    public static void setSensorType(Sensor sensor, int type, String strType) throws Exception {
+        Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+        setter.setAccessible(true);
+        setter.invoke(sensor, type);
+        if (strType != null) {
+            Field f = sensor.getClass().getDeclaredField("mStringType");
+            f.setAccessible(true);
+            f.set(sensor, strType);
+        }
+    }
+
+    public static Sensor createSensor(int type, String strType) throws Exception {
+        Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+        constr.setAccessible(true);
+        Sensor sensor = constr.newInstance();
+        setSensorType(sensor, type, strType);
+        return sensor;
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index acf2d0e..2565ae3 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -16,31 +16,19 @@
 
 package com.android.server.display.whitebalance;
 
-import com.android.internal.R;
-import com.android.server.display.utils.AmbientFilter;
-import com.android.server.display.utils.AmbientFilterStubber;
-import com.google.common.collect.ImmutableList;
-
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.eq;
-import org.mockito.stubbing.Answer;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.Looper;
@@ -48,15 +36,22 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.internal.R;
+import com.android.server.display.TestUtils;
+import com.android.server.display.utils.AmbientFilter;
+import com.android.server.display.utils.AmbientFilterStubber;
+
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Before;
-import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.util.List;
 
 @RunWith(JUnit4.class)
@@ -84,8 +79,8 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mLightSensor = createSensor(Sensor.TYPE_LIGHT, null);
-        mAmbientColorSensor = createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
+        mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, null);
+        mAmbientColorSensor = TestUtils.createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
         when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
@@ -482,25 +477,6 @@
         }
     }
 
-    private void setSensorType(Sensor sensor, int type, String strType) throws Exception {
-        Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
-        setter.setAccessible(true);
-        setter.invoke(sensor, type);
-        if (strType != null) {
-            Field f = sensor.getClass().getDeclaredField("mStringType");
-            f.setAccessible(true);
-            f.set(sensor, strType);
-        }
-    }
-
-    private Sensor createSensor(int type, String strType) throws Exception {
-        Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
-        constr.setAccessible(true);
-        Sensor sensor = constr.newInstance();
-        setSensorType(sensor, type, strType);
-        return sensor;
-    }
-
     private TypedArray createTypedArray() throws Exception {
         TypedArray mockArray = mock(TypedArray.class);
         return mockArray;
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
index 6ff4f3b..3e3e535 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
@@ -28,15 +28,14 @@
 import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.SystemClock;
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.server.display.TestUtils;
 import com.android.server.display.whitebalance.AmbientSensor.AmbientBrightnessSensor;
 import com.android.server.display.whitebalance.AmbientSensor.AmbientColorTemperatureSensor;
 
@@ -50,9 +49,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -73,8 +69,8 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mLightSensor = createSensor(Sensor.TYPE_LIGHT, null);
-        mAmbientColorSensor = createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
+        mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, null);
+        mAmbientColorSensor = TestUtils.createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
         when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
@@ -96,7 +92,7 @@
         // There should be no issues when we callback the listener, even if there is no callback
         // set.
         SensorEventListener listener = captor.getValue();
-        listener.onSensorChanged(createSensorEvent(mLightSensor, 100));
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 100));
     }
 
     @Test
@@ -122,7 +118,7 @@
         verify(mSensorManagerMock).registerListener(captor.capture(), eq(mLightSensor),
                 anyInt(), eq(mHandler));
         SensorEventListener listener = captor.getValue();
-        listener.onSensorChanged(createSensorEvent(mLightSensor, luxValue));
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, luxValue));
         assertTrue(changeSignal.await(5, TimeUnit.SECONDS));
         assertEquals(luxValue, luxReturned[0]);
     }
@@ -155,39 +151,8 @@
         verify(mSensorManagerMock).registerListener(captor.capture(), eq(mAmbientColorSensor),
                 anyInt(), eq(mHandler));
         SensorEventListener listener = captor.getValue();
-        listener.onSensorChanged(createSensorEvent(mAmbientColorSensor, colorTempValue));
+        listener.onSensorChanged(TestUtils.createSensorEvent(mAmbientColorSensor, colorTempValue));
         assertTrue(changeSignal.await(5, TimeUnit.SECONDS));
         assertEquals(colorTempValue, colorTempReturned[0]);
     }
-
-    private SensorEvent createSensorEvent(Sensor sensor, int lux) throws Exception {
-        final Constructor<SensorEvent> constructor =
-                SensorEvent.class.getDeclaredConstructor(int.class);
-        constructor.setAccessible(true);
-        final SensorEvent event = constructor.newInstance(1);
-        event.sensor = sensor;
-        event.values[0] = lux;
-        event.timestamp = SystemClock.elapsedRealtimeNanos();
-        return event;
-    }
-
-
-    private void setSensorType(Sensor sensor, int type, String strType) throws Exception {
-        Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
-        setter.setAccessible(true);
-        setter.invoke(sensor, type);
-        if (strType != null) {
-            Field f = sensor.getClass().getDeclaredField("mStringType");
-            f.setAccessible(true);
-            f.set(sensor, strType);
-        }
-    }
-
-    private Sensor createSensor(int type, String strType) throws Exception {
-        Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
-        constr.setAccessible(true);
-        Sensor sensor = constr.newInstance();
-        setSensorType(sensor, type, strType);
-        return sensor;
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
new file mode 100644
index 0000000..37ff06a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.integrity;
+
+import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
+
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/** Unit test for {@link com.android.server.integrity.AppIntegrityManagerServiceImpl} */
+@RunWith(AndroidJUnit4.class)
+public class AppIntegrityManagerServiceImplTest {
+
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock PackageManagerInternal mPackageManagerInternal;
+
+    // under test
+    private AppIntegrityManagerServiceImpl mService;
+
+    @Before
+    public void setup() {
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+
+        mService = new AppIntegrityManagerServiceImpl(InstrumentationRegistry.getContext());
+    }
+
+    @Test
+    public void integrityVerification_allow() {
+        int verificationId = 2;
+        Intent integrityVerificationIntent = new Intent();
+        integrityVerificationIntent.setAction(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
+        integrityVerificationIntent.putExtra(EXTRA_VERIFICATION_ID, verificationId);
+
+        // We cannot send the broadcast using the context since it is a protected broadcast and
+        // we will get a security exception.
+        mService.handleIntegrityVerification(integrityVerificationIntent);
+
+        verify(mPackageManagerInternal)
+                .setIntegrityVerificationResult(verificationId, PackageManager.VERIFICATION_ALLOW);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
deleted file mode 100644
index f45316f..0000000
--- a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * 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.om
-
-import android.content.pm.parsing.AndroidPackage
-import android.net.Uri
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.testng.Assert.assertThrows
-import test.util.mockThrowOnUnmocked
-import test.util.whenever
-
-@RunWith(Parameterized::class)
-class OverlayReferenceMapperTests {
-
-    companion object {
-        private const val TARGET_PACKAGE_NAME = "com.test.target"
-        private const val OVERLAY_PACKAGE_NAME = "com.test.overlay"
-        private const val ACTOR_PACKAGE_NAME = "com.test.actor"
-        private const val ACTOR_NAME = "overlay://test/actorName"
-
-        @JvmStatic
-        @Parameterized.Parameters(name = "deferRebuild {0}")
-        fun parameters() = arrayOf(true, false)
-    }
-
-    private lateinit var mapper: OverlayReferenceMapper
-
-    @JvmField
-    @Parameterized.Parameter(0)
-    var deferRebuild = false
-
-    @Before
-    fun initMapper() {
-        mapper = mapper()
-    }
-
-    @Test
-    fun targetWithOverlay() {
-        val target = mockTarget()
-        val overlay = mockOverlay()
-        val existing = mapper.addInOrder(overlay)
-        assertEmpty()
-        mapper.addInOrder(target, existing = existing)
-        assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay))
-        mapper.remove(target)
-        assertEmpty()
-    }
-
-    @Test
-    fun targetWithMultipleOverlays() {
-        val target = mockTarget()
-        val overlay0 = mockOverlay(0)
-        val overlay1 = mockOverlay(1)
-        mapper = mapper(
-                overlayToTargetToOverlayables = mapOf(
-                        overlay0.packageName to mapOf(
-                                target.packageName to target.overlayables.keys
-                        ),
-                        overlay1.packageName to mapOf(
-                                target.packageName to target.overlayables.keys
-                        )
-                )
-        )
-        val existing = mapper.addInOrder(overlay0, overlay1)
-        assertEmpty()
-        mapper.addInOrder(target, existing = existing)
-        assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay0, overlay1))
-        mapper.remove(overlay0)
-        assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay1))
-        mapper.remove(target)
-        assertEmpty()
-    }
-
-    @Test
-    fun targetWithoutOverlay() {
-        val target = mockTarget()
-        mapper.addInOrder(target)
-        assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
-        mapper.remove(target)
-        assertEmpty()
-    }
-
-    @Test
-    fun overlayWithTarget() {
-        val target = mockTarget()
-        val overlay = mockOverlay()
-        val existing = mapper.addInOrder(target)
-        assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
-        mapper.addInOrder(overlay, existing = existing)
-        assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay))
-        mapper.remove(overlay)
-        assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
-    }
-
-    @Test
-    fun overlayWithMultipleTargets() {
-        val target0 = mockTarget(0)
-        val target1 = mockTarget(1)
-        val overlay = mockOverlay()
-        mapper = mapper(
-                overlayToTargetToOverlayables = mapOf(
-                        overlay.packageName to mapOf(
-                                target0.packageName to target0.overlayables.keys,
-                                target1.packageName to target1.overlayables.keys
-                        )
-                )
-        )
-        mapper.addInOrder(target0, target1, overlay)
-        assertMapping(ACTOR_PACKAGE_NAME to setOf(target0, target1, overlay))
-        mapper.remove(target0)
-        assertMapping(ACTOR_PACKAGE_NAME to setOf(target1, overlay))
-        mapper.remove(target1)
-        assertEmpty()
-    }
-
-    @Test
-    fun overlayWithoutTarget() {
-        val overlay = mockOverlay()
-        mapper.addInOrder(overlay)
-        // An overlay can only have visibility exposed through its target
-        assertEmpty()
-        mapper.remove(overlay)
-        assertEmpty()
-    }
-
-    private fun OverlayReferenceMapper.addInOrder(
-        vararg pkgs: AndroidPackage,
-        existing: MutableMap<String, AndroidPackage> = mutableMapOf()
-    ) = pkgs.fold(existing) { map, pkg ->
-        addPkg(pkg, map)
-        map[pkg.packageName] = pkg
-        return@fold map
-    }
-
-    private fun OverlayReferenceMapper.remove(pkg: AndroidPackage) = removePkg(pkg.packageName)
-
-    private fun assertMapping(vararg pairs: Pair<String, Set<AndroidPackage>>) {
-        val expected = pairs.associate { it }
-                .mapValues { pair -> pair.value.map { it.packageName }.toSet() }
-
-        // This validates the API exposed for querying the relationships
-        expected.forEach { (actorPkgName, expectedPkgNames) ->
-            expectedPkgNames.forEach { expectedPkgName ->
-                if (deferRebuild) {
-                    assertThrows(IllegalStateException::class.java) {
-                        mapper.isValidActor(expectedPkgName, actorPkgName)
-                    }
-                    mapper.rebuildIfDeferred()
-                    deferRebuild = false
-                }
-
-                assertThat(mapper.isValidActor(expectedPkgName, actorPkgName)).isTrue()
-            }
-        }
-
-        // This asserts no other relationships are defined besides those tested above
-        assertThat(mapper.actorPkgToPkgs).containsExactlyEntriesIn(expected)
-    }
-
-    private fun assertEmpty() = assertMapping()
-
-    private fun mapper(
-        namedActors: Map<String, Map<String, String>> = Uri.parse(ACTOR_NAME).run {
-            mapOf(authority!! to mapOf(pathSegments.first() to ACTOR_PACKAGE_NAME))
-        },
-        overlayToTargetToOverlayables: Map<String, Map<String, Set<String>>> = mapOf(
-                mockOverlay().packageName to mapOf(
-                        mockTarget().run { packageName to overlayables.keys }
-                )
-        )
-    ) = OverlayReferenceMapper(deferRebuild, object : OverlayReferenceMapper.Provider {
-        override fun getActorPkg(actor: String?) =
-                OverlayActorEnforcer.getPackageNameForActor(actor, namedActors).first
-
-        override fun getTargetToOverlayables(pkg: AndroidPackage) =
-                overlayToTargetToOverlayables[pkg.packageName] ?: emptyMap()
-    })
-
-    private fun mockTarget(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> {
-        whenever(packageName) { "$TARGET_PACKAGE_NAME$increment" }
-        whenever(overlayables) { mapOf("overlayableName$increment" to ACTOR_NAME) }
-        whenever(toString()) { "Package{$packageName}" }
-    }
-
-    private fun mockOverlay(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> {
-        whenever(packageName) { "$OVERLAY_PACKAGE_NAME$increment" }
-        whenever(overlayables) { emptyMap<String, String>() }
-        whenever(toString()) { "Package{$packageName}" }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 82bbdcb..4fc625a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -35,11 +35,6 @@
 import android.os.Build;
 import android.os.Process;
 import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import androidx.annotation.NonNull;
-
-import com.android.server.om.OverlayReferenceMapper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -48,18 +43,11 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-
 @RunWith(JUnit4.class)
 public class AppsFilterTest {
 
     private static final int DUMMY_CALLING_UID = 10345;
     private static final int DUMMY_TARGET_UID = 10556;
-    private static final int DUMMY_ACTOR_UID = 10656;
-    private static final int DUMMY_OVERLAY_UID = 10756;
-    private static final int DUMMY_ACTOR_TWO_UID = 10856;
 
     @Mock
     AppsFilter.FeatureConfig mFeatureConfigMock;
@@ -129,7 +117,7 @@
     @Test
     public void testSystemReadyPropogates() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
         appsFilter.onSystemReady();
         verify(mFeatureConfigMock).onSystemReady();
     }
@@ -137,8 +125,7 @@
     @Test
     public void testQueriesAction_FilterMatches() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_UID);
@@ -151,8 +138,7 @@
     @Test
     public void testQueriesAction_NoMatchingAction_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -165,8 +151,7 @@
     @Test
     public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -184,8 +169,7 @@
     @Test
     public void testNoQueries_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -198,8 +182,7 @@
     @Test
     public void testForceQueryable_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                         pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID);
@@ -212,8 +195,7 @@
     @Test
     public void testForceQueryableByDevice_SystemCaller_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID,
@@ -227,8 +209,7 @@
     @Test
     public void testForceQueryableByDevice_NonSystemCaller_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -243,8 +224,7 @@
     public void testSystemQueryable_DoesntFilter() {
         final AppsFilter appsFilter =
                 new AppsFilter(mFeatureConfigMock, new String[]{},
-                        true /* system force queryable */, null);
-        appsFilter.onSystemReady();
+                        true /* system force queryable */);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID,
@@ -258,8 +238,7 @@
     @Test
     public void testQueriesPackage_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -274,8 +253,7 @@
         when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class)))
                 .thenReturn(false);
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(
                 appsFilter, pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -288,22 +266,20 @@
     @Test
     public void testSystemUid_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
 
         assertFalse(appsFilter.shouldFilterApplication(0, null, target, 0));
-        assertFalse(appsFilter.shouldFilterApplication(Process.FIRST_APPLICATION_UID - 1,
-                null, target, 0));
+        assertFalse(appsFilter.shouldFilterApplication(
+                Process.FIRST_APPLICATION_UID - 1, null, target, 0));
     }
 
     @Test
     public void testNonSystemUid_NoCallingSetting_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -314,8 +290,7 @@
     @Test
     public void testNoTargetPackage_filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
-        appsFilter.onSystemReady();
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
 
         PackageSetting target = new PackageSettingBuilder()
                 .setName("com.some.package")
@@ -329,127 +304,6 @@
         assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
     }
 
-    @Test
-    public void testActsOnTargetOfOverlay() {
-        final String actorName = "overlay://test/actorName";
-
-        ParsingPackage target = pkg("com.some.package.target")
-                .addOverlayable("overlayableName", actorName);
-        ParsingPackage overlay = pkg("com.some.package.overlay")
-                .setIsOverlay(true)
-                .setOverlayTarget(target.getPackageName())
-                .setOverlayTargetName("overlayableName");
-        ParsingPackage actor = pkg("com.some.package.actor");
-
-        final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
-                new OverlayReferenceMapper.Provider() {
-                    @Nullable
-                    @Override
-                    public String getActorPkg(String actorString) {
-                        if (actorName.equals(actorString)) {
-                            return actor.getPackageName();
-                        }
-                        return null;
-                    }
-
-                    @NonNull
-                    @Override
-                    public Map<String, Set<String>> getTargetToOverlayables(
-                            @NonNull AndroidPackage pkg) {
-                        if (overlay.getPackageName().equals(pkg.getPackageName())) {
-                            Map<String, Set<String>> map = new ArrayMap<>();
-                            Set<String> set = new ArraySet<>();
-                            set.add(overlay.getOverlayTargetName());
-                            map.put(overlay.getOverlayTarget(), set);
-                            return map;
-                        }
-                        return Collections.emptyMap();
-                    }
-                });
-        appsFilter.onSystemReady();
-
-        PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
-        PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
-        PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_UID);
-
-        // Actor can see both target and overlay
-        assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
-                targetSetting, 0));
-        assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
-                overlaySetting, 0));
-
-        // But target/overlay can't see each other
-        assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
-                overlaySetting, 0));
-        assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
-                targetSetting, 0));
-
-        // And can't see the actor
-        assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
-                actorSetting, 0));
-        assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
-                actorSetting, 0));
-    }
-
-    @Test
-    public void testActsOnTargetOfOverlayThroughSharedUser() {
-        final String actorName = "overlay://test/actorName";
-
-        ParsingPackage target = pkg("com.some.package.target")
-                .addOverlayable("overlayableName", actorName);
-        ParsingPackage overlay = pkg("com.some.package.overlay")
-                .setIsOverlay(true)
-                .setOverlayTarget(target.getPackageName())
-                .setOverlayTargetName("overlayableName");
-        ParsingPackage actorOne = pkg("com.some.package.actor.one");
-        ParsingPackage actorTwo = pkg("com.some.package.actor.two");
-
-        final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
-                new OverlayReferenceMapper.Provider() {
-                    @Nullable
-                    @Override
-                    public String getActorPkg(String actorString) {
-                        // Only actorOne is mapped as a valid actor
-                        if (actorName.equals(actorString)) {
-                            return actorOne.getPackageName();
-                        }
-                        return null;
-                    }
-
-                    @NonNull
-                    @Override
-                    public Map<String, Set<String>> getTargetToOverlayables(
-                            @NonNull AndroidPackage pkg) {
-                        if (overlay.getPackageName().equals(pkg.getPackageName())) {
-                            Map<String, Set<String>> map = new ArrayMap<>();
-                            Set<String> set = new ArraySet<>();
-                            set.add(overlay.getOverlayTargetName());
-                            map.put(overlay.getOverlayTarget(), set);
-                            return map;
-                        }
-                        return Collections.emptyMap();
-                    }
-                });
-        appsFilter.onSystemReady();
-
-        PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
-        PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
-        PackageSetting actorOneSetting = simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_UID);
-        PackageSetting actorTwoSetting = simulateAddPackage(appsFilter, actorTwo,
-                DUMMY_ACTOR_TWO_UID);
-
-        SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser",
-                actorOneSetting.pkgFlags, actorOneSetting.pkgPrivateFlags);
-        actorSharedSetting.addPackage(actorOneSetting);
-        actorSharedSetting.addPackage(actorTwoSetting);
-
-        // actorTwo can see both target and overlay
-        assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
-                targetSetting, 0));
-        assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
-                overlaySetting, 0));
-    }
-
     private interface WithSettingBuilder {
         PackageSettingBuilder withBuilder(PackageSettingBuilder builder);
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 2473997..5baeede 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -22,7 +22,7 @@
 
 import java.io.File;
 
-public class PackageSettingBuilder {
+class PackageSettingBuilder {
     private String mName;
     private String mRealName;
     private String mCodePath;
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
deleted file mode 100644
index 7a0a28d..0000000
--- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
+++ /dev/null
@@ -1,661 +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.timedetector;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.app.timedetector.ManualTimeSuggestion;
-import android.app.timedetector.PhoneTimeSuggestion;
-import android.content.Intent;
-import android.icu.util.Calendar;
-import android.icu.util.GregorianCalendar;
-import android.icu.util.TimeZone;
-import android.util.TimestampedValue;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.time.Duration;
-
-@RunWith(AndroidJUnit4.class)
-public class SimpleTimeDetectorStrategyTest {
-
-    private static final Scenario SCENARIO_1 = new Scenario.Builder()
-            .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
-            .setInitialDeviceRealtimeMillis(123456789L)
-            .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
-            .build();
-
-    private static final int ARBITRARY_PHONE_ID = 123456;
-
-    private static final long ONE_DAY_MILLIS = Duration.ofDays(1).toMillis();
-
-    private Script mScript;
-
-    @Before
-    public void setUp() {
-        mScript = new Script();
-    }
-
-    @Test
-    public void testSuggestPhoneTime_autoTimeEnabled() {
-        Scenario scenario = SCENARIO_1;
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(true);
-
-        PhoneTimeSuggestion timeSuggestion =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        final int clockIncrement = 1000;
-        long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement;
-
-        mScript.simulateTimePassing(clockIncrement)
-                .simulatePhoneTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectSystemClockMillis, true /* expectNetworkBroadcast */);
-    }
-
-    @Test
-    public void testSuggestPhoneTime_emptySuggestionIgnored() {
-        Scenario scenario = SCENARIO_1;
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(true);
-
-        PhoneTimeSuggestion timeSuggestion = createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, null);
-
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-    }
-
-    @Test
-    public void testSuggestPhoneTime_systemClockThreshold() {
-        Scenario scenario = SCENARIO_1;
-        final int systemClockUpdateThresholdMillis = 1000;
-        mScript.pokeFakeClocks(scenario)
-                .pokeThresholds(systemClockUpdateThresholdMillis)
-                .pokeTimeDetectionEnabled(true);
-
-        PhoneTimeSuggestion timeSuggestion1 =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
-
-        final int clockIncrement = 100;
-        // Increment the the device clocks to simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-
-        long expectSystemClockMillis1 =
-                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
-
-        // Send the first time signal. It should be used.
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectSystemClockMillis1, true /* expectNetworkBroadcast */);
-
-        // Now send another time signal, but one that is too similar to the last one and should be
-        // ignored.
-        int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
-        TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
-                mScript.peekElapsedRealtimeMillis(),
-                mScript.peekSystemClockMillis() + underThresholdMillis);
-        PhoneTimeSuggestion timeSuggestion2 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
-        mScript.simulateTimePassing(clockIncrement)
-                .simulatePhoneTimeSuggestion(timeSuggestion2)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Now send another time signal, but one that is on the threshold and so should be used.
-        TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
-                mScript.peekElapsedRealtimeMillis(),
-                mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
-
-        PhoneTimeSuggestion timeSuggestion3 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3);
-        mScript.simulateTimePassing(clockIncrement);
-
-        long expectSystemClockMillis3 =
-                TimeDetectorStrategy.getTimeAt(utcTime3, mScript.peekElapsedRealtimeMillis());
-
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectSystemClockMillis3, true /* expectNetworkBroadcast */);
-    }
-
-    @Test
-    public void testSuggestPhoneTime_autoTimeDisabled() {
-        Scenario scenario = SCENARIO_1;
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(false);
-
-        PhoneTimeSuggestion timeSuggestion =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-    }
-
-    @Test
-    public void testSuggestPhoneTime_invalidNitzReferenceTimesIgnored() {
-        Scenario scenario = SCENARIO_1;
-        final int systemClockUpdateThreshold = 2000;
-        mScript.pokeFakeClocks(scenario)
-                .pokeThresholds(systemClockUpdateThreshold)
-                .pokeTimeDetectionEnabled(true);
-        PhoneTimeSuggestion timeSuggestion1 =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
-
-        // Initialize the strategy / device with a time set from NITZ.
-        mScript.simulateTimePassing(100);
-        long expectedSystemClockMillis1 =
-                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis1, true /* expectNetworkBroadcast */);
-
-        // The UTC time increment should be larger than the system clock update threshold so we
-        // know it shouldn't be ignored for other reasons.
-        long validUtcTimeMillis = utcTime1.getValue() + (2 * systemClockUpdateThreshold);
-
-        // Now supply a new signal that has an obviously bogus reference time : older than the last
-        // one.
-        long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1;
-        TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
-                referenceTimeBeforeLastSignalMillis, validUtcTimeMillis);
-        PhoneTimeSuggestion timeSuggestion2 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Now supply a new signal that has an obviously bogus reference time : substantially in the
-        // future.
-        long referenceTimeInFutureMillis =
-                utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1;
-        TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
-                referenceTimeInFutureMillis, validUtcTimeMillis);
-        PhoneTimeSuggestion timeSuggestion3 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Just to prove validUtcTimeMillis is valid.
-        long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
-        TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
-                validReferenceTimeMillis, validUtcTimeMillis);
-        long expectedSystemClockMillis4 =
-                TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
-        PhoneTimeSuggestion timeSuggestion4 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime4);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis4, true /* expectNetworkBroadcast */);
-    }
-
-    @Test
-    public void testSuggestPhoneTime_timeDetectionToggled() {
-        Scenario scenario = SCENARIO_1;
-        final int clockIncrementMillis = 100;
-        final int systemClockUpdateThreshold = 2000;
-        mScript.pokeFakeClocks(scenario)
-                .pokeThresholds(systemClockUpdateThreshold)
-                .pokeTimeDetectionEnabled(false);
-
-        PhoneTimeSuggestion timeSuggestion1 =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
-
-        // Simulate time passing.
-        mScript.simulateTimePassing(clockIncrementMillis);
-
-        // Simulate the time signal being received. It should not be used because auto time
-        // detection is off but it should be recorded.
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Simulate more time passing.
-        mScript.simulateTimePassing(clockIncrementMillis);
-
-        long expectedSystemClockMillis1 =
-                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
-
-        // Turn on auto time detection.
-        mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis1, true /* expectNetworkBroadcast */);
-
-        // Turn off auto time detection.
-        mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Receive another valid time signal.
-        // It should be on the threshold and accounting for the clock increments.
-        TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
-                mScript.peekElapsedRealtimeMillis(),
-                mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
-        PhoneTimeSuggestion timeSuggestion2 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
-
-        // Simulate more time passing.
-        mScript.simulateTimePassing(clockIncrementMillis);
-
-        long expectedSystemClockMillis2 =
-                TimeDetectorStrategy.getTimeAt(utcTime2, mScript.peekElapsedRealtimeMillis());
-
-        // The new time, though valid, should not be set in the system clock because auto time is
-        // disabled.
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Turn on auto time detection.
-        mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis2, true /* expectNetworkBroadcast */);
-    }
-
-    @Test
-    public void testSuggestManualTime_autoTimeDisabled() {
-        Scenario scenario = SCENARIO_1;
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(false);
-
-        ManualTimeSuggestion timeSuggestion = scenario.createManualTimeSuggestionForActual();
-        final int clockIncrement = 1000;
-        long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement;
-
-        mScript.simulateTimePassing(clockIncrement)
-                .simulateManualTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectSystemClockMillis, false /* expectNetworkBroadcast */);
-    }
-
-    @Test
-    public void testSuggestManualTime_retainsAutoSignal() {
-        Scenario scenario = SCENARIO_1;
-
-        // Configure the start state.
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(true);
-
-        // Simulate a phone suggestion.
-        PhoneTimeSuggestion phoneTimeSuggestion =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        long expectedAutoClockMillis = phoneTimeSuggestion.getUtcTime().getValue();
-        final int clockIncrement = 1000;
-
-        // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
-
-        mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedAutoClockMillis, true /* expectNetworkBroadcast */);
-
-        // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
-
-        // Switch to manual.
-        mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
-
-
-        // Simulate a manual suggestion 1 day different from the auto suggestion.
-        long manualTimeMillis = SCENARIO_1.getActualTimeMillis() + ONE_DAY_MILLIS;
-        long expectedManualClockMillis = manualTimeMillis;
-        ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion(manualTimeMillis);
-        mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedManualClockMillis, false /* expectNetworkBroadcast */);
-
-        // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
-
-        // Switch back to auto.
-        mScript.simulateAutoTimeDetectionToggle();
-
-        mScript.verifySystemClockWasSetAndResetCallTracking(
-                        expectedAutoClockMillis, true /* expectNetworkBroadcast */);
-
-        // Switch back to manual - nothing should happen to the clock.
-        mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasNotSetAndResetCallTracking();
-    }
-
-    /**
-     * Manual suggestions should be ignored if auto time is enabled.
-     */
-    @Test
-    public void testSuggestManualTime_autoTimeEnabled() {
-        Scenario scenario = SCENARIO_1;
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(true);
-
-        ManualTimeSuggestion timeSuggestion = scenario.createManualTimeSuggestionForActual();
-        final int clockIncrement = 1000;
-
-        mScript.simulateTimePassing(clockIncrement)
-                .simulateManualTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-    }
-
-    /**
-     * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
-     * like the real thing should, it also asserts preconditions.
-     */
-    private static class FakeCallback implements TimeDetectorStrategy.Callback {
-        private boolean mTimeDetectionEnabled;
-        private boolean mWakeLockAcquired;
-        private long mElapsedRealtimeMillis;
-        private long mSystemClockMillis;
-        private int mSystemClockUpdateThresholdMillis = 2000;
-
-        // Tracking operations.
-        private boolean mSystemClockWasSet;
-        private Intent mBroadcastSent;
-
-        @Override
-        public int systemClockUpdateThresholdMillis() {
-            return mSystemClockUpdateThresholdMillis;
-        }
-
-        @Override
-        public boolean isAutoTimeDetectionEnabled() {
-            return mTimeDetectionEnabled;
-        }
-
-        @Override
-        public void acquireWakeLock() {
-            if (mWakeLockAcquired) {
-                fail("Wake lock already acquired");
-            }
-            mWakeLockAcquired = true;
-        }
-
-        @Override
-        public long elapsedRealtimeMillis() {
-            assertWakeLockAcquired();
-            return mElapsedRealtimeMillis;
-        }
-
-        @Override
-        public long systemClockMillis() {
-            assertWakeLockAcquired();
-            return mSystemClockMillis;
-        }
-
-        @Override
-        public void setSystemClock(long newTimeMillis) {
-            assertWakeLockAcquired();
-            mSystemClockWasSet = true;
-            mSystemClockMillis = newTimeMillis;
-        }
-
-        @Override
-        public void releaseWakeLock() {
-            assertWakeLockAcquired();
-            mWakeLockAcquired = false;
-        }
-
-        @Override
-        public void sendStickyBroadcast(Intent intent) {
-            assertNotNull(intent);
-            mBroadcastSent = intent;
-        }
-
-        // Methods below are for managing the fake's behavior.
-
-        public void pokeSystemClockUpdateThreshold(int thresholdMillis) {
-            mSystemClockUpdateThresholdMillis = thresholdMillis;
-        }
-
-        public void pokeElapsedRealtimeMillis(long elapsedRealtimeMillis) {
-            mElapsedRealtimeMillis = elapsedRealtimeMillis;
-        }
-
-        public void pokeSystemClockMillis(long systemClockMillis) {
-            mSystemClockMillis = systemClockMillis;
-        }
-
-        public void pokeAutoTimeDetectionEnabled(boolean enabled) {
-            mTimeDetectionEnabled = enabled;
-        }
-
-        public long peekElapsedRealtimeMillis() {
-            return mElapsedRealtimeMillis;
-        }
-
-        public long peekSystemClockMillis() {
-            return mSystemClockMillis;
-        }
-
-        public void simulateTimePassing(int incrementMillis) {
-            mElapsedRealtimeMillis += incrementMillis;
-            mSystemClockMillis += incrementMillis;
-        }
-
-        public void simulateAutoTimeZoneDetectionToggle() {
-            mTimeDetectionEnabled = !mTimeDetectionEnabled;
-        }
-
-        public void verifySystemClockNotSet() {
-            assertFalse(mSystemClockWasSet);
-        }
-
-        public void verifySystemClockWasSet(long expectSystemClockMillis) {
-            assertTrue(mSystemClockWasSet);
-            assertEquals(expectSystemClockMillis, mSystemClockMillis);
-        }
-
-        public void verifyIntentWasBroadcast() {
-            assertTrue(mBroadcastSent != null);
-        }
-
-        public void verifyIntentWasNotBroadcast() {
-            assertNull(mBroadcastSent);
-        }
-
-        public void resetCallTracking() {
-            mSystemClockWasSet = false;
-            mBroadcastSent = null;
-        }
-
-        private void assertWakeLockAcquired() {
-            assertTrue("The operation must be performed only after acquiring the wakelock",
-                    mWakeLockAcquired);
-        }
-    }
-
-    /**
-     * A fluent helper class for tests.
-     */
-    private class Script {
-
-        private final FakeCallback mFakeCallback;
-        private final SimpleTimeDetectorStrategy mSimpleTimeDetectorStrategy;
-
-        Script() {
-            mFakeCallback = new FakeCallback();
-            mSimpleTimeDetectorStrategy = new SimpleTimeDetectorStrategy();
-            mSimpleTimeDetectorStrategy.initialize(mFakeCallback);
-
-        }
-
-        Script pokeTimeDetectionEnabled(boolean enabled) {
-            mFakeCallback.pokeAutoTimeDetectionEnabled(enabled);
-            return this;
-        }
-
-        Script pokeFakeClocks(Scenario scenario) {
-            mFakeCallback.pokeElapsedRealtimeMillis(scenario.getInitialRealTimeMillis());
-            mFakeCallback.pokeSystemClockMillis(scenario.getInitialSystemClockMillis());
-            return this;
-        }
-
-        Script pokeThresholds(int systemClockUpdateThreshold) {
-            mFakeCallback.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
-            return this;
-        }
-
-        long peekElapsedRealtimeMillis() {
-            return mFakeCallback.peekElapsedRealtimeMillis();
-        }
-
-        long peekSystemClockMillis() {
-            return mFakeCallback.peekSystemClockMillis();
-        }
-
-        Script simulatePhoneTimeSuggestion(PhoneTimeSuggestion timeSuggestion) {
-            mSimpleTimeDetectorStrategy.suggestPhoneTime(timeSuggestion);
-            return this;
-        }
-
-        Script simulateManualTimeSuggestion(ManualTimeSuggestion timeSuggestion) {
-            mSimpleTimeDetectorStrategy.suggestManualTime(timeSuggestion);
-            return this;
-        }
-
-        Script simulateAutoTimeDetectionToggle() {
-            mFakeCallback.simulateAutoTimeZoneDetectionToggle();
-            mSimpleTimeDetectorStrategy.handleAutoTimeDetectionChanged();
-            return this;
-        }
-
-        Script simulateTimePassing(int clockIncrement) {
-            mFakeCallback.simulateTimePassing(clockIncrement);
-            return this;
-        }
-
-        Script verifySystemClockWasNotSetAndResetCallTracking() {
-            mFakeCallback.verifySystemClockNotSet();
-            mFakeCallback.verifyIntentWasNotBroadcast();
-            mFakeCallback.resetCallTracking();
-            return this;
-        }
-
-        Script verifySystemClockWasSetAndResetCallTracking(
-                long expectSystemClockMillis, boolean expectNetworkBroadcast) {
-            mFakeCallback.verifySystemClockWasSet(expectSystemClockMillis);
-            if (expectNetworkBroadcast) {
-                mFakeCallback.verifyIntentWasBroadcast();
-            }
-            mFakeCallback.resetCallTracking();
-            return this;
-        }
-    }
-
-    /**
-     * A starting scenario used during tests. Describes a fictional "physical" reality.
-     */
-    private static class Scenario {
-
-        private final long mInitialDeviceSystemClockMillis;
-        private final long mInitialDeviceRealtimeMillis;
-        private final long mActualTimeMillis;
-
-        Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis) {
-            mInitialDeviceSystemClockMillis = initialDeviceSystemClock;
-            mActualTimeMillis = timeMillis;
-            mInitialDeviceRealtimeMillis = elapsedRealtime;
-        }
-
-        long getInitialRealTimeMillis() {
-            return mInitialDeviceRealtimeMillis;
-        }
-
-        long getInitialSystemClockMillis() {
-            return mInitialDeviceSystemClockMillis;
-        }
-
-        long getActualTimeMillis() {
-            return mActualTimeMillis;
-        }
-
-        PhoneTimeSuggestion createPhoneTimeSuggestionForActual(int phoneId) {
-            TimestampedValue<Long> time = new TimestampedValue<>(
-                    mInitialDeviceRealtimeMillis, mActualTimeMillis);
-            return createPhoneTimeSuggestion(phoneId, time);
-        }
-
-        ManualTimeSuggestion createManualTimeSuggestionForActual() {
-            TimestampedValue<Long> time = new TimestampedValue<>(
-                    mInitialDeviceRealtimeMillis, mActualTimeMillis);
-            return new ManualTimeSuggestion(time);
-        }
-
-        static class Builder {
-
-            private long mInitialDeviceSystemClockMillis;
-            private long mInitialDeviceRealtimeMillis;
-            private long mActualTimeMillis;
-
-            Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day,
-                    int hourOfDay, int minute, int second) {
-                mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay,
-                        minute, second);
-                return this;
-            }
-
-            Builder setInitialDeviceRealtimeMillis(long realtimeMillis) {
-                mInitialDeviceRealtimeMillis = realtimeMillis;
-                return this;
-            }
-
-            Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay,
-                    int minute, int second) {
-                mActualTimeMillis =
-                        createUtcTime(year, monthInYear, day, hourOfDay, minute, second);
-                return this;
-            }
-
-            Scenario build() {
-                return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis,
-                        mActualTimeMillis);
-            }
-        }
-    }
-
-    private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId,
-            TimestampedValue<Long> utcTime) {
-        return new PhoneTimeSuggestion.Builder(phoneId)
-                .setUtcTime(utcTime)
-                .build();
-    }
-
-    private ManualTimeSuggestion createManualTimeSuggestion(long timeMillis) {
-        TimestampedValue<Long> utcTime =
-                new TimestampedValue<>(mScript.peekElapsedRealtimeMillis(), timeMillis);
-        return new ManualTimeSuggestion(utcTime);
-    }
-
-    private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
-            int second) {
-        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
-        cal.clear();
-        cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
-        return cal.getTimeInMillis();
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 84b495f..72a7f50 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -38,8 +38,6 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.timedetector.TimeDetectorStrategy.Callback;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -52,7 +50,6 @@
 
     private Context mMockContext;
     private StubbedTimeDetectorStrategy mStubbedTimeDetectorStrategy;
-    private Callback mMockCallback;
 
     private TimeDetectorService mTimeDetectorService;
     private HandlerThread mHandlerThread;
@@ -68,12 +65,10 @@
         mHandlerThread.start();
         mTestHandler = new TestHandler(mHandlerThread.getLooper());
 
-        mMockCallback = mock(Callback.class);
         mStubbedTimeDetectorStrategy = new StubbedTimeDetectorStrategy();
 
         mTimeDetectorService = new TimeDetectorService(
-                mMockContext, mTestHandler, mMockCallback,
-                mStubbedTimeDetectorStrategy);
+                mMockContext, mTestHandler, mStubbedTimeDetectorStrategy);
     }
 
     @After
@@ -100,13 +95,13 @@
 
     @Test
     public void testSuggestManualTime() throws Exception {
-        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+        doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
 
         ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion();
         mTimeDetectorService.suggestManualTime(manualTimeSuggestion);
         mTestHandler.assertTotalMessagesEnqueued(1);
 
-        verify(mMockContext).enforceCallingPermission(
+        verify(mMockContext).enforceCallingOrSelfPermission(
                 eq(android.Manifest.permission.SET_TIME),
                 anyString());
 
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
new file mode 100644
index 0000000..1aa3d8f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -0,0 +1,758 @@
+/*
+ * 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.timedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.PhoneTimeSuggestion;
+import android.content.Intent;
+import android.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
+import android.util.TimestampedValue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Duration;
+
+@RunWith(AndroidJUnit4.class)
+public class TimeDetectorStrategyImplTest {
+
+    private static final TimestampedValue<Long> ARBITRARY_CLOCK_INITIALIZATION_INFO =
+            new TimestampedValue<>(
+                    123456789L /* realtimeClockMillis */,
+                    createUtcTime(1977, 1, 1, 12, 0, 0));
+
+    private static final long ARBITRARY_TEST_TIME_MILLIS = createUtcTime(2018, 1, 1, 12, 0, 0);
+
+    private static final int ARBITRARY_PHONE_ID = 123456;
+
+    private static final long ONE_DAY_MILLIS = Duration.ofDays(1).toMillis();
+
+    private Script mScript;
+
+    @Before
+    public void setUp() {
+        mScript = new Script();
+    }
+
+    @Test
+    public void testSuggestPhoneTime_autoTimeEnabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        PhoneTimeSuggestion timeSuggestion =
+                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        int clockIncrement = 1000;
+        long expectedSystemClockMillis = testTimeMillis + clockIncrement;
+
+        mScript.simulateTimePassing(clockIncrement)
+                .simulatePhoneTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion);
+    }
+
+    @Test
+    public void testSuggestPhoneTime_emptySuggestionIgnored() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+        PhoneTimeSuggestion timeSuggestion =
+                mScript.generatePhoneTimeSuggestion(phoneId, null);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, null);
+    }
+
+    @Test
+    public void testSuggestPhoneTime_systemClockThreshold() {
+        int systemClockUpdateThresholdMillis = 1000;
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeThresholds(systemClockUpdateThresholdMillis)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        final int clockIncrement = 100;
+        int phoneId = ARBITRARY_PHONE_ID;
+
+        // Send the first time signal. It should be used.
+        {
+            long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+            PhoneTimeSuggestion timeSuggestion1 =
+                    mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+            TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
+
+            // Increment the the device clocks to simulate the passage of time.
+            mScript.simulateTimePassing(clockIncrement);
+
+            long expectedSystemClockMillis1 =
+                    TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+
+            mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
+                    .verifySystemClockWasSetAndResetCallTracking(
+                            expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+                    .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+        }
+
+        // Now send another time signal, but one that is too similar to the last one and should be
+        // stored, but not used to set the system clock.
+        {
+            int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
+            PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion(
+                    phoneId, mScript.peekSystemClockMillis() + underThresholdMillis);
+            mScript.simulateTimePassing(clockIncrement)
+                    .simulatePhoneTimeSuggestion(timeSuggestion2)
+                    .verifySystemClockWasNotSetAndResetCallTracking()
+                    .assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
+        }
+
+        // Now send another time signal, but one that is on the threshold and so should be used.
+        {
+            PhoneTimeSuggestion timeSuggestion3 = mScript.generatePhoneTimeSuggestion(
+                    phoneId,
+                    mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
+            mScript.simulateTimePassing(clockIncrement);
+
+            long expectedSystemClockMillis3 =
+                    TimeDetectorStrategy.getTimeAt(timeSuggestion3.getUtcTime(),
+                            mScript.peekElapsedRealtimeMillis());
+
+            mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
+                    .verifySystemClockWasSetAndResetCallTracking(
+                            expectedSystemClockMillis3, true /* expectNetworkBroadcast */)
+                    .assertLatestPhoneSuggestion(phoneId, timeSuggestion3);
+        }
+    }
+
+    @Test
+    public void testSuggestPhoneTime_multiplePhoneIdsAndBucketing() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        // There are 2 phones in this test. Phone 2 has a different idea of the current time.
+        // phone1Id < phone2Id (which is important because the strategy uses the lowest ID when
+        // multiple phone suggestions are available.
+        int phone1Id = ARBITRARY_PHONE_ID;
+        int phone2Id = ARBITRARY_PHONE_ID + 1;
+        long phone1TimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        long phone2TimeMillis = phone1TimeMillis + 60000;
+
+        final int clockIncrement = 999;
+
+        // Make a suggestion with phone2Id.
+        {
+            PhoneTimeSuggestion phone2TimeSuggestion =
+                    mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
+            mScript.simulateTimePassing(clockIncrement);
+
+            long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
+
+            mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
+                    .verifySystemClockWasSetAndResetCallTracking(
+                            expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+                    .assertLatestPhoneSuggestion(phone1Id, null)
+                    .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
+        }
+
+        mScript.simulateTimePassing(clockIncrement);
+
+        // Now make a different suggestion with phone1Id.
+        {
+            PhoneTimeSuggestion phone1TimeSuggestion =
+                    mScript.generatePhoneTimeSuggestion(phone1Id, phone1TimeMillis);
+            mScript.simulateTimePassing(clockIncrement);
+
+            long expectedSystemClockMillis = phone1TimeMillis + clockIncrement;
+
+            mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion)
+                    .verifySystemClockWasSetAndResetCallTracking(
+                            expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+                    .assertLatestPhoneSuggestion(phone1Id, phone1TimeSuggestion);
+
+        }
+
+        mScript.simulateTimePassing(clockIncrement);
+
+        // Make another suggestion with phone2Id. It should be stored but not used because the
+        // phone1Id suggestion will still "win".
+        {
+            PhoneTimeSuggestion phone2TimeSuggestion =
+                    mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
+            mScript.simulateTimePassing(clockIncrement);
+
+            mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
+                    .verifySystemClockWasNotSetAndResetCallTracking()
+                    .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
+        }
+
+        // Let enough time pass that phone1Id's suggestion should now be too old.
+        mScript.simulateTimePassing(TimeDetectorStrategyImpl.PHONE_BUCKET_SIZE_MILLIS);
+
+        // Make another suggestion with phone2Id. It should be used because the phoneId1
+        // is in an older "bucket".
+        {
+            PhoneTimeSuggestion phone2TimeSuggestion =
+                    mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
+            mScript.simulateTimePassing(clockIncrement);
+
+            long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
+
+            mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
+                    .verifySystemClockWasSetAndResetCallTracking(
+                            expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+                    .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
+        }
+    }
+
+    @Test
+    public void testSuggestPhoneTime_autoTimeDisabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(false);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+        PhoneTimeSuggestion timeSuggestion =
+                mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
+        mScript.simulateTimePassing(1000)
+                .simulatePhoneTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion);
+    }
+
+    @Test
+    public void testSuggestPhoneTime_invalidNitzReferenceTimesIgnored() {
+        final int systemClockUpdateThreshold = 2000;
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeThresholds(systemClockUpdateThreshold)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        int phoneId = ARBITRARY_PHONE_ID;
+
+        PhoneTimeSuggestion timeSuggestion1 =
+                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
+
+        // Initialize the strategy / device with a time set from a phone suggestion.
+        mScript.simulateTimePassing(100);
+        long expectedSystemClockMillis1 =
+                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // The UTC time increment should be larger than the system clock update threshold so we
+        // know it shouldn't be ignored for other reasons.
+        long validUtcTimeMillis = utcTime1.getValue() + (2 * systemClockUpdateThreshold);
+
+        // Now supply a new signal that has an obviously bogus reference time : older than the last
+        // one.
+        long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1;
+        TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
+                referenceTimeBeforeLastSignalMillis, validUtcTimeMillis);
+        PhoneTimeSuggestion timeSuggestion2 =
+                createPhoneTimeSuggestion(phoneId, utcTime2);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // Now supply a new signal that has an obviously bogus reference time : substantially in the
+        // future.
+        long referenceTimeInFutureMillis =
+                utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1;
+        TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
+                referenceTimeInFutureMillis, validUtcTimeMillis);
+        PhoneTimeSuggestion timeSuggestion3 =
+                createPhoneTimeSuggestion(phoneId, utcTime3);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // Just to prove validUtcTimeMillis is valid.
+        long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
+        TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
+                validReferenceTimeMillis, validUtcTimeMillis);
+        long expectedSystemClockMillis4 =
+                TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
+        PhoneTimeSuggestion timeSuggestion4 =
+                createPhoneTimeSuggestion(phoneId, utcTime4);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis4, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion4);
+    }
+
+    @Test
+    public void testSuggestPhoneTime_timeDetectionToggled() {
+        final int clockIncrementMillis = 100;
+        final int systemClockUpdateThreshold = 2000;
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeThresholds(systemClockUpdateThreshold)
+                .pokeAutoTimeDetectionEnabled(false);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        PhoneTimeSuggestion timeSuggestion1 =
+                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
+
+        // Simulate time passing.
+        mScript.simulateTimePassing(clockIncrementMillis);
+
+        // Simulate the time signal being received. It should not be used because auto time
+        // detection is off but it should be recorded.
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // Simulate more time passing.
+        mScript.simulateTimePassing(clockIncrementMillis);
+
+        long expectedSystemClockMillis1 =
+                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+
+        // Turn on auto time detection.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // Turn off auto time detection.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // Receive another valid time signal.
+        // It should be on the threshold and accounting for the clock increments.
+        PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion(
+                phoneId, mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
+
+        // Simulate more time passing.
+        mScript.simulateTimePassing(clockIncrementMillis);
+
+        long expectedSystemClockMillis2 = TimeDetectorStrategy.getTimeAt(
+                timeSuggestion2.getUtcTime(), mScript.peekElapsedRealtimeMillis());
+
+        // The new time, though valid, should not be set in the system clock because auto time is
+        // disabled.
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
+
+        // Turn on auto time detection.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis2, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
+    }
+
+    @Test
+    public void testSuggestPhoneTime_maxSuggestionAge() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        PhoneTimeSuggestion phoneSuggestion =
+                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        int clockIncrementMillis = 1000;
+
+        mScript.simulateTimePassing(clockIncrementMillis)
+                .simulatePhoneTimeSuggestion(phoneSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        testTimeMillis + clockIncrementMillis, true /* expectedNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
+
+        // Look inside and check what the strategy considers the current best phone suggestion.
+        assertEquals(phoneSuggestion, mScript.peekBestPhoneSuggestion());
+
+        // Simulate time passing, long enough that phoneSuggestion is now too old.
+        mScript.simulateTimePassing(TimeDetectorStrategyImpl.PHONE_MAX_AGE_MILLIS);
+
+        // Look inside and check what the strategy considers the current best phone suggestion. It
+        // should still be the, it's just no longer used.
+        assertNull(mScript.peekBestPhoneSuggestion());
+        mScript.assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
+    }
+
+    @Test
+    public void testSuggestManualTime_autoTimeDisabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(false);
+
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        ManualTimeSuggestion timeSuggestion = mScript.generateManualTimeSuggestion(testTimeMillis);
+        final int clockIncrement = 1000;
+        long expectedSystemClockMillis = testTimeMillis + clockIncrement;
+
+        mScript.simulateTimePassing(clockIncrement)
+                .simulateManualTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis, false /* expectNetworkBroadcast */);
+    }
+
+    @Test
+    public void testSuggestManualTime_retainsAutoSignal() {
+        // Configure the start state.
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+
+        // Simulate a phone suggestion.
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        PhoneTimeSuggestion phoneTimeSuggestion =
+                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        long expectedAutoClockMillis = phoneTimeSuggestion.getUtcTime().getValue();
+        final int clockIncrement = 1000;
+
+        // Simulate the passage of time.
+        mScript.simulateTimePassing(clockIncrement);
+        expectedAutoClockMillis += clockIncrement;
+
+        mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedAutoClockMillis, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+
+        // Simulate the passage of time.
+        mScript.simulateTimePassing(clockIncrement);
+        expectedAutoClockMillis += clockIncrement;
+
+        // Switch to manual.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+
+        // Simulate the passage of time.
+        mScript.simulateTimePassing(clockIncrement);
+        expectedAutoClockMillis += clockIncrement;
+
+        // Simulate a manual suggestion 1 day different from the auto suggestion.
+        long manualTimeMillis = testTimeMillis + ONE_DAY_MILLIS;
+        long expectedManualClockMillis = manualTimeMillis;
+        ManualTimeSuggestion manualTimeSuggestion =
+                mScript.generateManualTimeSuggestion(manualTimeMillis);
+        mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedManualClockMillis, false /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+
+        // Simulate the passage of time.
+        mScript.simulateTimePassing(clockIncrement);
+        expectedAutoClockMillis += clockIncrement;
+
+        // Switch back to auto.
+        mScript.simulateAutoTimeDetectionToggle();
+
+        mScript.verifySystemClockWasSetAndResetCallTracking(
+                        expectedAutoClockMillis, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+
+        // Switch back to manual - nothing should happen to the clock.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+    }
+
+    /**
+     * Manual suggestions should be ignored if auto time is enabled.
+     */
+    @Test
+    public void testSuggestManualTime_autoTimeEnabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        ManualTimeSuggestion timeSuggestion =
+                mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
+        final int clockIncrement = 1000;
+
+        mScript.simulateTimePassing(clockIncrement)
+                .simulateManualTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+    }
+
+    /**
+     * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
+     * like the real thing should, it also asserts preconditions.
+     */
+    private static class FakeCallback implements TimeDetectorStrategy.Callback {
+        private boolean mAutoTimeDetectionEnabled;
+        private boolean mWakeLockAcquired;
+        private long mElapsedRealtimeMillis;
+        private long mSystemClockMillis;
+        private int mSystemClockUpdateThresholdMillis = 2000;
+
+        // Tracking operations.
+        private boolean mSystemClockWasSet;
+        private Intent mBroadcastSent;
+
+        @Override
+        public int systemClockUpdateThresholdMillis() {
+            return mSystemClockUpdateThresholdMillis;
+        }
+
+        @Override
+        public boolean isAutoTimeDetectionEnabled() {
+            return mAutoTimeDetectionEnabled;
+        }
+
+        @Override
+        public void acquireWakeLock() {
+            if (mWakeLockAcquired) {
+                fail("Wake lock already acquired");
+            }
+            mWakeLockAcquired = true;
+        }
+
+        @Override
+        public long elapsedRealtimeMillis() {
+            return mElapsedRealtimeMillis;
+        }
+
+        @Override
+        public long systemClockMillis() {
+            assertWakeLockAcquired();
+            return mSystemClockMillis;
+        }
+
+        @Override
+        public void setSystemClock(long newTimeMillis) {
+            assertWakeLockAcquired();
+            mSystemClockWasSet = true;
+            mSystemClockMillis = newTimeMillis;
+        }
+
+        @Override
+        public void releaseWakeLock() {
+            assertWakeLockAcquired();
+            mWakeLockAcquired = false;
+        }
+
+        @Override
+        public void sendStickyBroadcast(Intent intent) {
+            assertNotNull(intent);
+            mBroadcastSent = intent;
+        }
+
+        // Methods below are for managing the fake's behavior.
+
+        void pokeSystemClockUpdateThreshold(int thresholdMillis) {
+            mSystemClockUpdateThresholdMillis = thresholdMillis;
+        }
+
+        void pokeElapsedRealtimeMillis(long elapsedRealtimeMillis) {
+            mElapsedRealtimeMillis = elapsedRealtimeMillis;
+        }
+
+        void pokeSystemClockMillis(long systemClockMillis) {
+            mSystemClockMillis = systemClockMillis;
+        }
+
+        void pokeAutoTimeDetectionEnabled(boolean enabled) {
+            mAutoTimeDetectionEnabled = enabled;
+        }
+
+        long peekElapsedRealtimeMillis() {
+            return mElapsedRealtimeMillis;
+        }
+
+        long peekSystemClockMillis() {
+            return mSystemClockMillis;
+        }
+
+        void simulateTimePassing(long incrementMillis) {
+            mElapsedRealtimeMillis += incrementMillis;
+            mSystemClockMillis += incrementMillis;
+        }
+
+        void simulateAutoTimeZoneDetectionToggle() {
+            mAutoTimeDetectionEnabled = !mAutoTimeDetectionEnabled;
+        }
+
+        void verifySystemClockNotSet() {
+            assertFalse(mSystemClockWasSet);
+        }
+
+        void verifySystemClockWasSet(long expectedSystemClockMillis) {
+            assertTrue(mSystemClockWasSet);
+            assertEquals(expectedSystemClockMillis, mSystemClockMillis);
+        }
+
+        void verifyIntentWasBroadcast() {
+            assertTrue(mBroadcastSent != null);
+        }
+
+        void verifyIntentWasNotBroadcast() {
+            assertNull(mBroadcastSent);
+        }
+
+        void resetCallTracking() {
+            mSystemClockWasSet = false;
+            mBroadcastSent = null;
+        }
+
+        private void assertWakeLockAcquired() {
+            assertTrue("The operation must be performed only after acquiring the wakelock",
+                    mWakeLockAcquired);
+        }
+    }
+
+    /**
+     * A fluent helper class for tests.
+     */
+    private class Script {
+
+        private final FakeCallback mFakeCallback;
+        private final TimeDetectorStrategyImpl mTimeDetectorStrategy;
+
+        Script() {
+            mFakeCallback = new FakeCallback();
+            mTimeDetectorStrategy = new TimeDetectorStrategyImpl();
+            mTimeDetectorStrategy.initialize(mFakeCallback);
+
+        }
+
+        Script pokeAutoTimeDetectionEnabled(boolean enabled) {
+            mFakeCallback.pokeAutoTimeDetectionEnabled(enabled);
+            return this;
+        }
+
+        Script pokeFakeClocks(TimestampedValue<Long> timeInfo) {
+            mFakeCallback.pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis());
+            mFakeCallback.pokeSystemClockMillis(timeInfo.getValue());
+            return this;
+        }
+
+        Script pokeThresholds(int systemClockUpdateThreshold) {
+            mFakeCallback.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
+            return this;
+        }
+
+        long peekElapsedRealtimeMillis() {
+            return mFakeCallback.peekElapsedRealtimeMillis();
+        }
+
+        long peekSystemClockMillis() {
+            return mFakeCallback.peekSystemClockMillis();
+        }
+
+        Script simulatePhoneTimeSuggestion(PhoneTimeSuggestion timeSuggestion) {
+            mTimeDetectorStrategy.suggestPhoneTime(timeSuggestion);
+            return this;
+        }
+
+        Script simulateManualTimeSuggestion(ManualTimeSuggestion timeSuggestion) {
+            mTimeDetectorStrategy.suggestManualTime(timeSuggestion);
+            return this;
+        }
+
+        Script simulateAutoTimeDetectionToggle() {
+            mFakeCallback.simulateAutoTimeZoneDetectionToggle();
+            mTimeDetectorStrategy.handleAutoTimeDetectionChanged();
+            return this;
+        }
+
+        Script simulateTimePassing(long clockIncrementMillis) {
+            mFakeCallback.simulateTimePassing(clockIncrementMillis);
+            return this;
+        }
+
+        Script verifySystemClockWasNotSetAndResetCallTracking() {
+            mFakeCallback.verifySystemClockNotSet();
+            mFakeCallback.verifyIntentWasNotBroadcast();
+            mFakeCallback.resetCallTracking();
+            return this;
+        }
+
+        Script verifySystemClockWasSetAndResetCallTracking(
+                long expectedSystemClockMillis, boolean expectNetworkBroadcast) {
+            mFakeCallback.verifySystemClockWasSet(expectedSystemClockMillis);
+            if (expectNetworkBroadcast) {
+                mFakeCallback.verifyIntentWasBroadcast();
+            }
+            mFakeCallback.resetCallTracking();
+            return this;
+        }
+
+        /**
+         * White box test info: Asserts the latest suggestion for the phone ID is as expected.
+         */
+        Script assertLatestPhoneSuggestion(int phoneId, PhoneTimeSuggestion expected) {
+            assertEquals(expected, mTimeDetectorStrategy.getLatestPhoneSuggestion(phoneId));
+            return this;
+        }
+
+        /**
+         * White box test info: Returns the phone suggestion that would be used, if any, given the
+         * current elapsed real time clock.
+         */
+        PhoneTimeSuggestion peekBestPhoneSuggestion() {
+            return mTimeDetectorStrategy.findBestPhoneSuggestionForTests();
+        }
+
+        /**
+         * Generates a ManualTimeSuggestion using the current elapsed realtime clock for the
+         * reference time.
+         */
+        ManualTimeSuggestion generateManualTimeSuggestion(long timeMillis) {
+            TimestampedValue<Long> utcTime =
+                    new TimestampedValue<>(mFakeCallback.peekElapsedRealtimeMillis(), timeMillis);
+            return new ManualTimeSuggestion(utcTime);
+        }
+
+        /**
+         * Generates a PhoneTimeSuggestion using the current elapsed realtime clock for the
+         * reference time.
+         */
+        PhoneTimeSuggestion generatePhoneTimeSuggestion(int phoneId, Long timeMillis) {
+            TimestampedValue<Long> time = null;
+            if (timeMillis != null) {
+                time = new TimestampedValue<>(peekElapsedRealtimeMillis(), timeMillis);
+            }
+            return createPhoneTimeSuggestion(phoneId, time);
+        }
+    }
+
+    private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId,
+            TimestampedValue<Long> utcTime) {
+        return new PhoneTimeSuggestion.Builder(phoneId)
+                .setUtcTime(utcTime)
+                .build();
+    }
+
+    private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
+            int second) {
+        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
+        cal.clear();
+        cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
+        return cal.getTimeInMillis();
+    }
+}
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index cd4f0da..cdba9a1 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -13,7 +13,7 @@
     ],
 
     static_libs: [
-        "frameworks-base-testutils-minus-mockito",
+        "frameworks-base-testutils",
         "services.core",
         "androidx.test.runner",
         "androidx.test.rules",
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index de2bba2..5928641 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -144,7 +144,7 @@
 
         // Put a finishing standard activity which will be reparented.
         final ActivityStack stack = createFullscreenStackWithSimpleActivityAt(display);
-        stack.topRunningActivityLocked().makeFinishingLocked();
+        stack.topRunningActivity().makeFinishingLocked();
 
         clearInvocations(homeStack);
         display.remove();
@@ -301,24 +301,18 @@
         doAnswer(invocation -> {
             display.positionStackAtTop(stack3, false);
             return true;
-        }).when(mSupervisor).removeTaskByIdLocked(eq(task4.mTaskId), anyBoolean(), anyBoolean(),
-                any());
+        }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
 
         // Removing stacks from the display while removing stacks.
         doAnswer(invocation -> {
             display.removeStack(stack2);
             return true;
-        }).when(mSupervisor).removeTaskByIdLocked(eq(task2.mTaskId), anyBoolean(), anyBoolean(),
-                any());
+        }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
 
         runnable.run();
-        verify(mSupervisor).removeTaskByIdLocked(eq(task4.mTaskId), anyBoolean(), anyBoolean(),
-                any());
-        verify(mSupervisor).removeTaskByIdLocked(eq(task3.mTaskId), anyBoolean(), anyBoolean(),
-                any());
-        verify(mSupervisor).removeTaskByIdLocked(eq(task2.mTaskId), anyBoolean(), anyBoolean(),
-                any());
-        verify(mSupervisor).removeTaskByIdLocked(eq(task1.mTaskId), anyBoolean(), anyBoolean(),
-                any());
+        verify(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
+        verify(mSupervisor).removeTask(eq(task3), anyBoolean(), anyBoolean(), any());
+        verify(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
+        verify(mSupervisor).removeTask(eq(task1), anyBoolean(), anyBoolean(), any());
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index dbcfa94..7a449b3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -107,7 +107,7 @@
     @Before
     public void setUp() throws Exception {
         mStack = new StackBuilder(mRootActivityContainer).build();
-        mTask = mStack.getChildAt(0);
+        mTask = mStack.getBottomMostTask();
         mActivity = mTask.getTopNonFinishingActivity();
 
         doReturn(false).when(mService).isBooting();
@@ -640,7 +640,7 @@
         // stacks. Then when mActivity is finishing, its stack will be invisible (no running
         // activities in the stack) that is the key condition to verify.
         final ActivityStack stack2 = new StackBuilder(mRootActivityContainer).build();
-        stack2.moveToBack("test", stack2.getChildAt(0));
+        stack2.moveToBack("test", stack2.getBottomMostTask());
 
         assertTrue(mStack.isTopStackOnDisplay());
 
@@ -948,9 +948,7 @@
     public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() {
         // Empty the home stack.
         final ActivityStack homeStack = mActivity.getDisplay().getHomeStack();
-        for (Task t : homeStack.getAllTasks()) {
-            homeStack.removeChild(t, "test");
-        }
+        homeStack.forAllTasks((t) -> { homeStack.removeChild(t, "test"); });
         mActivity.finishing = true;
         doReturn(false).when(mRootActivityContainer).resumeFocusedStacksTopActivities();
         spyOn(mStack);
@@ -974,9 +972,7 @@
     public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() {
         // Empty the home stack.
         final ActivityStack homeStack = mActivity.getDisplay().getHomeStack();
-        for (Task t : homeStack.getAllTasks()) {
-            homeStack.removeChild(t, "test");
-        }
+        homeStack.forAllTasks((t) -> { homeStack.removeChild(t, "test"); });
         mActivity.finishing = true;
         spyOn(mStack);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 7806d40..47f454e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -286,12 +286,12 @@
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Do not move display to back because there is still another stack.
-        stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask());
+        stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.getTopMostTask());
         verify(stack2).positionChildAtBottom(any(), eq(false) /* includingParents */);
 
         // Also move display to back because there is only one stack left.
         display.removeStack(stack1);
-        stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask());
+        stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.getTopMostTask());
         verify(stack2).positionChildAtBottom(any(), eq(true) /* includingParents */);
     }
 
@@ -545,7 +545,7 @@
     public void testShouldBeVisible_Finishing() {
         final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+        ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity();
         if (topRunningHomeActivity == null) {
             topRunningHomeActivity = new ActivityBuilder(mService)
                     .setStack(homeStack)
@@ -563,7 +563,7 @@
 
         topRunningHomeActivity.finishing = true;
         final ActivityRecord topRunningTranslucentActivity =
-                translucentStack.topRunningActivityLocked();
+                translucentStack.topRunningActivity();
         topRunningTranslucentActivity.finishing = true;
 
         // Home stack should be visible even there are no running activities.
@@ -883,7 +883,7 @@
         // removed from the task. Since the overlay activity should be removed as well, the task
         // should be empty.
         assertFalse(mTask.hasChild());
-        assertThat(mStack.getAllTasks()).isEmpty();
+        assertFalse(mStack.hasChild());
     }
 
     @Test
@@ -905,7 +905,7 @@
         mStack.handleAppDiedLocked(secondActivity.app);
 
         assertFalse(mTask.hasChild());
-        assertThat(mStack.getAllTasks()).isEmpty();
+        assertFalse(mStack.hasChild());
     }
 
     @Test
@@ -919,7 +919,7 @@
         mStack.handleAppDiedLocked(activity.app);
 
         assertEquals(1, mTask.getChildCount());
-        assertEquals(1, mStack.getAllTasks().size());
+        assertEquals(1, mStack.getChildCount());
     }
 
     @Test
@@ -933,7 +933,7 @@
         mStack.handleAppDiedLocked(activity.app);
 
         assertFalse(mTask.hasChild());
-        assertThat(mStack.getAllTasks()).isEmpty();
+        assertFalse(mStack.hasChild());
     }
 
     @Test
@@ -947,7 +947,7 @@
         mStack.handleAppDiedLocked(activity.app);
 
         assertEquals(1, mTask.getChildCount());
-        assertEquals(1, mStack.getAllTasks().size());
+        assertEquals(1, mStack.getChildCount());
     }
 
     @Test
@@ -961,7 +961,7 @@
         mStack.handleAppDiedLocked(activity.app);
 
         assertFalse(mTask.hasChild());
-        assertThat(mStack.getAllTasks()).isEmpty();
+        assertFalse(mStack.hasChild());
     }
 
     @Test
@@ -982,7 +982,7 @@
         final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
-        ActivityRecord activity = homeStack.topRunningActivityLocked();
+        ActivityRecord activity = homeStack.topRunningActivity();
         if (activity == null) {
             activity = new ActivityBuilder(mService)
                     .setStack(homeStack)
@@ -1020,7 +1020,7 @@
     }
 
     private ActivityRecord finishTopActivity(ActivityStack stack) {
-        final ActivityRecord activity = stack.topRunningActivityLocked();
+        final ActivityRecord activity = stack.topRunningActivity();
         assertNotNull(activity);
         activity.setState(STOPPED, "finishTopActivity");
         activity.makeFinishingLocked();
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 d5fdf98..018c10a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -54,6 +54,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -519,7 +520,7 @@
     private void assertNoTasks(ActivityDisplay display) {
         for (int i = display.getStackCount() - 1; i >= 0; --i) {
             final ActivityStack stack = display.getStackAt(i);
-            assertThat(stack.getAllTasks()).isEmpty();
+            assertFalse(stack.hasChild());
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index dae1052..07b7cf4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -59,7 +59,7 @@
     @Test
     public void testActivityFinish() {
         final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
-        final ActivityRecord activity = stack.getChildAt(0).getTopNonFinishingActivity();
+        final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
         assertTrue("Activity must be finished", mService.finishActivity(activity.appToken,
                 0 /* resultCode */, null /* resultData */,
                 Activity.DONT_FINISH_TASK_WITH_ACTIVITY));
@@ -75,7 +75,7 @@
         removeGlobalMinSizeRestriction();
         final ActivityStack stack = new StackBuilder(mRootActivityContainer)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
-        final Task task = stack.topTask();
+        final Task task = stack.getTopMostTask();
         WindowContainerTransaction t = new WindowContainerTransaction();
         Rect newBounds = new Rect(10, 10, 100, 100);
         t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100));
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 0382bf8..6f1e6df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -31,7 +31,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_UNSET;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
@@ -303,8 +302,8 @@
                 "closingWindow");
         closingWindow.mAnimatingExit = true;
         closingWindow.mRemoveOnExit = true;
-        closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
-                true /* performLayout */, false /* isVoiceInteraction */);
+        closingWindow.mActivityRecord.commitVisibility(
+                false /* visible */, true /* performLayout */);
 
         // We pretended that we were running an exit animation, but that should have been cleared up
         // by changing visibility of ActivityRecord
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 716e777..14c09eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -244,7 +244,6 @@
         // Add stack with activity.
         final ActivityStack stack = createTaskStackOnDisplay(dc);
         assertEquals(dc.getDisplayId(), stack.getDisplayContent().getDisplayId());
-        assertEquals(dc, stack.getParent().getParent());
         assertEquals(dc, stack.getDisplayContent());
 
         final Task task = createTaskInStack(stack, 0 /* userId */);
@@ -256,7 +255,6 @@
         // Move stack to first display.
         mDisplayContent.moveStackToDisplay(stack, true /* onTop */);
         assertEquals(mDisplayContent.getDisplayId(), stack.getDisplayContent().getDisplayId());
-        assertEquals(mDisplayContent, stack.getParent().getParent());
         assertEquals(mDisplayContent, stack.getDisplayContent());
         assertEquals(mDisplayContent, task.getDisplayContent());
         assertEquals(mDisplayContent, activity.getDisplayContent());
@@ -744,7 +742,7 @@
         final ActivityStack stack =
                 new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
                         .setDisplay(dc).build();
-        final ActivityRecord activity = stack.topTask().getTopNonFinishingActivity();
+        final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity();
 
         activity.setRequestedOrientation(newOrientation);
 
@@ -766,7 +764,7 @@
         final ActivityStack stack =
                 new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
                         .setDisplay(dc).build();
-        final ActivityRecord activity = stack.topTask().getTopNonFinishingActivity();
+        final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity();
 
         activity.setRequestedOrientation(newOrientation);
 
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 6ced816..0cc2626 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -106,6 +106,7 @@
 
     @Mock private ActivityStackSupervisor mSupervisor;
     @Mock private RootActivityContainer mRootActivityContainer;
+    @Mock private RootWindowContainer mRootWindowContainer;
     @Mock private IDevicePolicyManager mDevicePolicyManager;
     @Mock private IStatusBarService mStatusBarService;
     @Mock private WindowManagerService mWindowManager;
@@ -134,6 +135,7 @@
 
         mSupervisor.mRecentTasks = mRecentTasks;
         mSupervisor.mRootActivityContainer = mRootActivityContainer;
+        mRootActivityContainer.mRootWindowContainer = mRootWindowContainer;
 
         mLockTaskController = new LockTaskController(mContext, mSupervisor,
                 new ImmediatelyExecuteHandler());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 9f97c48..09d7b54 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -41,8 +41,12 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -112,6 +116,7 @@
     @Before
     public void setUp() throws Exception {
         mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
+        spyOn(mTaskPersister);
         mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
 
         // Set the recent tasks we should use for testing in this class.
@@ -171,6 +176,46 @@
     }
 
     @Test
+    public void testPersister() {
+        // Add some tasks, ensure the persister is woken
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.add(mTasks.get(1));
+        verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(0)), anyBoolean());
+        verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(1)), anyBoolean());
+        reset(mTaskPersister);
+
+        // Update a task, ensure the persister is woken
+        mRecentTasks.add(mTasks.get(0));
+        verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(0)), anyBoolean());
+        reset(mTaskPersister);
+
+        // Remove some tasks, ensure the persister is woken
+        mRecentTasks.remove(mTasks.get(0));
+        mRecentTasks.remove(mTasks.get(1));
+        verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(0)), anyBoolean());
+        verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(1)), anyBoolean());
+        reset(mTaskPersister);
+    }
+
+    @Test
+    public void testPersisterTrimmed() {
+        mRecentTasks.setOnlyTestVisibleRange();
+
+        // Limit the global maximum number of recent tasks to a fixed size
+        mRecentTasks.setGlobalMaxNumTasks(1 /* globalMaxNumTasks */);
+
+        mRecentTasks.add(mTasks.get(0));
+        verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(0)), anyBoolean());
+        reset(mTaskPersister);
+
+        // Add N+1 tasks to ensure the previous task is trimmed
+        mRecentTasks.add(mTasks.get(1));
+        verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(0)), anyBoolean());
+        verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(1)), anyBoolean());
+        assertTrimmed(mTasks.get(0));
+    }
+
+    @Test
     public void testAddTasksNoMultiple_expectNoTrim() {
         // Add same non-multiple-task document tasks will remove the task (to re-add it) but not
         // trim it
@@ -1282,10 +1327,10 @@
 
         @Override
         void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType,
-                int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
+                int ignoreWindowingMode, RootActivityContainer root,
                 int callingUid, boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) {
             mLastAllowed = allowed;
-            super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, activityDisplays,
+            super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, root,
                     callingUid, allowed, crossUser, profileIds);
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 4abab63..8fbb16c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -120,7 +120,7 @@
         final ActivityStack homeStack =
                 defaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
         defaultDisplay.positionStackAtTop(homeStack, false /* includingParents */);
-        ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+        ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity();
         if (topRunningHomeActivity == null) {
             topRunningHomeActivity = new ActivityBuilder(mService)
                     .setStack(homeStack)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 59c7c02..5417ade 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -147,7 +147,7 @@
     }
 
     private static void ensureStackPlacement(ActivityStack stack, ActivityRecord... activities) {
-        final Task task = stack.getAllTasks().get(0);
+        final Task task = stack.getBottomMostTask();
         final ArrayList<ActivityRecord> stackActivities = new ArrayList<>();
 
         task.forAllActivities((Consumer<ActivityRecord>) stackActivities::add, false);
@@ -320,7 +320,7 @@
                 .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
                 .setOnTop(true)
                 .build();
-        final Task task = primaryStack.topTask();
+        final Task task = primaryStack.getTopMostTask();
 
         // Resize dock stack.
         mService.resizeDockedStack(stackSize, taskSize, null, null, null);
@@ -340,7 +340,7 @@
         final ActivityStack targetStack = new StackBuilder(mRootActivityContainer)
                 .setOnTop(false)
                 .build();
-        final Task targetTask = targetStack.getChildAt(0);
+        final Task targetTask = targetStack.getBottomMostTask();
 
         // Create Recents on top of the display.
         final ActivityStack stack = new StackBuilder(mRootActivityContainer).setActivityType(
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 5c9ccae..66f4d2a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -82,8 +82,8 @@
         final int numFetchTasks = 5;
         ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
         mRunningTasks.getTasks(5, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
-                displays, -1 /* callingUid */, true /* allowed */, true /*crossUser */,
-                PROFILE_IDS);
+                mRootActivityContainer, -1 /* callingUid */, true /* allowed */,
+                true /*crossUser */, PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < numFetchTasks; i++) {
             assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -93,8 +93,8 @@
         // and does not crash
         tasks.clear();
         mRunningTasks.getTasks(100, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
-                displays, -1 /* callingUid */, true /* allowed */, true /* crossUser */,
-                PROFILE_IDS);
+                mRootActivityContainer, -1 /* callingUid */, true /* allowed */,
+                true /* crossUser */, PROFILE_IDS);
         assertThat(tasks).hasSize(numTasks);
         for (int i = 0; i < numTasks; i++) {
             assertEquals(numTasks - i - 1, tasks.get(i).id);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 212931b..46f95ed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
@@ -27,7 +26,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 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.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 
@@ -36,7 +35,6 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -54,7 +52,6 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Tests for Size Compatibility mode.
@@ -72,7 +69,7 @@
 
     private void setUpApp(ActivityDisplay display) {
         mStack = new StackBuilder(mRootActivityContainer).setDisplay(display).build();
-        mTask = mStack.getChildAt(0);
+        mTask = mStack.getBottomMostTask();
         mActivity = mTask.getTopNonFinishingActivity();
     }
 
@@ -265,7 +262,8 @@
         setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
 
         prepareUnresizable(1.4f /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
-        assertTrue(mActivity.inSizeCompatMode());
+        // The display aspect ratio 2.5 > 1.4 (max of activity), so the size is fitted.
+        assertFalse(mActivity.inSizeCompatMode());
 
         final Rect originalBounds = new Rect(mActivity.getBounds());
         final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
@@ -317,37 +315,35 @@
     @Test
     public void testResetNonVisibleActivity() {
         setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
-        final ActivityDisplay display = mStack.getDisplay();
-        spyOn(display);
-
         prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
+        final ActivityDisplay display = mStack.getDisplay();
+        // Resize the display so the activity is in size compatibility mode.
+        resizeDisplay(display, 900, 1800);
+
         mActivity.setState(STOPPED, "testSizeCompatMode");
         mActivity.mVisibleRequested = false;
         mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
-        // Make the parent bounds to be different so the activity is in size compatibility mode.
-        mTask.getWindowConfiguration().setAppBounds(new Rect(0, 0, 600, 1200));
 
         // Simulate the display changes orientation.
-        when(display.getLastOverrideConfigurationChanges()).thenReturn(
-                ActivityInfo.CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
-                        | ActivityInfo.CONFIG_WINDOW_CONFIGURATION);
-        mActivity.onConfigurationChanged(mTask.getConfiguration());
-        when(display.getLastOverrideConfigurationChanges()).thenCallRealMethod();
-        // The override configuration should not change so it is still in size compatibility mode.
+        final Configuration c = new Configuration();
+        display.getDisplayRotation().setRotation(ROTATION_90);
+        display.computeScreenConfiguration(c);
+        display.onRequestedOverrideConfigurationChanged(c);
+        // Size compatibility mode is able to handle orientation change so the process shouldn't be
+        // restarted and the override configuration won't be cleared.
+        verify(mActivity, never()).restartProcessIfVisible();
         assertTrue(mActivity.inSizeCompatMode());
 
         // Change display density
-        final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
-        displayContent.mBaseDisplayDensity = (int) (0.7f * displayContent.mBaseDisplayDensity);
-        final Configuration c = new Configuration();
-        displayContent.computeScreenConfiguration(c);
+        display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
+        display.computeScreenConfiguration(c);
         mService.mAmInternal = mock(ActivityManagerInternal.class);
-        mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
+        display.onRequestedOverrideConfigurationChanged(c);
 
         // The override configuration should be reset and the activity's process will be killed.
         assertFalse(mActivity.inSizeCompatMode());
         verify(mActivity).restartProcessIfVisible();
-        mLockRule.runWithScissors(mService.mH, () -> { }, TimeUnit.SECONDS.toMillis(3));
+        waitHandlerIdle(mService.mH);
         verify(mService.mAmInternal).killProcess(
                 eq(mActivity.app.mName), eq(mActivity.app.mUid), anyString());
     }
@@ -362,7 +358,6 @@
         ActivityRecord activity = mActivity;
         activity.setState(ActivityStack.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
         prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
-        ensureActivityConfiguration();
         assertFalse(mActivity.inSizeCompatMode());
 
         final ArrayList<IBinder> compatTokens = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index d3b68e0..a172b65 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -287,6 +287,10 @@
         // Mock root, some default display, and home stack.
         spyOn(mWmService.mRoot);
         final ActivityDisplay display = mAtmService.mRootActivityContainer.getDefaultDisplay();
+        // Set default display to be in fullscreen mode. Devices with PC feature may start their
+        // default display in freeform mode but some of tests in WmTests have implicit assumption on
+        // that the default display is in fullscreen mode.
+        display.setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN);
         spyOn(display);
         spyOn(display.mDisplayContent);
         final ActivityStack homeStack = display.getStack(
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 599edb1..39e7885 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -235,7 +235,7 @@
         ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
         ActivityStack stack = new StackBuilder(mRootActivityContainer).setDisplay(display)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
-        Task task = stack.getChildAt(0);
+        Task task = stack.getBottomMostTask();
         task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
         DisplayInfo info = new DisplayInfo();
         display.mDisplay.getDisplayInfo(info);
@@ -276,7 +276,7 @@
 
         ActivityStack stack = new StackBuilder(mRootActivityContainer)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
-        Task task = stack.getChildAt(0);
+        Task task = stack.getBottomMostTask();
         ActivityRecord root = task.getTopNonFinishingActivity();
 
         assertEquals(fullScreenBounds, task.getBounds());
@@ -337,7 +337,7 @@
                 display.getRequestedOverrideConfiguration());
         ActivityStack stack = new StackBuilder(mRootActivityContainer)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
-        Task task = stack.getChildAt(0);
+        Task task = stack.getBottomMostTask();
         ActivityRecord root = task.getTopNonFinishingActivity();
 
         final WindowContainer parentWindowContainer =
@@ -803,7 +803,7 @@
 
     private Task getTestTask() {
         final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
-        return stack.getChildAt(0);
+        return stack.getBottomMostTask();
     }
 
     private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 6ebaf29..7174e5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -18,7 +18,6 @@
 
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.TRANSIT_UNSET;
 
 import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THEME;
 import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL;
@@ -50,8 +49,8 @@
     public void testGetClosingApps_closing() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
-        closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
-                true /* performLayout */, false /* isVoiceInteraction */);
+        closingWindow.mActivityRecord.commitVisibility(
+                false /* visible */, true /* performLayout */);
         final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mActivityRecord);
         final ArraySet<Task> closingTasks = new ArraySet<>();
@@ -66,10 +65,10 @@
                 "closingWindow");
         final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
                 FIRST_APPLICATION_WINDOW, "openingWindow");
-        closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
-                true /* performLayout */, false /* isVoiceInteraction */);
-        openingWindow.mActivityRecord.commitVisibility(null, true /* visible */, TRANSIT_UNSET,
-                true /* performLayout */, false /* isVoiceInteraction */);
+        closingWindow.mActivityRecord.commitVisibility(
+                false /* visible */, true /* performLayout */);
+        openingWindow.mActivityRecord.commitVisibility(
+                true /* visible */, true /* performLayout */);
         final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mActivityRecord);
         final ArraySet<Task> closingTasks = new ArraySet<>();
@@ -81,8 +80,8 @@
     public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
-        closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
-                true /* performLayout */, false /* isVoiceInteraction */);
+        closingWindow.mActivityRecord.commitVisibility(
+                false /* visible */, true /* performLayout */);
         final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mActivityRecord);
         final ArraySet<Task> closingTasks = new ArraySet<>();
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 735b9a1..198b4c3 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -16,11 +16,14 @@
 
 package com.android.server.soundtrigger;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.ModelParams;
 import android.hardware.soundtrigger.SoundTrigger;
 import android.hardware.soundtrigger.SoundTrigger.GenericRecognitionEvent;
 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
@@ -28,6 +31,7 @@
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
 import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
@@ -605,6 +609,67 @@
         return STATUS_ERROR;
     }
 
+    int setParameter(UUID modelId, @ModelParams int modelParam, int value) {
+        synchronized (mLock) {
+            MetricsLogger.count(mContext, "sth_set_parameter", 1);
+            if (modelId == null || mModule == null) {
+                return SoundTrigger.STATUS_ERROR;
+            }
+            ModelData modelData = mModelDataMap.get(modelId);
+            if (modelData == null) {
+                Slog.w(TAG, "SetParameter: Invalid model id:" + modelId);
+                return SoundTrigger.STATUS_BAD_VALUE;
+            }
+            if (!modelData.isModelLoaded()) {
+                Slog.i(TAG, "SetParameter: Given model is not loaded:" + modelId);
+                return SoundTrigger.STATUS_BAD_VALUE;
+            }
+
+            return mModule.setParameter(modelData.getHandle(), modelParam, value);
+        }
+    }
+
+    int getParameter(@NonNull UUID modelId, @ModelParams int modelParam)
+            throws UnsupportedOperationException, IllegalArgumentException {
+        synchronized (mLock) {
+            MetricsLogger.count(mContext, "sth_get_parameter", 1);
+            if (mModule == null) {
+                throw new UnsupportedOperationException("SoundTriggerModule not initialized");
+            }
+
+            ModelData modelData = mModelDataMap.get(modelId);
+            if (modelData == null) {
+                throw new IllegalArgumentException("Invalid model id:" + modelId);
+            }
+            if (!modelData.isModelLoaded()) {
+                throw new UnsupportedOperationException("Given model is not loaded:" + modelId);
+            }
+
+            return mModule.getParameter(modelData.getHandle(), modelParam);
+        }
+    }
+
+    @Nullable
+    ModelParamRange queryParameter(@NonNull UUID modelId, @ModelParams int modelParam) {
+        synchronized (mLock) {
+            MetricsLogger.count(mContext, "sth_query_parameter", 1);
+            if (mModule == null) {
+                return null;
+            }
+            ModelData modelData = mModelDataMap.get(modelId);
+            if (modelData == null) {
+                Slog.w(TAG, "queryParameter: Invalid model id:" + modelId);
+                return null;
+            }
+            if (!modelData.isModelLoaded()) {
+                Slog.i(TAG, "queryParameter: Given model is not loaded:" + modelId);
+                return null;
+            }
+
+            return mModule.queryParameter(modelData.getHandle(), modelParam);
+        }
+    }
+
     //---- SoundTrigger.StatusListener methods
     @Override
     public void onRecognition(RecognitionEvent event) {
@@ -653,15 +718,15 @@
         }
         ModelData model = getModelDataForLocked(event.soundModelHandle);
         if (model == null || !model.isGenericModel()) {
-            Slog.w(TAG, "Generic recognition event: Model does not exist for handle: " +
-                    event.soundModelHandle);
+            Slog.w(TAG, "Generic recognition event: Model does not exist for handle: "
+                    + event.soundModelHandle);
             return;
         }
 
         IRecognitionStatusCallback callback = model.getCallback();
         if (callback == null) {
-            Slog.w(TAG, "Generic recognition event: Null callback for model handle: " +
-                    event.soundModelHandle);
+            Slog.w(TAG, "Generic recognition event: Null callback for model handle: "
+                    + event.soundModelHandle);
             return;
         }
 
@@ -678,8 +743,8 @@
 
         RecognitionConfig config = model.getRecognitionConfig();
         if (config == null) {
-            Slog.w(TAG, "Generic recognition event: Null RecognitionConfig for model handle: " +
-                    event.soundModelHandle);
+            Slog.w(TAG, "Generic recognition event: Null RecognitionConfig for model handle: "
+                    + event.soundModelHandle);
             return;
         }
 
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 1dd3972..96d2df1 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -22,7 +22,9 @@
 import static android.content.pm.PackageManager.GET_META_DATA;
 import static android.content.pm.PackageManager.GET_SERVICES;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.hardware.soundtrigger.SoundTrigger.STATUS_BAD_VALUE;
 import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR;
+import static android.hardware.soundtrigger.SoundTrigger.STATUS_NO_INIT;
 import static android.hardware.soundtrigger.SoundTrigger.STATUS_OK;
 import static android.provider.Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY;
 import static android.provider.Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT;
@@ -39,9 +41,11 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.ModelParams;
 import android.hardware.soundtrigger.SoundTrigger;
 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
 import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
 import android.hardware.soundtrigger.SoundTrigger.SoundModel;
@@ -683,6 +687,101 @@
                 return properties;
             }
         }
+
+        @Override
+        public int setParameter(ParcelUuid soundModelId,
+                @ModelParams int modelParam, int value) {
+            enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
+            if (!isInitialized()) return STATUS_NO_INIT;
+            if (DEBUG) {
+                Slog.d(TAG, "setParameter(): id=" + soundModelId
+                        + ", param=" + modelParam
+                        + ", value=" + value);
+            }
+
+            sEventLogger.log(new SoundTriggerLogger.StringEvent(
+                    "setParameter(): id=" + soundModelId
+                            + ", param=" + modelParam
+                            + ", value=" + value));
+
+            synchronized (mLock) {
+                SoundModel soundModel = mLoadedModels.get(soundModelId.getUuid());
+                if (soundModel == null) {
+                    Slog.e(TAG, soundModelId + " is not loaded. Loaded models: "
+                            + mLoadedModels.toString());
+
+                    sEventLogger.log(new SoundTriggerLogger.StringEvent("setParameter(): "
+                            + soundModelId + " is not loaded"));
+
+                    return STATUS_BAD_VALUE;
+                }
+
+                return mSoundTriggerHelper.setParameter(soundModel.uuid, modelParam, value);
+            }
+        }
+
+        @Override
+        public int getParameter(@NonNull ParcelUuid soundModelId,
+                @ModelParams int modelParam)
+                throws UnsupportedOperationException, IllegalArgumentException {
+            enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
+            if (!isInitialized()) {
+                throw new UnsupportedOperationException("SoundTriggerHelper not initialized");
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "getParameter(): id=" + soundModelId
+                        + ", param=" + modelParam);
+            }
+
+            sEventLogger.log(new SoundTriggerLogger.StringEvent(
+                    "getParameter(): id=" + soundModelId
+                            + ", param=" + modelParam));
+
+            synchronized (mLock) {
+                SoundModel soundModel = mLoadedModels.get(soundModelId.getUuid());
+                if (soundModel == null) {
+                    Slog.e(TAG, soundModelId + " is not loaded");
+
+                    sEventLogger.log(new SoundTriggerLogger.StringEvent("getParameter(): "
+                            + soundModelId + " is not loaded"));
+
+                    throw new IllegalArgumentException("sound model is not loaded");
+                }
+
+                return mSoundTriggerHelper.getParameter(soundModel.uuid, modelParam);
+            }
+        }
+
+        @Override
+        @Nullable
+        public ModelParamRange queryParameter(@NonNull ParcelUuid soundModelId,
+                @ModelParams int modelParam) {
+            enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
+            if (!isInitialized()) return null;
+            if (DEBUG) {
+                Slog.d(TAG, "queryParameter(): id=" + soundModelId
+                        + ", param=" + modelParam);
+            }
+
+            sEventLogger.log(new SoundTriggerLogger.StringEvent(
+                    "queryParameter(): id=" + soundModelId
+                            + ", param=" + modelParam));
+
+            synchronized (mLock) {
+                SoundModel soundModel = mLoadedModels.get(soundModelId.getUuid());
+                if (soundModel == null) {
+                    Slog.e(TAG, soundModelId + " is not loaded");
+
+                    sEventLogger.log(new SoundTriggerLogger.StringEvent(
+                            "queryParameter(): "
+                                    + soundModelId + " is not loaded"));
+
+                    return null;
+                }
+
+                return mSoundTriggerHelper.queryParameter(soundModel.uuid, modelParam);
+            }
+        }
     }
 
     /**
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index f89bbc7..3940a3b 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -116,7 +116,8 @@
             ApnSetting.TYPE_CBS,
             ApnSetting.TYPE_IA,
             ApnSetting.TYPE_EMERGENCY,
-            ApnSetting.TYPE_MCX
+            ApnSetting.TYPE_MCX,
+            ApnSetting.TYPE_XCAP,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApnType {
@@ -463,9 +464,7 @@
             DataFailCause.UNKNOWN,
             DataFailCause.RADIO_NOT_AVAILABLE,
             DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
-            DataFailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN,
             DataFailCause.LOST_CONNECTION,
-            DataFailCause.RESET_BY_FRAMEWORK
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DataFailureCause {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 91646ad..001888c 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1052,9 +1052,6 @@
      *
      * When {@code false}, the old behavior is used, where the toggle in accessibility settings is
      * used to set the IMS stack's RTT enabled state.
-     *
-     * @deprecated -- this flag no longer does anything. Remove once the new behavior is verified.
-     *
      * @hide
      */
     public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
@@ -2092,12 +2089,6 @@
             "allow_metered_network_for_cert_download_bool";
 
     /**
-     * Carrier specified WiFi networks.
-     * @hide
-     */
-    public static final String KEY_CARRIER_WIFI_STRING_ARRAY = "carrier_wifi_string_array";
-
-    /**
      * Time delay (in ms) after which we show the notification to switch the preferred
      * network.
      * @hide
@@ -3046,7 +3037,6 @@
         /**
          * Location information during (and after) an emergency call is only provided over control
          * plane signaling from the network.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_CP_ONLY = 0;
 
@@ -3054,7 +3044,6 @@
          * Location information during (and after) an emergency call is provided over the data
          * plane and serviced by the framework GNSS service, but if it fails, the carrier also
          * supports control plane backup signaling.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK = 1;
 
@@ -3062,7 +3051,6 @@
          * Location information during (and after) an emergency call is provided over the data plane
          * and serviced by the framework GNSS service only. There is no backup signalling over the
          * control plane if it fails.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_DP_ONLY = 2;
 
@@ -3169,11 +3157,21 @@
          * {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
          * <p>
          * The default value for this configuration is {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
-         * @hide
          */
         public static final String KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT = KEY_PREFIX
                 + "es_supl_control_plane_support_int";
 
+        /**
+         * A list of roaming PLMNs where SUPL ES mode does not support a control-plane mechanism to
+         * get a user's location in the event that data plane SUPL fails or is otherwise
+         * unavailable.
+         * <p>
+         * A string array of PLMNs that do not support a control-plane mechanism for getting a
+         * user's location for SUPL ES.
+         */
+        public static final String KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY =
+                KEY_PREFIX + "es_supl_data_plane_only_roaming_plmn_string_array";
+
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
             defaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
@@ -3190,63 +3188,11 @@
             defaults.putString(KEY_NFW_PROXY_APPS_STRING, "");
             defaults.putInt(KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                     SUPL_EMERGENCY_MODE_TYPE_CP_ONLY);
+            defaults.putStringArray(KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY, null);
             return defaults;
         }
     }
 
-    /**
-     * Wi-Fi configs used in Carrier Wi-Fi application.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final class Wifi {
-        /** Prefix of all Wifi.KEY_* constants. */
-        public static final String KEY_PREFIX = "wifi.";
-
-        /**
-         * Whenever any information under wifi namespace is changed, the version should be
-         * incremented by 1 so that the device is able to figure out the latest profiles based on
-         * the version.
-         */
-        public static final String KEY_CARRIER_PROFILES_VERSION_INT =
-                KEY_PREFIX + "carrier_profiles_version_int";
-
-        /**
-         * It contains the package name of connection manager that the carrier owns.
-         *
-         * <P>Once it is installed, the profiles installed by Carrier Wi-Fi Application
-         * will be deleted.
-         * Once it is uninstalled, Carrier Wi-Fi Application will re-install the latest profiles.
-         */
-        public static final String KEY_CARRIER_CONNECTION_MANAGER_PACKAGE_STRING =
-                KEY_PREFIX + "carrier_connection_manager_package_string";
-        /**
-         * It is to have the list of wifi networks profiles which contain the information about
-         * the wifi-networks to which carrier wants the device to connect.
-         */
-        public static final String KEY_NETWORK_PROFILES_STRING_ARRAY =
-                KEY_PREFIX + "network_profiles_string_array";
-
-        /**
-         * It is to have the list of Passpoint profiles which contain the information about
-         * the Passpoint networks to which carrier wants the device to connect.
-         */
-        public static final String KEY_PASSPOINT_PROFILES_STRING_ARRAY =
-                KEY_PREFIX + "passpoint_profiles_string_array";
-
-        private static PersistableBundle getDefaults() {
-            PersistableBundle defaults = new PersistableBundle();
-            defaults.putInt(KEY_CARRIER_PROFILES_VERSION_INT, -1);
-            defaults.putString(KEY_CARRIER_CONNECTION_MANAGER_PACKAGE_STRING, null);
-            defaults.putStringArray(KEY_NETWORK_PROFILES_STRING_ARRAY, null);
-            defaults.putStringArray(KEY_PASSPOINT_PROFILES_STRING_ARRAY, null);
-            return defaults;
-        }
-
-        private Wifi() {}
-    }
-
    /**
     * An int array containing CDMA enhanced roaming indicator values for Home (non-roaming) network.
     * The default values come from 3GPP2 C.R1001 table 8.1-1.
@@ -3548,7 +3494,7 @@
         sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
         sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
         sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
-        sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, true);
+        sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
         sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
@@ -3699,7 +3645,6 @@
         sDefaults.putBoolean(KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL, false);
         sDefaults.putBoolean(KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL, false);
         sDefaults.putBoolean(KEY_HIDE_DIGITS_HELPER_TEXT_ON_STK_INPUT_SCREEN_BOOL, true);
-        sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null);
         sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
         sDefaults.putInt(KEY_EMERGENCY_NOTIFICATION_DELAY_INT, -1);
         sDefaults.putBoolean(KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL, true);
@@ -3828,7 +3773,6 @@
         /* Default value is 60 seconds. */
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000);
         sDefaults.putAll(Gps.getDefaults());
-        sDefaults.putAll(Wifi.getDefaults());
         sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
                 new int[] {
                         1 /* Roaming Indicator Off */
diff --git a/telephony/java/android/telephony/CbGeoUtils.java b/telephony/java/android/telephony/CbGeoUtils.java
index ce5e3f3..d4d4e13 100644
--- a/telephony/java/android/telephony/CbGeoUtils.java
+++ b/telephony/java/android/telephony/CbGeoUtils.java
@@ -18,9 +18,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.os.Build;
 import android.text.TextUtils;
 
+import com.android.internal.telephony.util.TelephonyUtils;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -262,7 +263,7 @@
         @Override
         public String toString() {
             String str = "Polygon: ";
-            if (Build.IS_DEBUGGABLE) {
+            if (TelephonyUtils.IS_DEBUGGABLE) {
                 str += mVertices;
             }
             return str;
@@ -298,7 +299,7 @@
         @Override
         public String toString() {
             String str = "Circle: ";
-            if (Build.IS_DEBUGGABLE) {
+            if (TelephonyUtils.IS_DEBUGGABLE) {
                 str += mCenter + ", radius = " + mRadiusMeter;
             }
 
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index 7bdf1f5..e1c4bef 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -950,14 +950,10 @@
     public static final int UNKNOWN = 0x10000;
     /** Data fail due to radio not unavailable. */
     public static final int RADIO_NOT_AVAILABLE = 0x10001;                   /* no retry */
-    /** @hide */
+    /** Data fail due to unacceptable network parameter. */
     public static final int UNACCEPTABLE_NETWORK_PARAMETER = 0x10002;        /* no retry */
-    /** @hide */
-    public static final int CONNECTION_TO_DATACONNECTIONAC_BROKEN = 0x10003;
     /** Data connection was lost. */
     public static final int LOST_CONNECTION = 0x10004;
-    /** @hide */
-    public static final int RESET_BY_FRAMEWORK = 0x10005;
 
     /**
      * Data handover failed.
@@ -1361,10 +1357,7 @@
         sFailCauseMap.put(RADIO_NOT_AVAILABLE, "RADIO_NOT_AVAILABLE");
         sFailCauseMap.put(UNACCEPTABLE_NETWORK_PARAMETER,
                 "UNACCEPTABLE_NETWORK_PARAMETER");
-        sFailCauseMap.put(CONNECTION_TO_DATACONNECTIONAC_BROKEN,
-                "CONNECTION_TO_DATACONNECTIONAC_BROKEN");
         sFailCauseMap.put(LOST_CONNECTION, "LOST_CONNECTION");
-        sFailCauseMap.put(RESET_BY_FRAMEWORK, "RESET_BY_FRAMEWORK");
     }
 
     private DataFailCause() {
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index fe273b2..95aa101 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -34,6 +34,8 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import com.android.internal.telephony.util.TelephonyUtils;
+
 import java.util.List;
 
 /**
@@ -174,7 +176,7 @@
         }
         Log.e(TAG, errorMsg);
         try {
-            if (Build.IS_DEBUGGABLE) {
+            if (TelephonyUtils.IS_DEBUGGABLE) {
                 Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show();
             }
         } catch (Throwable t) {
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
index 465b6aa..0ceb103 100644
--- a/telephony/java/android/telephony/NetworkScanRequest.java
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -20,10 +20,10 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Defines a request to peform a network scan.
@@ -221,9 +221,11 @@
 
     private NetworkScanRequest(Parcel in) {
         mScanType = in.readInt();
-        mSpecifiers = (RadioAccessSpecifier[]) in.readParcelableArray(
-                Object.class.getClassLoader(),
-                RadioAccessSpecifier.class);
+        Parcelable[] tempSpecifiers = in.readParcelableArray(Object.class.getClassLoader());
+        mSpecifiers = new RadioAccessSpecifier[tempSpecifiers.length];
+        for (int i = 0; i < tempSpecifiers.length; i++) {
+            mSpecifiers[i] = (RadioAccessSpecifier) tempSpecifiers[i];
+        }
         mSearchPeriodicity = in.readInt();
         mMaxSearchTime = in.readInt();
         mIncrementalResults = in.readBoolean();
diff --git a/telephony/java/android/telephony/PhoneNumberRange.java b/telephony/java/android/telephony/PhoneNumberRange.java
index e6f107e..2b199d2 100644
--- a/telephony/java/android/telephony/PhoneNumberRange.java
+++ b/telephony/java/android/telephony/PhoneNumberRange.java
@@ -85,18 +85,18 @@
     }
 
     private PhoneNumberRange(Parcel in) {
-        mCountryCode = in.readStringNoHelper();
-        mPrefix = in.readStringNoHelper();
-        mLowerBound = in.readStringNoHelper();
-        mUpperBound = in.readStringNoHelper();
+        mCountryCode = in.readString();
+        mPrefix = in.readString();
+        mLowerBound = in.readString();
+        mUpperBound = in.readString();
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStringNoHelper(mCountryCode);
-        dest.writeStringNoHelper(mPrefix);
-        dest.writeStringNoHelper(mLowerBound);
-        dest.writeStringNoHelper(mUpperBound);
+        dest.writeString(mCountryCode);
+        dest.writeString(mPrefix);
+        dest.writeString(mLowerBound);
+        dest.writeString(mUpperBound);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index ebb5175..b71bd85 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -30,7 +30,6 @@
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.graphics.Typeface;
-import android.os.Build;
 import android.os.Parcel;
 import android.os.ParcelUuid;
 import android.os.Parcelable;
@@ -38,6 +37,8 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 
+import com.android.internal.telephony.util.TelephonyUtils;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -665,8 +666,8 @@
             int id = source.readInt();
             String iccId = source.readString();
             int simSlotIndex = source.readInt();
-            CharSequence displayName = source.readCharSequence();
-            CharSequence carrierName = source.readCharSequence();
+            CharSequence displayName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            CharSequence carrierName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
             int nameSource = source.readInt();
             int iconTint = source.readInt();
             String number = source.readString();
@@ -685,8 +686,8 @@
             int carrierid = source.readInt();
             int profileClass = source.readInt();
             int subType = source.readInt();
-            String[] ehplmns = source.readStringArray();
-            String[] hplmns = source.readStringArray();
+            String[] ehplmns = source.createStringArray();
+            String[] hplmns = source.createStringArray();
             String groupOwner = source.readString();
             UiccAccessRule[] carrierConfigAccessRules = source.createTypedArray(
                 UiccAccessRule.CREATOR);
@@ -711,8 +712,8 @@
         dest.writeInt(mId);
         dest.writeString(mIccId);
         dest.writeInt(mSimSlotIndex);
-        dest.writeCharSequence(mDisplayName);
-        dest.writeCharSequence(mCarrierName);
+        TextUtils.writeToParcel(mDisplayName, dest, 0);
+        TextUtils.writeToParcel(mCarrierName, dest, 0);
         dest.writeInt(mNameSource);
         dest.writeInt(mIconTint);
         dest.writeString(mNumber);
@@ -748,7 +749,7 @@
     public static String givePrintableIccid(String iccId) {
         String iccIdToPrint = null;
         if (iccId != null) {
-            if (iccId.length() > 9 && !Build.IS_DEBUGGABLE) {
+            if (iccId.length() > 9 && !TelephonyUtils.IS_DEBUGGABLE) {
                 iccIdToPrint = iccId.substring(0, 9) + Rlog.pii(false, iccId.substring(9));
             } else {
                 iccIdToPrint = iccId;
@@ -764,7 +765,8 @@
         return "{id=" + mId + " iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
                 + " carrierId=" + mCarrierId + " displayName=" + mDisplayName
                 + " carrierName=" + mCarrierName + " nameSource=" + mNameSource
-                + " iconTint=" + mIconTint + " mNumber=" + Rlog.pii(Build.IS_DEBUGGABLE, mNumber)
+                + " iconTint=" + mIconTint
+                + " mNumber=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumber)
                 + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
                 + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
                 + " nativeAccessRules " + Arrays.toString(mNativeAccessRules)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index cf705f4..a82ae88 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -48,7 +48,6 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.ParcelUuid;
 import android.os.Process;
@@ -64,6 +63,7 @@
 import com.android.internal.telephony.ISetOpportunisticDataCallback;
 import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.util.HandlerExecutor;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8f5c1e9..5b7ad12 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -430,14 +430,14 @@
         int modemCount = 1;
         switch (getMultiSimConfiguration()) {
             case UNKNOWN:
-                ConnectivityManager cm = mContext == null ? null : (ConnectivityManager) mContext
-                        .getSystemService(Context.CONNECTIVITY_SERVICE);
+                modemCount = MODEM_COUNT_SINGLE_MODEM;
                 // check for voice and data support, 0 if not supported
-                if (!isVoiceCapable() && !isSmsCapable() && cm != null
-                        && !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
-                    modemCount = MODEM_COUNT_NO_MODEM;
-                } else {
-                    modemCount = MODEM_COUNT_SINGLE_MODEM;
+                if (!isVoiceCapable() && !isSmsCapable() && mContext != null) {
+                    ConnectivityManager cm = (ConnectivityManager) mContext
+                            .getSystemService(Context.CONNECTIVITY_SERVICE);
+                    if (cm != null && !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
+                        modemCount = MODEM_COUNT_NO_MODEM;
+                    }
                 }
                 break;
             case DSDS:
@@ -716,31 +716,6 @@
     public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
 
     /**
-     * Broadcast intent action indicating that a precise call state
-     * (cellular) on the device has changed.
-     *
-     * <p>
-     * The {@link #EXTRA_RINGING_CALL_STATE} extra indicates the ringing call state.
-     * The {@link #EXTRA_FOREGROUND_CALL_STATE} extra indicates the foreground call state.
-     * The {@link #EXTRA_BACKGROUND_CALL_STATE} extra indicates the background call state.
-     *
-     * <p class="note">
-     * Requires the READ_PRECISE_PHONE_STATE permission.
-     *
-     * @see #EXTRA_RINGING_CALL_STATE
-     * @see #EXTRA_FOREGROUND_CALL_STATE
-     * @see #EXTRA_BACKGROUND_CALL_STATE
-     *
-     * <p class="note">
-     * Requires the READ_PRECISE_PHONE_STATE permission.
-     * @deprecated use {@link PhoneStateListener#LISTEN_PRECISE_CALL_STATE} instead
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PRECISE_CALL_STATE_CHANGED =
-            "android.intent.action.PRECISE_CALL_STATE";
-
-    /**
      * Broadcast intent action indicating that call disconnect cause has changed.
      *
      * <p>
@@ -762,78 +737,6 @@
     /**
      * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
      * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
-     * containing the state of the current ringing call.
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_RINGING_CALL_STATE = "ringing_state";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
-     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
-     * containing the state of the current foreground call.
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_FOREGROUND_CALL_STATE = "foreground_state";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
-     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
-     * containing the state of the current background call.
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_BACKGROUND_CALL_STATE = "background_state";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
-     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
      * containing the disconnect cause.
      *
      * @see DisconnectCause
@@ -862,88 +765,6 @@
     public static final String EXTRA_PRECISE_DISCONNECT_CAUSE = "precise_disconnect_cause";
 
     /**
-     * Broadcast intent action indicating a data connection has changed,
-     * providing precise information about the connection.
-     *
-     * <p>
-     * The {@link #EXTRA_DATA_STATE} extra indicates the connection state.
-     * 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_IFACE_PROPERTIES} extra indicates the connection interface.
-     * The {@link #EXTRA_DATA_FAILURE_CAUSE} extra indicates the connection fail cause.
-     *
-     * <p class="note">
-     * Requires the READ_PRECISE_PHONE_STATE permission.
-     *
-     * @see #EXTRA_DATA_STATE
-     * @see #EXTRA_DATA_NETWORK_TYPE
-     * @see #EXTRA_DATA_APN_TYPE
-     * @see #EXTRA_DATA_APN
-     * @see #EXTRA_DATA_IFACE
-     * @see #EXTRA_DATA_FAILURE_CAUSE
-     * @hide
-     *
-     * @deprecated If the app is running in the background, it won't be able to receive this
-     * broadcast. Apps should use ConnectivityManager {@link #registerNetworkCallback(
-     * android.net.NetworkRequest, ConnectivityManager.NetworkCallback)} to listen for network
-     * changes.
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @Deprecated
-    @UnsupportedAppUsage
-    public static final String ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED =
-            "android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an integer containing the state of the current data connection.
-     *
-     * @see TelephonyManager#DATA_UNKNOWN
-     * @see TelephonyManager#DATA_DISCONNECTED
-     * @see TelephonyManager#DATA_CONNECTING
-     * @see TelephonyManager#DATA_CONNECTED
-     * @see TelephonyManager#DATA_SUSPENDED
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_STATE = PhoneConstants.STATE_KEY;
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an integer containing the network type.
-     *
-     * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
-     * @see TelephonyManager#NETWORK_TYPE_GPRS
-     * @see TelephonyManager#NETWORK_TYPE_EDGE
-     * @see TelephonyManager#NETWORK_TYPE_UMTS
-     * @see TelephonyManager#NETWORK_TYPE_CDMA
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_0
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_A
-     * @see TelephonyManager#NETWORK_TYPE_1xRTT
-     * @see TelephonyManager#NETWORK_TYPE_HSDPA
-     * @see TelephonyManager#NETWORK_TYPE_HSUPA
-     * @see TelephonyManager#NETWORK_TYPE_HSPA
-     * @see TelephonyManager#NETWORK_TYPE_IDEN
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_B
-     * @see TelephonyManager#NETWORK_TYPE_LTE
-     * @see TelephonyManager#NETWORK_TYPE_EHRPD
-     * @see TelephonyManager#NETWORK_TYPE_HSPAP
-     * @see TelephonyManager#NETWORK_TYPE_NR
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_NETWORK_TYPE = PhoneConstants.DATA_NETWORK_TYPE_KEY;
-
-    /**
      * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
      * for an String containing the data APN type.
      *
@@ -980,18 +801,6 @@
     public static final String EXTRA_DATA_LINK_PROPERTIES_KEY = PhoneConstants.DATA_LINK_PROPERTIES_KEY;
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for the data connection fail cause.
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getStringExtra(String name)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_FAILURE_CAUSE = PhoneConstants.DATA_FAILURE_CAUSE_KEY;
-
-    /**
      * Broadcast intent action for letting the default dialer to know to show voicemail
      * notification.
      *
@@ -2632,7 +2441,7 @@
 
     /*
      * When adding a network type to the list below, make sure to add the correct icon to
-     * MobileSignalController.mapIconSets().
+     * MobileSignalController.mapIconSets() as well as NETWORK_TYPES
      * Do not add negative types.
      */
     /** Network type is unknown */
@@ -2680,8 +2489,36 @@
     /** Current network is NR(New Radio) 5G. */
     public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20.
 
-    /** Max network type number. Update as new types are added. Don't add negative types. {@hide} */
-    public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_NR;
+    private static final @NetworkType int[] NETWORK_TYPES = {
+            NETWORK_TYPE_GPRS,
+            NETWORK_TYPE_EDGE,
+            NETWORK_TYPE_UMTS,
+            NETWORK_TYPE_CDMA,
+            NETWORK_TYPE_EVDO_0,
+            NETWORK_TYPE_EVDO_A,
+            NETWORK_TYPE_1xRTT,
+            NETWORK_TYPE_HSDPA,
+            NETWORK_TYPE_HSUPA,
+            NETWORK_TYPE_HSPA,
+            NETWORK_TYPE_IDEN,
+            NETWORK_TYPE_EVDO_B,
+            NETWORK_TYPE_LTE,
+            NETWORK_TYPE_EHRPD,
+            NETWORK_TYPE_HSPAP,
+            NETWORK_TYPE_GSM,
+            NETWORK_TYPE_TD_SCDMA,
+            NETWORK_TYPE_IWLAN,
+            NETWORK_TYPE_LTE_CA,
+            NETWORK_TYPE_NR
+    };
+
+    /**
+     * Return a collection of all network types
+     * @return network types
+     */
+    public static @NonNull @NetworkType int[] getAllNetworkTypes() {
+        return NETWORK_TYPES;
+    }
 
     /**
      * Return the current data network type.
@@ -8549,6 +8386,44 @@
     }
 
     /**
+     * Shut down all the live radios over all the slot index.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void shutdownAllRadios() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.shutdownMobileRadios();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#shutdownMobileRadios", e);
+        }
+    }
+
+    /**
+     * Check if any radio is on over all the slot indexes.
+     *
+     * @return {@code true} if any radio is on over any slot index.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isAnyRadioPoweredOn() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.needMobileRadioShutdown();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#needMobileRadioShutdown", e);
+        }
+        return false;
+    }
+
+    /**
      * Radio explicitly powered off (e.g, airplane mode).
      * @hide
      */
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 60774e7..034fc22 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.content.ContentValues;
 import android.database.Cursor;
-import android.hardware.radio.V1_4.ApnTypes;
+import android.hardware.radio.V1_5.ApnTypes;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -110,6 +110,8 @@
     public static final int TYPE_EMERGENCY = ApnTypes.EMERGENCY;
     /** APN type for MCX (Mission Critical Service) where X can be PTT/Video/Data */
     public static final int TYPE_MCX = ApnTypes.MCX;
+    /** APN type for XCAP. */
+    public static final int TYPE_XCAP = ApnTypes.XCAP;
 
     // Possible values for authentication types.
     /** No authentication type. */
@@ -198,6 +200,7 @@
         APN_TYPE_STRING_MAP.put("ia", TYPE_IA);
         APN_TYPE_STRING_MAP.put("emergency", TYPE_EMERGENCY);
         APN_TYPE_STRING_MAP.put("mcx", TYPE_MCX);
+        APN_TYPE_STRING_MAP.put("xcap", TYPE_XCAP);
         APN_TYPE_INT_MAP = new ArrayMap<Integer, String>();
         APN_TYPE_INT_MAP.put(TYPE_DEFAULT, "default");
         APN_TYPE_INT_MAP.put(TYPE_MMS, "mms");
@@ -210,6 +213,7 @@
         APN_TYPE_INT_MAP.put(TYPE_IA, "ia");
         APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, "emergency");
         APN_TYPE_INT_MAP.put(TYPE_MCX, "mcx");
+        APN_TYPE_INT_MAP.put(TYPE_XCAP, "xcap");
 
         PROTOCOL_STRING_MAP = new ArrayMap<String, Integer>();
         PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
@@ -1944,8 +1948,9 @@
          * {@link ApnSetting} built from this builder otherwise.
          */
         public ApnSetting build() {
-            if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI |
-                    TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX)) == 0
+            if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI
+                    | TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX
+                    | TYPE_XCAP)) == 0
                 || TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) {
                 return null;
             }
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index 30c209b..96a5a81 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.Annotation.ApnType;
@@ -31,6 +30,7 @@
 import android.text.TextUtils;
 
 import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -261,7 +261,7 @@
     @Override
     public String toString() {
         return "DataProfile=" + mProfileId + "/" + mProtocolType + "/" + mAuthType
-                + "/" + (Build.IS_USER ? "***/***/***" :
+                + "/" + (TelephonyUtils.IS_USER ? "***/***/***" :
                          (mApn + "/" + mUserName + "/" + mPassword)) + "/" + mType + "/"
                 + mMaxConnectionsTime + "/" + mMaxConnections + "/"
                 + mWaitTime + "/" + mEnabled + "/" + mSupportedApnTypesBitmask + "/"
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index 47c4681..5adc99e 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -1173,18 +1173,8 @@
         public void callSessionMergeComplete(IImsCallSession newSession) {
             if (mListener != null) {
                 if (newSession != null) {
-                    // Check if the active session is the same session that was
-                    // active before the merge request was sent.
-                    ImsCallSession validActiveSession = ImsCallSession.this;
-                    try {
-                        if (!Objects.equals(miSession.getCallId(), newSession.getCallId())) {
-                            // New session created after conference
-                            validActiveSession = new ImsCallSession(newSession);
-                        }
-                    } catch (RemoteException rex) {
-                        Log.e(TAG, "callSessionMergeComplete: exception for getCallId!");
-                    }
-                    mListener.callSessionMergeComplete(validActiveSession);
+                    // New session created after conference
+                    mListener.callSessionMergeComplete(new ImsCallSession(newSession));
                } else {
                    // Session already exists. Hence no need to pass
                    mListener.callSessionMergeComplete(null);
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 4fc6a19..cfc803c 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -17,7 +17,6 @@
 package com.android.ims;
 
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.telephony.Rlog;
@@ -26,6 +25,8 @@
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsConfigCallback;
 
+import com.android.internal.telephony.util.HandlerExecutor;
+
 import java.util.concurrent.Executor;
 
 /**
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index 6ff27b1..25f03c2 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -15,6 +15,7 @@
  */
 package com.android.internal.telephony;
 
+import android.content.Intent;
 import android.telephony.TelephonyManager;
 
 import dalvik.annotation.compat.UnsupportedAppUsage;
@@ -25,37 +26,38 @@
 public class IccCardConstants {
 
     /* The extra data for broadcasting intent INTENT_ICC_STATE_CHANGE */
-    public static final String INTENT_KEY_ICC_STATE = "ss";
+    public static final String INTENT_KEY_ICC_STATE = Intent.EXTRA_SIM_STATE;
     /* UNKNOWN means the ICC state is unknown */
-    public static final String INTENT_VALUE_ICC_UNKNOWN = "UNKNOWN";
+    public static final String INTENT_VALUE_ICC_UNKNOWN = Intent.SIM_STATE_UNKNOWN;
     /* NOT_READY means the ICC interface is not ready (eg, radio is off or powering on) */
-    public static final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY";
+    public static final String INTENT_VALUE_ICC_NOT_READY = Intent.SIM_STATE_NOT_READY;
     /* ABSENT means ICC is missing */
-    public static final String INTENT_VALUE_ICC_ABSENT = "ABSENT";
+    public static final String INTENT_VALUE_ICC_ABSENT = Intent.SIM_STATE_ABSENT;
     /* PRESENT means ICC is present */
-    public static final String INTENT_VALUE_ICC_PRESENT = "PRESENT";
+    public static final String INTENT_VALUE_ICC_PRESENT = Intent.SIM_STATE_PRESENT;
     /* CARD_IO_ERROR means for three consecutive times there was SIM IO error */
-    static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = "CARD_IO_ERROR";
+    static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = Intent.SIM_STATE_CARD_IO_ERROR;
     /* CARD_RESTRICTED means card is present but not usable due to carrier restrictions */
-    static public final String INTENT_VALUE_ICC_CARD_RESTRICTED = "CARD_RESTRICTED";
+    static public final String INTENT_VALUE_ICC_CARD_RESTRICTED = Intent.SIM_STATE_CARD_RESTRICTED;
     /* LOCKED means ICC is locked by pin or by network */
-    public static final String INTENT_VALUE_ICC_LOCKED = "LOCKED";
+    public static final String INTENT_VALUE_ICC_LOCKED = Intent.SIM_STATE_LOCKED;
     /* READY means ICC is ready to access */
-    public static final String INTENT_VALUE_ICC_READY = "READY";
+    public static final String INTENT_VALUE_ICC_READY = Intent.SIM_STATE_READY;
     /* IMSI means ICC IMSI is ready in property */
-    public static final String INTENT_VALUE_ICC_IMSI = "IMSI";
+    public static final String INTENT_VALUE_ICC_IMSI = Intent.SIM_STATE_IMSI;
     /* LOADED means all ICC records, including IMSI, are loaded */
-    public static final String INTENT_VALUE_ICC_LOADED = "LOADED";
+    public static final String INTENT_VALUE_ICC_LOADED = Intent.SIM_STATE_LOADED;
     /* The extra data for broadcasting intent INTENT_ICC_STATE_CHANGE */
-    public static final String INTENT_KEY_LOCKED_REASON = "reason";
+    public static final String INTENT_KEY_LOCKED_REASON = Intent.EXTRA_SIM_LOCKED_REASON;
     /* PIN means ICC is locked on PIN1 */
-    public static final String INTENT_VALUE_LOCKED_ON_PIN = "PIN";
+    public static final String INTENT_VALUE_LOCKED_ON_PIN = Intent.SIM_LOCKED_ON_PIN;
     /* PUK means ICC is locked on PUK1 */
-    public static final String INTENT_VALUE_LOCKED_ON_PUK = "PUK";
+    public static final String INTENT_VALUE_LOCKED_ON_PUK = Intent.SIM_LOCKED_ON_PUK;
     /* NETWORK means ICC is locked on NETWORK PERSONALIZATION */
-    public static final String INTENT_VALUE_LOCKED_NETWORK = "NETWORK";
+    public static final String INTENT_VALUE_LOCKED_NETWORK = Intent.SIM_LOCKED_NETWORK;
     /* PERM_DISABLED means ICC is permanently disabled due to puk fails */
-    public static final String INTENT_VALUE_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
+    public static final String INTENT_VALUE_ABSENT_ON_PERM_DISABLED =
+            Intent.SIM_ABSENT_ON_PERM_DISABLED;
 
     /**
      * This is combination of IccCardStatus.CardState and IccCardApplicationStatus.AppState
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index c19ae7b..fde2c5a 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -154,6 +154,8 @@
     public static final String APN_TYPE_EMERGENCY = "emergency";
     /** APN type for Mission Critical Services */
     public static final String APN_TYPE_MCX = "mcx";
+    /** APN type for XCAP */
+    public static final String APN_TYPE_XCAP = "xcap";
     /** Array of all APN types */
     public static final String[] APN_TYPES = {APN_TYPE_DEFAULT,
             APN_TYPE_MMS,
@@ -165,7 +167,8 @@
             APN_TYPE_CBS,
             APN_TYPE_IA,
             APN_TYPE_EMERGENCY,
-            APN_TYPE_MCX
+            APN_TYPE_MCX,
+            APN_TYPE_XCAP,
     };
 
     public static final int RIL_CARD_MAX_APPS    = 8;
@@ -182,10 +185,6 @@
 
     public static final String SLOT_KEY  = "slot";
 
-    /** Fired when a subscriptions phone state changes. */
-    public static final String ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED =
-        "android.intent.action.SUBSCRIPTION_PHONE_STATE";
-
     // FIXME: This is used to pass a subId via intents, we need to look at its usage, which is
     // FIXME: extensive, and see if this should be an array of all active subId's or ...?
     /**
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 22168c5..9cbcd7f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -489,6 +489,8 @@
     int RIL_REQUEST_EMERGENCY_DIAL = 205;
     int RIL_REQUEST_GET_PHONE_CAPABILITY = 206;
     int RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG = 207;
+    int RIL_REQUEST_ENABLE_UICC_APPLICATIONS = 208;
+    int RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT = 209;
 
     /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -552,4 +554,5 @@
     int RIL_UNSOL_ICC_SLOT_STATUS = 1100;
     int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1101;
     int RIL_UNSOL_EMERGENCY_NUMBER_LIST = 1102;
+    int RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED = 1103;
 }
diff --git a/telephony/java/com/android/internal/telephony/util/HandlerExecutor.java b/telephony/java/com/android/internal/telephony/util/HandlerExecutor.java
new file mode 100644
index 0000000..8a25457
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/util/HandlerExecutor.java
@@ -0,0 +1,47 @@
+/*
+ * 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.internal.telephony.util;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * An adapter {@link Executor} that posts all executed tasks onto the given
+ * {@link Handler}.
+ *
+ * @hide
+ */
+public class HandlerExecutor implements Executor {
+    private final Handler mHandler;
+
+    public HandlerExecutor(@NonNull Handler handler) {
+        if (handler == null) {
+            throw new NullPointerException();
+        }
+        mHandler = handler;
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        if (!mHandler.post(command)) {
+            throw new RejectedExecutionException(mHandler + " is shutting down");
+        }
+    }
+}
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index aa4174a..616b6b0 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -30,6 +30,7 @@
     libs: [
         "framework-all",
         "app-compat-annotations",
+        "unsupportedappusage",
     ],
 
     api_packages: [
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index adcbb428..c8d1ce1 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -13,20 +13,20 @@
 // limitations under the License.
 
 java_test_host {
-    name: "ApkVerityTests",
+    name: "ApkVerityTest",
     srcs: ["src/**/*.java"],
     libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
     test_suites: ["general-tests"],
     target_required: [
         "block_device_writer_module",
-        "ApkVerityTestApp",
-        "ApkVerityTestAppSplit",
     ],
     data: [
         ":ApkVerityTestCertDer",
+        ":ApkVerityTestApp",
         ":ApkVerityTestAppFsvSig",
         ":ApkVerityTestAppDm",
         ":ApkVerityTestAppDmFsvSig",
+        ":ApkVerityTestAppSplit",
         ":ApkVerityTestAppSplitFsvSig",
         ":ApkVerityTestAppSplitDm",
         ":ApkVerityTestAppSplitDmFsvSig",
diff --git a/tests/ApkVerityTest/AndroidTest.xml b/tests/ApkVerityTest/AndroidTest.xml
index 73779cb..51bcdea 100644
--- a/tests/ApkVerityTest/AndroidTest.xml
+++ b/tests/ApkVerityTest/AndroidTest.xml
@@ -36,6 +36,6 @@
     </target_preparer>
 
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
-        <option name="jar" value="ApkVerityTests.jar" />
+        <option name="jar" value="ApkVerityTest.jar" />
     </test>
 </configuration>
diff --git a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
index d95af34..1f47b03 100644
--- a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
+++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
@@ -17,4 +17,5 @@
     defaults: ["cts_defaults"],
     srcs: ["src/**/*.java"],
     sdk_version: "current",
+    test_suites: ["device-tests"],
 }
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index ef8face..b4cafe4 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -328,7 +328,8 @@
         long differentVersionCode = 2L;
         TestObserver observer = new TestObserver(OBSERVER_NAME_1) {
                 @Override
-                public int onHealthCheckFailed(VersionedPackage versionedPackage) {
+                public int onHealthCheckFailed(VersionedPackage versionedPackage,
+                        int failureReason) {
                     if (versionedPackage.getVersionCode() == VERSION_CODE) {
                         // Only rollback for specific versionCode
                         return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
@@ -1012,7 +1013,7 @@
             mImpact = impact;
         }
 
-        public int onHealthCheckFailed(VersionedPackage versionedPackage) {
+        public int onHealthCheckFailed(VersionedPackage versionedPackage, int failureReason) {
             mHealthCheckFailedPackages.add(versionedPackage.getPackageName());
             return mImpact;
         }
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 3be7a4a..7b289d8 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -22,18 +22,14 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.Manifest;
-import android.annotation.Nullable;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
 import android.os.ParcelFileDescriptor;
 import android.provider.DeviceConfig;
-import android.text.TextUtils;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -44,7 +40,6 @@
 import com.android.cts.install.lib.Uninstall;
 import com.android.cts.rollback.lib.Rollback;
 import com.android.cts.rollback.lib.RollbackUtils;
-import com.android.internal.R;
 
 import libcore.io.IoUtils;
 
@@ -75,7 +70,6 @@
     private static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
             "watchdog_request_timeout_millis";
 
-    private static final String MODULE_META_DATA_PACKAGE = getModuleMetadataPackageName();
     private static final TestApp NETWORK_STACK = new TestApp("NetworkStack",
             getNetworkStackPackageName(), -1, false, findNetworkStackApk());
 
@@ -186,21 +180,15 @@
     }
 
     /**
-     * Stage install ModuleMetadata package to simulate a Mainline module update.
+     * Stage install an apk with rollback that will be later triggered by unattributable crash.
      */
     @Test
     public void testNativeWatchdogTriggersRollback_Phase1() throws Exception {
-        resetModuleMetadataPackage();
-        Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        PackageInfo metadataPackageInfo = context.getPackageManager().getPackageInfo(
-                MODULE_META_DATA_PACKAGE, 0);
-        String metadataApkPath = metadataPackageInfo.applicationInfo.sourceDir;
-        assertThat(metadataApkPath).isNotNull();
-        assertThat(metadataApkPath).isNotEqualTo("");
+        Uninstall.packages(TestApp.A);
+        Install.single(TestApp.A1).commit();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
 
-        runShellCommand("pm install "
-                + "-r --enable-rollback --staged --wait "
-                + metadataApkPath);
+        Install.single(TestApp.A2).setEnableRollback().setStaged().commit();
     }
 
     /**
@@ -208,9 +196,10 @@
      */
     @Test
     public void testNativeWatchdogTriggersRollback_Phase2() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
         RollbackManager rm = RollbackUtils.getRollbackManager();
         assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
-                        MODULE_META_DATA_PACKAGE)).isNotNull();
+                TestApp.A)).isNotNull();
     }
 
     /**
@@ -218,9 +207,10 @@
      */
     @Test
     public void testNativeWatchdogTriggersRollback_Phase3() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         RollbackManager rm = RollbackUtils.getRollbackManager();
         assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
-                        MODULE_META_DATA_PACKAGE)).isNotNull();
+                TestApp.A)).isNotNull();
     }
 
     @Test
@@ -351,26 +341,6 @@
                         getNetworkStackPackageName())).isNull();
     }
 
-    @Nullable
-    private static String getModuleMetadataPackageName() {
-        String packageName = InstrumentationRegistry.getInstrumentation().getContext()
-                .getResources().getString(R.string.config_defaultModuleMetadataProvider);
-        if (TextUtils.isEmpty(packageName)) {
-            return null;
-        }
-        return packageName;
-    }
-
-    private void resetModuleMetadataPackage() {
-        RollbackManager rm = RollbackUtils.getRollbackManager();
-
-        assertThat(MODULE_META_DATA_PACKAGE).isNotNull();
-        rm.expireRollbackForPackage(MODULE_META_DATA_PACKAGE);
-
-        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
-                MODULE_META_DATA_PACKAGE)).isNull();
-    }
-
     private static void runShellCommand(String cmd) {
         ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .executeShellCommand(cmd);
diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp
index e13f906..f71be7b 100644
--- a/tests/utils/testutils/Android.bp
+++ b/tests/utils/testutils/Android.bp
@@ -17,24 +17,11 @@
 java_library {
     name: "frameworks-base-testutils",
 
-    static_libs: [
-        "frameworks-base-testutils-minus-mockito",
-        "mockito-target-minus-junit4",
-    ]
-}
-
-java_library {
-    name: "frameworks-base-testutils-minus-mockito",
-
-    srcs: [
-        "java/**/*.java",
-        "java/**/*.kt",
-    ],
+    srcs: ["java/**/*.java"],
 
     static_libs: [
         "junit",
         "hamcrest-library",
-        "truth-prebuilt",
     ],
 
     libs: [
diff --git a/tests/utils/testutils/java/test/util/MockitoUtils.kt b/tests/utils/testutils/java/test/util/MockitoUtils.kt
deleted file mode 100644
index 5151abe..0000000
--- a/tests/utils/testutils/java/test/util/MockitoUtils.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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 test.util
-
-import org.mockito.Answers
-import org.mockito.Mockito
-import org.mockito.invocation.InvocationOnMock
-import org.mockito.stubbing.Answer
-import org.mockito.stubbing.Stubber
-
-object MockitoUtils {
-    val ANSWER_THROWS = Answer<Any?> {
-        when (val name = it.method.name) {
-            "toString" -> return@Answer Answers.CALLS_REAL_METHODS.answer(it)
-            else -> {
-                val arguments = it.arguments
-                        ?.takeUnless { it.isEmpty() }
-                        ?.joinToString()
-                        ?.let {
-                            "with $it"
-                        }
-                        .orEmpty()
-
-                throw UnsupportedOperationException("${it.mock::class.java.simpleName}#$name " +
-                        "$arguments should not be called")
-            }
-        }
-    }
-}
-
-inline fun <reified T> mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.java).apply(block)
-
-fun <Type> Stubber.whenever(mock: Type) = Mockito.`when`(mock)
-fun <Type : Any?> whenever(mock: Type) = Mockito.`when`(mock)
-
-@Suppress("UNCHECKED_CAST")
-fun <Type : Any?> whenever(mock: Type, block: InvocationOnMock.() -> Any?) =
-        Mockito.`when`(mock).thenAnswer { block(it) }
-
-fun whenever(mock: Unit) = Mockito.`when`(mock).thenAnswer { }
-
-inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit): T {
-    val swappingAnswer = object : Answer<Any?> {
-        var delegate: Answer<*> = Answers.RETURNS_DEFAULTS
-
-        override fun answer(invocation: InvocationOnMock?): Any? {
-            return delegate.answer(invocation)
-        }
-    }
-
-    return Mockito.mock(T::class.java, swappingAnswer).apply(block)
-            .also {
-                // To allow when() usage inside block, only swap to throwing afterwards
-                swappingAnswer.delegate = MockitoUtils.ANSWER_THROWS
-            }
-}
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index b06607e..0db1807 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -160,13 +160,19 @@
   void Visit(xml::Element* node) override {
     if (node->namespace_uri.empty() && node->name == "item") {
       for (const auto& attr : node->attributes) {
-        if (attr.namespace_uri == xml::kSchemaAndroid) {
-          if ((attr.name == "actionViewClass" || attr.name == "actionProviderClass") &&
-              util::IsJavaClassName(attr.value)) {
-            AddClass(node->line_number, attr.value, "android.content.Context");
-          } else if (attr.name == "onClick") {
-            AddMethod(node->line_number, attr.value, "android.view.MenuItem");
-          }
+        // AppCompat-v7 defines its own versions of Android attributes if
+        // they're defined after SDK 7 (the below are from 11 and 14,
+        // respectively), so don't bother checking the XML namespace.
+        //
+        // Given the names of the containing XML files and the attribute
+        // names, it's unlikely that keeping these classes would be wrong.
+        if ((attr.name == "actionViewClass" || attr.name == "actionProviderClass") &&
+            util::IsJavaClassName(attr.value)) {
+          AddClass(node->line_number, attr.value, "android.content.Context");
+        }
+
+        if (attr.namespace_uri == xml::kSchemaAndroid && attr.name == "onClick") {
+          AddMethod(node->line_number, attr.value, "android.view.MenuItem");
         }
       }
     }
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index 8720597..b6e7602 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -326,6 +326,25 @@
   EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
 }
 
+TEST(ProguardRulesTest, MenuRulesAreEmittedForActionClasses) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  std::unique_ptr<xml::XmlResource> menu = test::BuildXmlDom(R"(
+      <menu xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto">
+        <item android:id="@+id/my_item"
+            app:actionViewClass="com.foo.Bar"
+            app:actionProviderClass="com.foo.Baz" />
+      </menu>)");
+  menu->file.name = test::ParseNameOrDie("menu/foo");
+
+  proguard::KeepSet set;
+  ASSERT_TRUE(proguard::CollectProguardRules(context.get(), menu.get(), &set));
+
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz"));
+}
+
 TEST(ProguardRulesTest, TransitionPathMotionRulesAreEmitted) {
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
   std::unique_ptr<xml::XmlResource> transition = test::BuildXmlDom(R"(
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
index d45c4e7..712b48e 100644
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ b/tools/stats_log_api_gen/java_writer.cpp
@@ -189,6 +189,7 @@
         }
 
         fprintf(out, "\n");
+        fprintf(out, "%s        builder.usePooledBuffer();\n", indent.c_str());
         fprintf(out, "%s        StatsLog.write(builder.build());\n", indent.c_str());
 
         // Add support for writing using Q schema if this is not the default module.
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 449f95e..66b0590 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -22,7 +22,6 @@
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.security.Credentials;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -93,10 +92,26 @@
      */
     public static final String ENGINE_DISABLE = "0";
 
+    /**
+     * Key prefix for CA certificates.
+     * Note: copied from {@link android.security.Credentials#CA_CERTIFICATE} since it is @hide.
+     */
+    private static final String CA_CERTIFICATE = "CACERT_";
+    /**
+     * Key prefix for user certificates.
+     * Note: copied from {@link android.security.Credentials#USER_CERTIFICATE} since it is @hide.
+     */
+    private static final String USER_CERTIFICATE = "USRCERT_";
+    /**
+     * Key prefix for user private and secret keys.
+     * Note: copied from {@link android.security.Credentials#USER_PRIVATE_KEY} since it is @hide.
+     */
+    private static final String USER_PRIVATE_KEY = "USRPKEY_";
+
     /** @hide */
-    public static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE;
+    public static final String CA_CERT_PREFIX = KEYSTORE_URI + CA_CERTIFICATE;
     /** @hide */
-    public static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + Credentials.USER_CERTIFICATE;
+    public static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + USER_CERTIFICATE;
     /** @hide */
     public static final String CLIENT_CERT_KEY     = "client_cert";
     /** @hide */
@@ -659,7 +674,7 @@
                 if (i > 0) {
                     sb.append(CA_CERT_ALIAS_DELIMITER);
                 }
-                sb.append(encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + aliases[i]));
+                sb.append(encodeCaCertificateAlias(CA_CERTIFICATE + aliases[i]));
             }
             setFieldValue(CA_CERT_KEY, sb.toString(), KEYSTORES_URI);
         }
@@ -693,8 +708,8 @@
             String[] aliases = TextUtils.split(values, CA_CERT_ALIAS_DELIMITER);
             for (int i = 0; i < aliases.length; i++) {
                 aliases[i] = decodeCaCertificateAlias(aliases[i]);
-                if (aliases[i].startsWith(Credentials.CA_CERTIFICATE)) {
-                    aliases[i] = aliases[i].substring(Credentials.CA_CERTIFICATE.length());
+                if (aliases[i].startsWith(CA_CERTIFICATE)) {
+                    aliases[i] = aliases[i].substring(CA_CERTIFICATE.length());
                 }
             }
             return aliases.length != 0 ? aliases : null;
@@ -832,7 +847,7 @@
     @SystemApi
     public void setClientCertificateAlias(@Nullable String alias) {
         setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX);
-        setFieldValue(PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY);
+        setFieldValue(PRIVATE_KEY_ID_KEY, alias, USER_PRIVATE_KEY);
         // Also, set engine parameters
         if (TextUtils.isEmpty(alias)) {
             setFieldValue(ENGINE_KEY, ENGINE_DISABLE);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 86c398b..50d62a0 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1069,7 +1069,7 @@
      * @deprecated This API is non-functional and will have no impact.
      */
     @Deprecated
-    public static final int WIFI_MODE_FULL = WifiProtoEnums.WIFI_MODE_FULL; // 1
+    public static final int WIFI_MODE_FULL = 1;
 
     /**
      * In this Wi-Fi lock mode, Wi-Fi will be kept active,
@@ -1083,7 +1083,7 @@
      * @deprecated This API is non-functional and will have no impact.
      */
     @Deprecated
-    public static final int WIFI_MODE_SCAN_ONLY = WifiProtoEnums.WIFI_MODE_SCAN_ONLY; // 2
+    public static final int WIFI_MODE_SCAN_ONLY = 2;
 
     /**
      * In this Wi-Fi lock mode, Wi-Fi will not go to power save.
@@ -1102,7 +1102,7 @@
      * When there is no support from the hardware, the {@link #WIFI_MODE_FULL_HIGH_PERF}
      * lock will have no impact.
      */
-    public static final int WIFI_MODE_FULL_HIGH_PERF = WifiProtoEnums.WIFI_MODE_FULL_HIGH_PERF; // 3
+    public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
 
     /**
      * In this Wi-Fi lock mode, Wi-Fi will operate with a priority to achieve low latency.
@@ -1132,8 +1132,8 @@
      * lock will be effective when app is running in foreground and screen is on,
      * while the {@link #WIFI_MODE_FULL_HIGH_PERF} lock will take effect otherwise.
      */
-    public static final int WIFI_MODE_FULL_LOW_LATENCY =
-            WifiProtoEnums.WIFI_MODE_FULL_LOW_LATENCY; // 4
+    public static final int WIFI_MODE_FULL_LOW_LATENCY = 4;
+
 
     /** Anything worse than or equal to this will show 0 bars. */
     @UnsupportedAppUsage
diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
index 24aa23a..04d2e1a 100644
--- a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.net.MacAddress;
 import android.net.MatchAllNetworkSpecifier;
-import android.net.NetworkAgent;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.os.Parcel;
@@ -120,7 +119,7 @@
 
     /**
      * Match {@link WifiNetworkSpecifier} in app's {@link NetworkRequest} with the
-     * {@link WifiNetworkAgentSpecifier} in wifi platform's {@link NetworkAgent}.
+     * {@link WifiNetworkAgentSpecifier} in wifi platform's {@link android.net.NetworkAgent}.
      */
     public boolean satisfiesNetworkSpecifier(@NonNull WifiNetworkSpecifier ns) {
         // None of these should be null by construction.